diff options
57 files changed, 1378 insertions, 99 deletions
diff --git a/api/current.txt b/api/current.txt index cdc2404..929da0e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -28593,9 +28593,9 @@ package android.security { method public int getPurposes(); method public java.lang.String[] getSignaturePaddings(); method public int getUserAuthenticationValidityDurationSeconds(); - method public boolean isTeeBacked(); + method public boolean isInsideSecureHardware(); method public boolean isUserAuthenticationRequired(); - method public boolean isUserAuthenticationRequirementTeeEnforced(); + method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware(); } public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter { diff --git a/api/system-current.txt b/api/system-current.txt index 7012773..f9fa4e3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -30607,9 +30607,9 @@ package android.security { method public int getPurposes(); method public java.lang.String[] getSignaturePaddings(); method public int getUserAuthenticationValidityDurationSeconds(); - method public boolean isTeeBacked(); + method public boolean isInsideSecureHardware(); method public boolean isUserAuthenticationRequired(); - method public boolean isUserAuthenticationRequirementTeeEnforced(); + method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware(); } public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter { diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 94e3b66..4d34349 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -33,6 +33,7 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.Window; import android.widget.SpinnerAdapter; import java.lang.annotation.Retention; @@ -1373,5 +1374,13 @@ public abstract class ActionBar { * version of the SDK an app can end up statically linking to the new MarginLayoutParams * overload, causing a crash when running on older platform versions with no other changes. */ + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("gravity", gravity); + } } } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 0a77868..ec6f18d 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -28,14 +28,11 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; +import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; -import android.app.ActivityThread; -import android.os.SystemProperties; -import android.provider.Settings; -import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -1255,7 +1252,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip filtering */ public boolean isOffloadedFilteringSupported() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { return mService.isOffloadedFilteringSupported(); } catch (RemoteException e) { @@ -1270,7 +1267,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip scan batching */ public boolean isOffloadedScanBatchingSupported() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { return mService.isOffloadedScanBatchingSupported(); } catch (RemoteException e) { @@ -1286,7 +1283,7 @@ public final class BluetoothAdapter { * @hide */ public boolean isHardwareTrackingFiltersAvailable() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 1d108a2..e65b4ca 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -20,7 +20,6 @@ import android.annotation.AttrRes; import android.annotation.ColorInt; import android.annotation.StyleRes; import android.annotation.StyleableRes; - import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.XmlUtils; @@ -62,6 +61,7 @@ import android.util.Pools.SynchronizedPool; import android.util.Slog; import android.util.TypedValue; import android.view.ViewDebug; +import android.view.ViewHierarchyEncoder; import java.io.IOException; import java.io.InputStream; @@ -1806,12 +1806,27 @@ public class Resources { for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) { final int resId = mKey.mResId[i]; final boolean forced = mKey.mForce[i]; - themes[i] = getResourceName(resId); + try { + themes[i] = getResourceName(resId); + } catch (NotFoundException e) { + themes[i] = Integer.toHexString(i); + } themes[i + 1] = forced ? "forced" : "not forced"; } return themes; } + /** @hide */ + public void encode(@NonNull ViewHierarchyEncoder encoder) { + encoder.beginObject(this); + // TODO: revert after getTheme() is fixed + String[] properties = new String[0]; // getTheme(); + for (int i = 0; i < properties.length; i += 2) { + encoder.addProperty(properties[i], properties[i+1]); + } + encoder.endObject(); + } + /** * Rebases the theme against the parent Resource object's current * configuration by re-applying the styles passed to diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java index 3a36b0a..be48633 100644 --- a/core/java/android/ddm/DdmHandleViewDebug.java +++ b/core/java/android/ddm/DdmHandleViewDebug.java @@ -229,15 +229,25 @@ public class DdmHandleViewDebug extends ChunkHandler { private Chunk dumpHierarchy(View rootView, ByteBuffer in) { boolean skipChildren = in.getInt() > 0; boolean includeProperties = in.getInt() > 0; + boolean v2 = in.hasRemaining() && in.getInt() > 0; - ByteArrayOutputStream b = new ByteArrayOutputStream(1024); + long start = System.currentTimeMillis(); + + ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024); try { - ViewDebug.dump(rootView, skipChildren, includeProperties, b); - } catch (IOException e) { + if (v2) { + ViewDebug.dumpv2(rootView, b); + } else { + ViewDebug.dump(rootView, skipChildren, includeProperties, b); + } + } catch (IOException | InterruptedException e) { return createFailChunk(1, "Unexpected error while obtaining view hierarchy: " + e.getMessage()); } + long end = System.currentTimeMillis(); + Log.d(TAG, "Time to obtain view hierarchy (ms): " + (end - start)); + byte[] data = b.toByteArray(); return new Chunk(CHUNK_VURT, data, 0, data.length); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a7af838..7c3c11b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6156,6 +6156,17 @@ public final class Settings { */ public static final String MOBILE_DATA = "mobile_data"; + /** + * Whether the mobile data connection should remain active even when higher + * priority networks like WiFi are active, to help make network switching faster. + * + * See ConnectivityService for more info. + * + * (0 = disabled, 1 = enabled) + * @hide + */ + public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; + /** {@hide} */ public static final String NETSTATS_ENABLED = "netstats_enabled"; /** {@hide} */ diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f62e6a2..a3d0b2a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -22353,4 +22353,138 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final String output = bits + " " + name; found.put(key, output); } + + /** {@hide} */ + public void encode(@NonNull ViewHierarchyEncoder stream) { + stream.beginObject(this); + encodeProperties(stream); + stream.endObject(); + } + + /** {@hide} */ + @CallSuper + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + Object resolveId = ViewDebug.resolveId(getContext(), mID); + if (resolveId instanceof String) { + stream.addProperty("id", (String) resolveId); + } else { + stream.addProperty("id", mID); + } + + stream.addProperty("misc:transformation.alpha", + mTransformationInfo != null ? mTransformationInfo.mAlpha : 0); + stream.addProperty("misc:transitionName", getTransitionName()); + + // layout + stream.addProperty("layout:left", mLeft); + stream.addProperty("layout:right", mRight); + stream.addProperty("layout:top", mTop); + stream.addProperty("layout:bottom", mBottom); + stream.addProperty("layout:width", getWidth()); + stream.addProperty("layout:height", getHeight()); + stream.addProperty("layout:layoutDirection", getLayoutDirection()); + stream.addProperty("layout:layoutRtl", isLayoutRtl()); + stream.addProperty("layout:hasTransientState", hasTransientState()); + stream.addProperty("layout:baseline", getBaseline()); + + // layout params + ViewGroup.LayoutParams layoutParams = getLayoutParams(); + if (layoutParams != null) { + stream.addPropertyKey("layoutParams"); + layoutParams.encode(stream); + } + + // scrolling + stream.addProperty("scrolling:scrollX", mScrollX); + stream.addProperty("scrolling:scrollY", mScrollY); + + // padding + stream.addProperty("padding:paddingLeft", mPaddingLeft); + stream.addProperty("padding:paddingRight", mPaddingRight); + stream.addProperty("padding:paddingTop", mPaddingTop); + stream.addProperty("padding:paddingBottom", mPaddingBottom); + stream.addProperty("padding:userPaddingRight", mUserPaddingRight); + stream.addProperty("padding:userPaddingLeft", mUserPaddingLeft); + stream.addProperty("padding:userPaddingBottom", mUserPaddingBottom); + stream.addProperty("padding:userPaddingStart", mUserPaddingStart); + stream.addProperty("padding:userPaddingEnd", mUserPaddingEnd); + + // measurement + stream.addProperty("measurement:minHeight", mMinHeight); + stream.addProperty("measurement:minWidth", mMinWidth); + stream.addProperty("measurement:measuredWidth", mMeasuredWidth); + stream.addProperty("measurement:measuredHeight", mMeasuredHeight); + + // drawing + stream.addProperty("drawing:elevation", getElevation()); + stream.addProperty("drawing:translationX", getTranslationX()); + stream.addProperty("drawing:translationY", getTranslationY()); + stream.addProperty("drawing:translationZ", getTranslationZ()); + stream.addProperty("drawing:rotation", getRotation()); + stream.addProperty("drawing:rotationX", getRotationX()); + stream.addProperty("drawing:rotationY", getRotationY()); + stream.addProperty("drawing:scaleX", getScaleX()); + stream.addProperty("drawing:scaleY", getScaleY()); + stream.addProperty("drawing:pivotX", getPivotX()); + stream.addProperty("drawing:pivotY", getPivotY()); + stream.addProperty("drawing:opaque", isOpaque()); + stream.addProperty("drawing:alpha", getAlpha()); + stream.addProperty("drawing:transitionAlpha", getTransitionAlpha()); + stream.addProperty("drawing:shadow", hasShadow()); + stream.addProperty("drawing:solidColor", getSolidColor()); + stream.addProperty("drawing:layerType", mLayerType); + stream.addProperty("drawing:willNotDraw", willNotDraw()); + stream.addProperty("drawing:hardwareAccelerated", isHardwareAccelerated()); + stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing()); + stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled()); + stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering()); + + // focus + stream.addProperty("focus:hasFocus", hasFocus()); + stream.addProperty("focus:isFocused", isFocused()); + stream.addProperty("focus:isFocusable", isFocusable()); + stream.addProperty("focus:isFocusableInTouchMode", isFocusableInTouchMode()); + + stream.addProperty("misc:clickable", isClickable()); + stream.addProperty("misc:pressed", isPressed()); + stream.addProperty("misc:selected", isSelected()); + stream.addProperty("misc:touchMode", isInTouchMode()); + stream.addProperty("misc:hovered", isHovered()); + stream.addProperty("misc:activated", isActivated()); + + stream.addProperty("misc:visibility", getVisibility()); + stream.addProperty("misc:fitsSystemWindows", getFitsSystemWindows()); + stream.addProperty("misc:filterTouchesWhenObscured", getFilterTouchesWhenObscured()); + + stream.addProperty("misc:enabled", isEnabled()); + stream.addProperty("misc:soundEffectsEnabled", isSoundEffectsEnabled()); + stream.addProperty("misc:hapticFeedbackEnabled", isHapticFeedbackEnabled()); + + // theme attributes + Resources.Theme theme = getContext().getTheme(); + if (theme != null) { + stream.addPropertyKey("theme"); + theme.encode(stream); + } + + // view attribute information + int n = mAttributes != null ? mAttributes.length : 0; + stream.addProperty("meta:__attrCount__", n/2); + for (int i = 0; i < n; i += 2) { + stream.addProperty("meta:__attr__" + mAttributes[i], mAttributes[i+1]); + } + + stream.addProperty("misc:scrollBarStyle", getScrollBarStyle()); + + // text + stream.addProperty("text:textDirection", getTextDirection()); + stream.addProperty("text:textAlignment", getTextAlignment()); + + // accessibility + CharSequence contentDescription = getContentDescription(); + stream.addProperty("accessibility:contentDescription", + contentDescription == null ? "" : contentDescription.toString()); + stream.addProperty("accessibility:labelFor", getLabelFor()); + stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility()); + } } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 27304f5..8bf53a8 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; @@ -800,6 +801,7 @@ public class ViewDebug { /** * Dumps the view hierarchy starting from the given view. + * @deprecated See {@link #dumpv2(View, ByteArrayOutputStream)} below. * @hide */ public static void dump(View root, boolean skipChildren, boolean includeProperties, @@ -825,6 +827,28 @@ public class ViewDebug { } /** + * Dumps the view hierarchy starting from the given view. + * Rather than using reflection, it uses View's encode method to obtain all the properties. + * @hide + */ + public static void dumpv2(@NonNull final View view, @NonNull ByteArrayOutputStream out) + throws InterruptedException { + final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(out); + final CountDownLatch latch = new CountDownLatch(1); + + view.post(new Runnable() { + @Override + public void run() { + view.encode(encoder); + latch.countDown(); + } + }); + + latch.await(2, TimeUnit.SECONDS); + encoder.endStream(); + } + + /** * Dumps the theme attributes from the given View. * @hide */ diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index babb4e9..d0738b0 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -6861,6 +6861,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } return String.valueOf(size); } + + /** @hide */ + void encode(@NonNull ViewHierarchyEncoder encoder) { + encoder.beginObject(this); + encodeProperties(encoder); + encoder.endObject(); + } + + /** @hide */ + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + encoder.addProperty("width", width); + encoder.addProperty("height", height); + } } /** @@ -7329,6 +7342,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager bottomMargin, paint); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("leftMargin", leftMargin); + encoder.addProperty("topMargin", topMargin); + encoder.addProperty("rightMargin", rightMargin); + encoder.addProperty("bottomMargin", bottomMargin); + encoder.addProperty("startMargin", startMargin); + encoder.addProperty("endMargin", endMargin); + } } /* Describes a touched view and the ids of the pointers that it has captured. @@ -7665,4 +7690,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager canvas.drawLines(sDebugLines, paint); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("focus:descendantFocusability", getDescendantFocusability()); + encoder.addProperty("drawing:clipChildren", getClipChildren()); + encoder.addProperty("drawing:clipToPadding", getClipToPadding()); + encoder.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled()); + encoder.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache()); + + int n = getChildCount(); + encoder.addProperty("meta:__childCount__", (short)n); + for (int i = 0; i < n; i++) { + encoder.addPropertyKey("meta:__child__" + i); + getChildAt(i).encode(encoder); + } + } } diff --git a/core/java/android/view/ViewHierarchyEncoder.java b/core/java/android/view/ViewHierarchyEncoder.java new file mode 100644 index 0000000..8770216 --- /dev/null +++ b/core/java/android/view/ViewHierarchyEncoder.java @@ -0,0 +1,201 @@ +package android.view; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link ViewHierarchyEncoder} is a serializer that is tailored towards writing out + * view hierarchies (the view tree, along with the properties for each view) to a stream. + * + * It is typically used as follows: + * <pre> + * ViewHierarchyEncoder e = new ViewHierarchyEncoder(); + * + * for (View view : views) { + * e.beginObject(view); + * e.addProperty("prop1", value); + * ... + * e.endObject(); + * } + * + * // repeat above snippet for each view, finally end with: + * e.endStream(); + * </pre> + * + * <p>On the stream, a snippet such as the above gets encoded as a series of Map's (one + * corresponding to each view) with the property name as the key and the property value + * as the value. + * + * <p>Since the property names are practically the same across all views, rather than using + * the property name directly as the key, we use a short integer id corresponding to each + * property name as the key. A final map is added at the end which contains the mapping + * from the integer to its property name. + * + * <p>A value is encoded as a single byte type identifier followed by the encoding of the + * value. Only primitive types are supported as values, in addition to the Map type. + * + * @hide + */ +public class ViewHierarchyEncoder { + // Prefixes for simple primitives. These match the JNI definitions. + private static final byte SIG_BOOLEAN = 'Z'; + private static final byte SIG_BYTE = 'B'; + private static final byte SIG_SHORT = 'S'; + private static final byte SIG_INT = 'I'; + private static final byte SIG_LONG = 'J'; + private static final byte SIG_FLOAT = 'F'; + private static final byte SIG_DOUBLE = 'D'; + + // Prefixes for some commonly used objects + private static final byte SIG_STRING = 'R'; + + private static final byte SIG_MAP = 'M'; // a map with an short key + private static final short SIG_END_MAP = 0; + + private final DataOutputStream mStream; + + private final Map<String,Short> mPropertyNames = new HashMap<String, Short>(200); + private short mPropertyId = 1; + private Charset mCharset = Charset.forName("utf-8"); + + public ViewHierarchyEncoder(@NonNull ByteArrayOutputStream stream) { + mStream = new DataOutputStream(stream); + } + + public void beginObject(@NonNull Object o) { + startPropertyMap(); + addProperty("meta:__name__", o.getClass().getName()); + addProperty("meta:__hash__", o.hashCode()); + } + + public void endObject() { + endPropertyMap(); + } + + public void endStream() { + // write out the string table + startPropertyMap(); + addProperty("__name__", "propertyIndex"); + for (Map.Entry<String,Short> entry : mPropertyNames.entrySet()) { + writeShort(entry.getValue()); + writeString(entry.getKey()); + } + endPropertyMap(); + } + + public void addProperty(@NonNull String name, boolean v) { + writeShort(createPropertyIndex(name)); + writeBoolean(v); + } + + public void addProperty(@NonNull String name, short s) { + writeShort(createPropertyIndex(name)); + writeShort(s); + } + + public void addProperty(@NonNull String name, int v) { + writeShort(createPropertyIndex(name)); + writeInt(v); + } + + public void addProperty(@NonNull String name, float v) { + writeShort(createPropertyIndex(name)); + writeFloat(v); + } + + public void addProperty(@NonNull String name, @Nullable String s) { + writeShort(createPropertyIndex(name)); + writeString(s); + } + + /** + * Writes the given name as the property name, and leaves it to the callee + * to fill in value for this property. + */ + public void addPropertyKey(@NonNull String name) { + writeShort(createPropertyIndex(name)); + } + + private short createPropertyIndex(@NonNull String name) { + Short index = mPropertyNames.get(name); + if (index == null) { + index = mPropertyId++; + mPropertyNames.put(name, index); + } + + return index; + } + + private void startPropertyMap() { + try { + mStream.write(SIG_MAP); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void endPropertyMap() { + writeShort(SIG_END_MAP); + } + + private void writeBoolean(boolean v) { + try { + mStream.write(SIG_BOOLEAN); + mStream.write(v ? 1 : 0); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void writeShort(short s) { + try { + mStream.write(SIG_SHORT); + mStream.writeShort(s); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void writeInt(int i) { + try { + mStream.write(SIG_INT); + mStream.writeInt(i); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void writeFloat(float v) { + try { + mStream.write(SIG_FLOAT); + mStream.writeFloat(v); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void writeString(@Nullable String s) { + if (s == null) { + s = ""; + } + + try { + mStream.write(SIG_STRING); + byte[] bytes = s.getBytes(mCharset); + + short len = (short)Math.min(bytes.length, Short.MAX_VALUE); + mStream.writeShort(len); + + mStream.write(bytes, 0, len); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } +} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e983910..2797b6e 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -16,11 +16,11 @@ package android.view; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Presentation; import android.content.Context; import android.content.pm.ActivityInfo; -import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.IBinder; @@ -1119,6 +1119,15 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 0x00000800; /** + * Flag to force the status bar window to be visible all the time. If the bar is hidden when + * this flag is set it will be shown again and the bar will have a transparent background. + * This can only be set by {@link LayoutParams#TYPE_STATUS_BAR}. + * + * {@hide} + */ + public static final int PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT = 0x00001000; + + /** * Control flags that are private to the platform. * @hide */ @@ -2066,5 +2075,18 @@ public interface WindowManager extends ViewManager { } private CharSequence mTitle = ""; + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("x", x); + encoder.addProperty("y", y); + encoder.addProperty("horizontalWeight", horizontalWeight); + encoder.addProperty("verticalWeight", verticalWeight); + encoder.addProperty("type", type); + encoder.addProperty("flags", flags); + } } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 7ab5aaa..a261aaf 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -16,6 +16,7 @@ package android.webkit; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.Widget; import android.content.Context; @@ -43,6 +44,7 @@ import android.view.View; import android.view.ViewAssistStructure; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -2576,4 +2578,18 @@ public class WebView extends AbsoluteLayout super.onFinishTemporaryDetach(); mProvider.getViewDelegate().onFinishTemporaryDetach(); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + checkThread(); + encoder.addProperty("webview:contentHeight", mProvider.getContentHeight()); + encoder.addProperty("webview:contentWidth", mProvider.getContentWidth()); + encoder.addProperty("webview:scale", mProvider.getScale()); + encoder.addProperty("webview:title", mProvider.getTitle()); + encoder.addProperty("webview:url", mProvider.getUrl()); + encoder.addProperty("webview:originalUrl", mProvider.getOriginalUrl()); + } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index c57a53a..9903b7e 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -18,6 +18,7 @@ package android.widget; import android.annotation.ColorInt; import android.annotation.DrawableRes; +import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; @@ -56,6 +57,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; @@ -6330,6 +6332,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public LayoutParams(ViewGroup.LayoutParams source) { super(source); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("list:viewType", viewType); + encoder.addProperty("list:recycledHeaderFooter", recycledHeaderFooter); + encoder.addProperty("list:forceAdd", forceAdd); + } } /** @@ -6912,6 +6924,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("drawing:cacheColorHint", getCacheColorHint()); + encoder.addProperty("list:fastScrollEnabled", isFastScrollEnabled()); + encoder.addProperty("list:scrollingCacheEnabled", isScrollingCacheEnabled()); + encoder.addProperty("list:smoothScrollbarEnabled", isSmoothScrollbarEnabled()); + encoder.addProperty("list:stackFromBottom", isStackFromBottom()); + encoder.addProperty("list:textFilterEnabled", isTextFilterEnabled()); + + View selectedView = getSelectedView(); + if (selectedView != null) { + encoder.addPropertyKey("selectedView"); + selectedView.encode(encoder); + } + } + /** * Abstract positon scroller used to handle smooth scrolling. */ diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java index d6f2276..278a8fb 100644 --- a/core/java/android/widget/ActionMenuView.java +++ b/core/java/android/widget/ActionMenuView.java @@ -15,6 +15,7 @@ */ package android.widget; +import android.annotation.NonNull; import android.annotation.StyleRes; import android.content.Context; import android.content.res.ColorStateList; @@ -28,6 +29,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import com.android.internal.view.menu.ActionMenuItemView; import com.android.internal.view.menu.MenuBuilder; @@ -835,5 +837,17 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo super(width, height); this.isOverflowButton = isOverflowButton; } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("layout:overFlowButton", isOverflowButton); + encoder.addProperty("layout:cellsUsed", cellsUsed); + encoder.addProperty("layout:extraPixels", extraPixels); + encoder.addProperty("layout:expandable", expandable); + encoder.addProperty("layout:preventEdgeOffset", preventEdgeOffset); + } } } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 72cb0b5..54e3996 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.database.DataSetObserver; @@ -29,6 +30,7 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -1245,4 +1247,16 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { } } } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("scrolling:firstPosition", mFirstPosition); + encoder.addProperty("list:nextSelectedPosition", mNextSelectedPosition); + encoder.addProperty("list:nextSelectedRowId", mNextSelectedRowId); + encoder.addProperty("list:selectedPosition", mSelectedPosition); + encoder.addProperty("list:itemCount", mItemCount); + } } diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index 22e079c..6b4b2c7 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -16,6 +16,8 @@ package android.widget; +import android.annotation.NonNull; +import android.view.ViewHierarchyEncoder; import com.android.internal.R; import android.annotation.DrawableRes; @@ -459,4 +461,11 @@ public class CheckedTextView extends TextView implements Checkable { info.setCheckable(true); info.setChecked(mChecked); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + stream.addProperty("text:checked", isChecked()); + } } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index f2afeeb..770077d 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -17,8 +17,10 @@ package android.widget; import android.annotation.DrawableRes; +import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.PorterDuff; +import android.view.ViewHierarchyEncoder; import com.android.internal.R; import android.content.Context; @@ -530,9 +532,16 @@ public abstract class CompoundButton extends Button implements Checkable { @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; - + super.onRestoreInstanceState(ss.getSuperState()); setChecked(ss.checked); requestLayout(); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + stream.addProperty("checked", isChecked()); + } } diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 0602944..7ca450a 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -33,6 +33,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.widget.RemoteViews.RemoteView; import com.android.internal.R; @@ -407,6 +408,18 @@ public class FrameLayout extends ViewGroup { return FrameLayout.class.getName(); } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("measurement:measureAllChildren", mMeasureAllChildren); + encoder.addProperty("padding:foregroundPaddingLeft", mForegroundPaddingLeft); + encoder.addProperty("padding:foregroundPaddingTop", mForegroundPaddingTop); + encoder.addProperty("padding:foregroundPaddingRight", mForegroundPaddingRight); + encoder.addProperty("padding:foregroundPaddingBottom", mForegroundPaddingBottom); + } + /** * Per-child layout information for layouts that support margins. * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes} diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index c959774..dcaafa5 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -17,6 +17,7 @@ package android.widget; import android.annotation.IntDef; +import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; @@ -31,6 +32,7 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -2420,4 +2422,11 @@ public class GridView extends AbsListView { row, 1, column, 1, isHeading, isSelected); info.setCollectionItemInfo(itemInfo); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("numColumns", getNumColumns()); + } } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 0879c5d..cf67905 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -35,6 +36,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -1695,6 +1697,13 @@ public class HorizontalScrollView extends FrameLayout { return ss; } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("layout:fillViewPort", mFillViewport); + } + static class SavedState extends BaseSavedState { public int scrollPosition; public boolean isLayoutRtl; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 6d2f368..05059bc 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -17,6 +17,7 @@ package android.widget; import android.annotation.DrawableRes; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; @@ -43,6 +44,7 @@ import android.util.Log; import android.view.RemotableViewMethod; import android.view.View; import android.view.ViewDebug; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; @@ -1431,4 +1433,11 @@ public class ImageView extends View { public CharSequence getAccessibilityClassName() { return ImageView.class.getName(); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + stream.addProperty("layout:baseline", getBaseline()); + } } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 72f51c9..f153ce5 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -19,6 +19,7 @@ package android.widget; import com.android.internal.R; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.TypedArray; @@ -29,6 +30,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.widget.RemoteViews.RemoteView; import java.lang.annotation.Retention; @@ -1813,6 +1815,20 @@ public class LinearLayout extends ViewGroup { return LinearLayout.class.getName(); } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("layout:baselineAligned", mBaselineAligned); + encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex); + encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop); + encoder.addProperty("measurement:orientation", mOrientation); + encoder.addProperty("measurement:gravity", mGravity); + encoder.addProperty("measurement:totalLength", mTotalLength); + encoder.addProperty("layout:totalLength", mTotalLength); + encoder.addProperty("layout:useLargestChild", mUseLargestChild); + } + /** * Per-child layout information associated with ViewLinearLayout. * @@ -1921,5 +1937,14 @@ public class LinearLayout extends ViewGroup { return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) + ", height=" + sizeToString(height) + " weight=" + weight + "}"; } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("layout:weight", weight); + encoder.addProperty("layout:gravity", gravity); + } } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index a79c8e8..7dcaa1f 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -23,6 +23,7 @@ import com.android.internal.util.Predicate; import com.google.android.collect.Lists; import android.annotation.IdRes; +import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; @@ -40,6 +41,7 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewParent; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityNodeInfo; @@ -3938,4 +3940,12 @@ public class ListView extends AbsListView { position, 1, 0, 1, isHeading, isSelected); info.setCollectionItemInfo(itemInfo); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("recycleOnMeasure", recycleOnMeasure()); + } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index b59ae17..639a09c 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.PorterDuff; @@ -49,6 +50,7 @@ import android.view.Gravity; import android.view.RemotableViewMethod; import android.view.View; import android.view.ViewDebug; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.AlphaAnimation; @@ -1893,6 +1895,17 @@ public class ProgressBar extends View { postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT); } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + + stream.addProperty("progress:max", getMax()); + stream.addProperty("progress:progress", getProgress()); + stream.addProperty("progress:secondaryProgress", getSecondaryProgress()); + stream.addProperty("progress:indeterminate", isIndeterminate()); + } + /** * Command for sending an accessibility event. */ diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index d12739f..affc5da 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.util.ArrayMap; import com.android.internal.R; @@ -36,6 +37,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; @@ -1616,6 +1618,13 @@ public class RelativeLayout extends ViewGroup { // This will set the layout direction super.resolveLayoutDirection(layoutDirection); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("layout:alignWithParent", alignWithParent); + } } private static class DependencyGraph { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 98d61d3..2709f25 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -38,6 +39,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -1787,6 +1789,13 @@ public class ScrollView extends FrameLayout { return ss; } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("fillViewport", mFillViewport); + } + static class SavedState extends BaseSavedState { public int scrollPosition; diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index f73ee49..d4288d6 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; @@ -24,7 +25,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; - +import android.view.ViewHierarchyEncoder; /** * <p>A layout that arranges its children horizontally. A TableRow should @@ -509,6 +510,14 @@ public class TableRow extends LinearLayout { height = WRAP_CONTENT; } } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("layout:column", column); + encoder.addProperty("layout:span", span); + } } // special transparent hierarchy change listener diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index e2acaac..5d7b569 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -32,6 +33,7 @@ import android.provider.Settings; import android.text.format.DateFormat; import android.util.AttributeSet; import android.view.RemotableViewMethod; +import android.view.ViewHierarchyEncoder; import com.android.internal.R; @@ -546,4 +548,18 @@ public class TextClock extends TextView { mTime.setTimeInMillis(System.currentTimeMillis()); setText(DateFormat.format(mFormat, mTime)); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + + CharSequence s = getFormat12Hour(); + stream.addProperty("format12Hour", s == null ? null : s.toString()); + + s = getFormat24Hour(); + stream.addProperty("format24Hour", s == null ? null : s.toString()); + stream.addProperty("format", mFormat == null ? null : mFormat.toString()); + stream.addProperty("hasSeconds", mHasSeconds); + } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 449173f..b9a08f5 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -120,6 +120,7 @@ import android.view.ViewDebug; import android.view.ViewGroup.LayoutParams; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -2844,7 +2845,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @ViewDebug.IntToString(from = Typeface.BOLD_ITALIC, to = "BOLD_ITALIC") }) public int getTypefaceStyle() { - return mTextPaint.getTypeface().getStyle(); + Typeface typeface = mTextPaint.getTypeface(); + return typeface != null ? typeface.getStyle() : Typeface.NORMAL; } /** @@ -9556,6 +9558,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + + TruncateAt ellipsize = getEllipsize(); + stream.addProperty("text:ellipsize", ellipsize == null ? null : ellipsize.name()); + stream.addProperty("text:textSize", getTextSize()); + stream.addProperty("text:scaledTextSize", getScaledTextSize()); + stream.addProperty("text:typefaceStyle", getTypefaceStyle()); + stream.addProperty("text:selectionStart", getSelectionStart()); + stream.addProperty("text:selectionEnd", getSelectionEnd()); + stream.addProperty("text:curTextColor", mCurTextColor); + stream.addProperty("text:text", mText == null ? null : mText.toString()); + stream.addProperty("text:gravity", mGravity); + } + /** * User interface state that is stored by TextView for implementing * {@link View#onSaveInstanceState}. diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java index 1c3e300..1cf6a7a 100644 --- a/keystore/java/android/security/KeyStoreKeyProperties.java +++ b/keystore/java/android/security/KeyStoreKeyProperties.java @@ -656,7 +656,7 @@ public abstract class KeyStoreKeyProperties { public static final int IMPORTED = 1 << 1; /** - * Origin of the key is unknown. This can occur only for keys backed by an old TEE + * Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed * implementation which does not record origin information. */ public static final int UNKNOWN = 1 << 2; diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java index 7533bdc..a630a0a 100644 --- a/keystore/java/android/security/KeyStoreKeySpec.java +++ b/keystore/java/android/security/KeyStoreKeySpec.java @@ -26,7 +26,7 @@ import java.util.Date; public class KeyStoreKeySpec implements KeySpec { private final String mKeystoreAlias; private final int mKeySize; - private final boolean mTeeBacked; + private final boolean mInsideSecureHardware; private final @KeyStoreKeyProperties.OriginEnum int mOrigin; private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; @@ -38,13 +38,13 @@ public class KeyStoreKeySpec implements KeySpec { private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes; private final boolean mUserAuthenticationRequired; private final int mUserAuthenticationValidityDurationSeconds; - private final boolean mUserAuthenticationRequirementTeeEnforced; + private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware; /** * @hide */ KeyStoreKeySpec(String keystoreKeyAlias, - boolean teeBacked, + boolean insideSecureHardware, @KeyStoreKeyProperties.OriginEnum int origin, int keySize, Date keyValidityStart, @@ -57,9 +57,9 @@ public class KeyStoreKeySpec implements KeySpec { @KeyStoreKeyProperties.BlockModeEnum String[] blockModes, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, - boolean userAuthenticationRequirementTeeEnforced) { + boolean userAuthenticationRequirementEnforcedBySecureHardware) { mKeystoreAlias = keystoreKeyAlias; - mTeeBacked = teeBacked; + mInsideSecureHardware = insideSecureHardware; mOrigin = origin; mKeySize = keySize; mKeyValidityStart = keyValidityStart; @@ -74,7 +74,8 @@ public class KeyStoreKeySpec implements KeySpec { mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes)); mUserAuthenticationRequired = userAuthenticationRequired; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; - mUserAuthenticationRequirementTeeEnforced = userAuthenticationRequirementTeeEnforced; + mUserAuthenticationRequirementEnforcedBySecureHardware = + userAuthenticationRequirementEnforcedBySecureHardware; } /** @@ -85,11 +86,12 @@ public class KeyStoreKeySpec implements KeySpec { } /** - * Returns {@code true} if the key is TEE-backed. Key material of TEE-backed keys is available - * in plaintext only inside the TEE. + * Returns {@code true} if the key resides inside secure hardware (e.g., Trusted Execution + * Environment (TEE) or Secure Element (SE)). Key material of such keys is available in + * plaintext only inside the secure hardware and is not exposed outside of it. */ - public boolean isTeeBacked() { - return mTeeBacked; + public boolean isInsideSecureHardware() { + return mInsideSecureHardware; } /** @@ -192,11 +194,12 @@ public class KeyStoreKeySpec implements KeySpec { /** * Returns {@code true} if the requirement that this key can only be used if the user has been - * authenticated if enforced by the TEE. + * authenticated if enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or + * Secure Element (SE)). * * @see #isUserAuthenticationRequired() */ - public boolean isUserAuthenticationRequirementTeeEnforced() { - return mUserAuthenticationRequirementTeeEnforced; + public boolean isUserAuthenticationRequirementEnforcedBySecureHardware() { + return mUserAuthenticationRequirementEnforcedBySecureHardware; } } diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java index ff79b7a..548296b 100644 --- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java @@ -74,7 +74,7 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { + " Keystore error: " + errorCode); } - boolean teeBacked; + boolean insideSecureHardware; @KeyStoreKeyProperties.OriginEnum int origin; int keySize; @KeyStoreKeyProperties.PurposeEnum int purposes; @@ -85,11 +85,11 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { int keymasterHwEnforcedUserAuthenticators; try { if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { - teeBacked = true; + insideSecureHardware = true; origin = KeyStoreKeyProperties.Origin.fromKeymaster( keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { - teeBacked = false; + insideSecureHardware = false; origin = KeyStoreKeyProperties.Origin.fromKeymaster( keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); } else { @@ -150,12 +150,12 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); int userAuthenticationValidityDurationSeconds = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1); - boolean userAuthenticationRequirementEnforcedInTee = (userAuthenticationRequired) + boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired) && (keymasterHwEnforcedUserAuthenticators != 0) && (keymasterSwEnforcedUserAuthenticators == 0); return new KeyStoreKeySpec(entryAlias, - teeBacked, + insideSecureHardware, origin, keySize, keyValidityStart, @@ -168,7 +168,7 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { blockModes, userAuthenticationRequired, userAuthenticationValidityDurationSeconds, - userAuthenticationRequirementEnforcedInTee); + userAuthenticationRequirementEnforcedBySecureHardware); } @Override diff --git a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml index a11bed0..2f0a411 100644 --- a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml +++ b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml @@ -10,6 +10,13 @@ android:layout_height="match_parent" android:orientation="vertical" > + <TextView + android:id="@+id/url_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="20sp" + android:singleLine="true" /> + <ProgressBar android:id="@+id/progress_bar" android:layout_width="match_parent" diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java index b86fc4b..1019e6c 100644 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java @@ -35,13 +35,13 @@ import android.util.ArrayMap; import android.util.Log; import android.view.Menu; import android.view.MenuItem; -import android.view.View; import android.webkit.SslErrorHandler; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; +import android.widget.TextView; import java.io.IOException; import java.net.HttpURLConnection; @@ -221,6 +221,7 @@ public class CaptivePortalLoginActivity extends Activity { } private class MyWebViewClient extends WebViewClient { + private static final String INTERNAL_ASSETS = "file:///android_asset/"; private boolean firstPageLoad = true; @Override @@ -240,6 +241,12 @@ public class CaptivePortalLoginActivity extends Activity { view.loadUrl(mURL.toString()); return; } + // For internally generated pages, leave URL bar listing prior URL as this is the URL + // the page refers to. + if (!url.startsWith(INTERNAL_ASSETS)) { + final TextView myUrlBar = (TextView) findViewById(R.id.url_bar); + myUrlBar.setText(url); + } testForCaptivePortal(); } @@ -252,17 +259,15 @@ public class CaptivePortalLoginActivity extends Activity { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { Log.w(TAG, "SSL error; displaying broken lock icon."); - view.loadDataWithBaseURL("file:///android_asset/", SSL_ERROR_HTML, "text/HTML", - "UTF-8", null); + view.loadDataWithBaseURL(INTERNAL_ASSETS, SSL_ERROR_HTML, "text/HTML", "UTF-8", null); } } private class MyWebChromeClient extends WebChromeClient { @Override public void onProgressChanged(WebView view, int newProgress) { - ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar); + final ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar); myProgressBar.setProgress(newProgress); - myProgressBar.setVisibility(newProgress == 100 ? View.GONE : View.VISIBLE); } } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java index b5eda90..1c4b963 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; @@ -25,12 +26,15 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ViewFlipper; import com.android.internal.widget.LockPatternUtils; +import java.lang.Override; + /** * Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so * we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy. @@ -268,5 +272,14 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper implements Keyguard R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxHeight, 0); a.recycle(); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("layout:maxWidth", maxWidth); + encoder.addProperty("layout:maxHeight", maxHeight); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 4542054..80fdd28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -157,7 +157,8 @@ public class CommandQueue extends IStatusBar.Stub { public void setSystemUiVisibility(int vis, int mask) { synchronized (mList) { - mHandler.removeMessages(MSG_SET_SYSTEMUI_VISIBILITY); + // Don't coalesce these, since it might have one time flags set such as + // STATUS_BAR_UNHIDE which might get lost. mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 351977b..471196c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -529,6 +529,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, = new HashMap<>(); private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>(); private RankingMap mLatestRankingMap; + private boolean mNoAnimationOnNextBarModeChange; @Override public void start() { @@ -1709,6 +1710,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, * State is one or more of the DISABLE constants from StatusBarManager. */ public void disable(int state1, int state2, boolean animate) { + animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN; mDisabledUnmodified1 = state1; mDisabledUnmodified2 = state2; state1 = adjustDisableFlags(state1); @@ -1868,6 +1870,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { if (inPinnedMode) { mStatusBarWindowManager.setHeadsUpShowing(true); + mStatusBarWindowManager.setForceStatusBarVisible(true); } else { Runnable endRunnable = new Runnable() { @Override @@ -2132,6 +2135,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Shrink the window to the size of the status bar only mStatusBarWindowManager.setStatusBarExpanded(false); + mStatusBarWindowManager.setForceStatusBarVisible(false); mStatusBarView.setFocusable(true); // Close any "App info" popups that might have snuck on-screen @@ -2272,6 +2276,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, setAreThereNotifications(); } + // ready to unhide + if ((vis & View.STATUS_BAR_UNHIDE) != 0) { + mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; + mNoAnimationOnNextBarModeChange = true; + } + // update status bar mode final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(), View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT); @@ -2303,10 +2313,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - // ready to unhide - if ((vis & View.STATUS_BAR_UNHIDE) != 0) { - mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; - } if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) { mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; } @@ -2351,17 +2357,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void checkBarModes() { if (mDemoMode) return; - checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); + checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions(), + mNoAnimationOnNextBarModeChange); if (mNavigationBarView != null) { checkBarMode(mNavigationBarMode, - mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); + mNavigationBarWindowState, mNavigationBarView.getBarTransitions(), + mNoAnimationOnNextBarModeChange); } + mNoAnimationOnNextBarModeChange = false; } - private void checkBarMode(int mode, int windowState, BarTransitions transitions) { + private void checkBarMode(int mode, int windowState, BarTransitions transitions, + boolean noAnimation) { final boolean powerSave = mBatteryController.isPowerSave(); - final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN - && !powerSave; + final boolean anim = !noAnimation && (mScreenOn == null || mScreenOn) + && windowState != WINDOW_STATE_HIDDEN && !powerSave; if (powerSave && getBarState() == StatusBarState.SHADE) { mode = MODE_WARNING; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index 84a9f64..e7e4384 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -166,6 +166,7 @@ public class StatusBarWindowManager { private void apply(State state) { applyKeyguardFlags(state); + applyForceStatusBarVisibleFlag(state); applyFocusableFlag(state); adjustScreenOrientation(state); applyHeight(state); @@ -178,6 +179,16 @@ public class StatusBarWindowManager { } } + private void applyForceStatusBarVisibleFlag(State state) { + if (state.forceStatusBarVisible) { + mLpChanged.privateFlags |= WindowManager + .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; + } else { + mLpChanged.privateFlags &= ~WindowManager + .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; + } + } + private void applyModalFlag(State state) { if (state.headsUpShowing) { mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; @@ -240,6 +251,11 @@ public class StatusBarWindowManager { apply(mCurrentState); } + public void setForceStatusBarVisible(boolean forceStatusBarVisible) { + mCurrentState.forceStatusBarVisible = forceStatusBarVisible; + apply(mCurrentState); + } + private static class State { boolean keyguardShowing; boolean keyguardOccluded; @@ -250,6 +266,7 @@ public class StatusBarWindowManager { boolean keyguardFadingAway; boolean qsExpanded; boolean headsUpShowing; + boolean forceStatusBarVisible; /** * The {@link BaseStatusBar} state from the status bar. diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 8d1d124..1dc2d7e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -190,7 +190,7 @@ public class ConnectivityService extends IConnectivityManager.Stub /** Set of ifaces that are costly. */ private HashSet<String> mMeteredIfaces = Sets.newHashSet(); - private Context mContext; + final private Context mContext; private int mNetworkPreference; // 0 is full bad, 100 is full good private int mDefaultInetConditionPublished = 0; @@ -344,6 +344,11 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private static final int EVENT_PROMPT_UNVALIDATED = 29; + /** + * used internally to (re)configure mobile data always-on settings. + */ + private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30; + /** Handler used for internal events. */ final private InternalHandler mHandler; /** Handler used for incoming {@link NetworkStateTracker} events. */ @@ -374,7 +379,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private PacManager mPacManager = null; - private SettingsObserver mSettingsObserver; + final private SettingsObserver mSettingsObserver; private UserManager mUserManager; @@ -555,13 +560,12 @@ public class ConnectivityService extends IConnectivityManager.Stub INetworkStatsService statsService, INetworkPolicyManager policyManager) { if (DBG) log("ConnectivityService starting up"); - NetworkCapabilities netCap = new NetworkCapabilities(); - netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId()); - NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(), - NetworkRequestInfo.REQUEST); - mNetworkRequests.put(mDefaultRequest, nri); + mDefaultRequest = createInternetRequestForTransport(-1); + mNetworkRequests.put(mDefaultRequest, new NetworkRequestInfo( + null, mDefaultRequest, new Binder(), NetworkRequestInfo.REQUEST)); + + mDefaultMobileDataRequest = createInternetRequestForTransport( + NetworkCapabilities.TRANSPORT_CELLULAR); HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread"); handlerThread.start(); @@ -696,8 +700,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mInetLog = new ArrayList(); } - mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY); - mSettingsObserver.observe(mContext); + mSettingsObserver = new SettingsObserver(mContext, mHandler); + registerSettingsCallbacks(); mDataConnectionStats = new DataConnectionStats(mContext); mDataConnectionStats.startMonitoring(); @@ -707,6 +711,44 @@ public class ConnectivityService extends IConnectivityManager.Stub mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); } + private NetworkRequest createInternetRequestForTransport(int transportType) { + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + if (transportType > -1) { + netCap.addTransportType(transportType); + } + return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId()); + } + + private void handleMobileDataAlwaysOn() { + final boolean enable = (Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 0) == 1); + final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null); + if (enable == isEnabled) { + return; // Nothing to do. + } + + if (enable) { + handleRegisterNetworkRequest(new NetworkRequestInfo( + null, mDefaultMobileDataRequest, new Binder(), NetworkRequestInfo.REQUEST)); + } else { + handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID); + } + } + + private void registerSettingsCallbacks() { + // Watch for global HTTP proxy changes. + mSettingsObserver.observe( + Settings.Global.getUriFor(Settings.Global.HTTP_PROXY), + EVENT_APPLY_GLOBAL_HTTP_PROXY); + + // Watch for whether or not to keep mobile data always on. + mSettingsObserver.observe( + Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON), + EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); + } + private synchronized int nextNetworkRequestId() { return mNextNetworkRequestId++; } @@ -1491,6 +1533,9 @@ public class ConnectivityService extends IConnectivityManager.Stub mContext.registerReceiver(mUserPresentReceiver, filter); } + // Configure whether mobile data is always on. + mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON)); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY)); mPermissionMonitor.startMonitoring(); @@ -2107,12 +2152,10 @@ public class ConnectivityService extends IConnectivityManager.Stub + nri.request + " because their intents matched."); handleReleaseNetworkRequest(existingRequest.request, getCallingUid()); } - handleRegisterNetworkRequest(msg); + handleRegisterNetworkRequest(nri); } - private void handleRegisterNetworkRequest(Message msg) { - final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj); - + private void handleRegisterNetworkRequest(NetworkRequestInfo nri) { mNetworkRequests.put(nri.request, nri); // TODO: This logic may be better replaced with a call to rematchNetworkAndRequests @@ -2423,7 +2466,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } case EVENT_REGISTER_NETWORK_REQUEST: case EVENT_REGISTER_NETWORK_LISTENER: { - handleRegisterNetworkRequest(msg); + handleRegisterNetworkRequest((NetworkRequestInfo) msg.obj); break; } case EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT: { @@ -2446,6 +2489,10 @@ public class ConnectivityService extends IConnectivityManager.Stub handlePromptUnvalidated((Network) msg.obj); break; } + case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: { + handleMobileDataAlwaysOn(); + break; + } case EVENT_SYSTEM_READY: { for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { nai.networkMonitor.systemReady = true; @@ -2837,23 +2884,36 @@ public class ConnectivityService extends IConnectivityManager.Stub } private static class SettingsObserver extends ContentObserver { - private int mWhat; - private Handler mHandler; - SettingsObserver(Handler handler, int what) { - super(handler); + final private HashMap<Uri, Integer> mUriEventMap; + final private Context mContext; + final private Handler mHandler; + + SettingsObserver(Context context, Handler handler) { + super(null); + mUriEventMap = new HashMap<Uri, Integer>(); + mContext = context; mHandler = handler; - mWhat = what; } - void observe(Context context) { - ContentResolver resolver = context.getContentResolver(); - resolver.registerContentObserver(Settings.Global.getUriFor( - Settings.Global.HTTP_PROXY), false, this); + void observe(Uri uri, int what) { + mUriEventMap.put(uri, what); + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(uri, false, this); } @Override public void onChange(boolean selfChange) { - mHandler.obtainMessage(mWhat).sendToTarget(); + Slog.wtf(TAG, "Should never be reached."); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + final Integer what = mUriEventMap.get(uri); + if (what != null) { + mHandler.obtainMessage(what.intValue()).sendToTarget(); + } else { + loge("No matching event to send for URI=" + uri); + } } } @@ -3643,6 +3703,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated. private final NetworkRequest mDefaultRequest; + // Request used to optionally keep mobile data active even when higher + // priority networks like Wi-Fi are active. + private final NetworkRequest mDefaultMobileDataRequest; + private NetworkAgentInfo getDefaultNetwork() { return mNetworkForRequestId.get(mDefaultRequest.requestId); } diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 6f59b54..7f961ae 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -82,7 +82,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { appToken.linkToDeath(device, 0); } catch (RemoteException ex) { mVirtualDisplayDevices.remove(appToken); - device.destroyLocked(); + device.destroyLocked(false); return null; } @@ -110,7 +110,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) { VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); if (device != null) { - device.destroyLocked(); + device.destroyLocked(true); appToken.unlinkToDeath(device, 0); } @@ -147,7 +147,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { if (device != null) { Slog.i(TAG, "Virtual display device released because application token died: " + device.mOwnerPackageName); - device.destroyLocked(); + device.destroyLocked(false); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); } } @@ -205,19 +205,19 @@ final class VirtualDisplayAdapter extends DisplayAdapter { @Override public void binderDied() { synchronized (getSyncRoot()) { - if (mSurface != null) { - handleBinderDiedLocked(mAppToken); - } + handleBinderDiedLocked(mAppToken); } } - public void destroyLocked() { + public void destroyLocked(boolean binderAlive) { if (mSurface != null) { mSurface.release(); mSurface = null; } SurfaceControl.destroyDisplay(getDisplayTokenLocked()); - mCallback.dispatchDisplayStopped(); + if (binderAlive) { + mCallback.dispatchDisplayStopped(); + } } @Override diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index e79a206..e6f5e3d 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -234,6 +234,14 @@ public class UserManagerService extends IUserManager.Stub { mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); initDefaultGuestRestrictions(); readUserListLocked(); + sInstance = this; + } + } + } + + void systemReady() { + synchronized (mInstallLock) { + synchronized (mPackagesLock) { // Prune out any partially created/partially removed users. ArrayList<UserInfo> partials = new ArrayList<UserInfo>(); for (int i = 0; i < mUsers.size(); i++) { @@ -248,12 +256,8 @@ public class UserManagerService extends IUserManager.Stub { + " (name=" + ui.name + ")"); removeUserStateLocked(ui.id); } - sInstance = this; } } - } - - void systemReady() { userForeground(UserHandle.USER_OWNER); mAppOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java index e972ec7..5877b3e 100644 --- a/services/core/java/com/android/server/policy/BarController.java +++ b/services/core/java/com/android/server/policy/BarController.java @@ -58,6 +58,9 @@ public class BarController { private int mTransientBarState; private boolean mPendingShow; private long mLastTranslucent; + private boolean mShowTransparent; + private boolean mSetUnHideFlagWhenNextTransparent; + private boolean mNoAnimationOnNextShow; public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag, int statusBarManagerId, int translucentWmFlag) { @@ -74,6 +77,14 @@ public class BarController { mWin = win; } + public void setShowTransparent(boolean transparent) { + if (transparent != mShowTransparent) { + mShowTransparent = transparent; + mSetUnHideFlagWhenNextTransparent = transparent; + mNoAnimationOnNextShow = true; + } + } + public void showTransient() { if (mWin != null) { setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED); @@ -135,7 +146,9 @@ public class BarController { } final boolean wasVis = mWin.isVisibleLw(); final boolean wasAnim = mWin.isAnimatingLw(); - final boolean change = show ? mWin.showLw(true) : mWin.hideLw(true); + final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow) + : mWin.hideLw(!mNoAnimationOnNextShow); + mNoAnimationOnNextShow = false; final int state = computeStateLw(wasVis, wasAnim, mWin, change); final boolean stateChanged = updateStateLw(state); return change || stateChanged; @@ -233,6 +246,13 @@ public class BarController { setTransientBarState(TRANSIENT_BAR_NONE); // request denied } } + if (mShowTransparent) { + vis |= View.SYSTEM_UI_TRANSPARENT; + if (mSetUnHideFlagWhenNextTransparent) { + vis |= mUnhideFlag; + mSetUnHideFlagWhenNextTransparent = false; + } + } if (mTransientBarState != TRANSIENT_BAR_NONE) { vis |= mTransientFlag; // ignore clear requests until transition completes vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index dbd3676..185ef03 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -476,6 +476,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mTopIsFullscreen; boolean mForceStatusBar; boolean mForceStatusBarFromKeyguard; + private boolean mForceStatusBarTransparent; boolean mHideLockScreen; boolean mForcingShowNavBar; int mForcingShowNavBarLayer; @@ -4129,6 +4130,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAppsThatDismissKeyguard.clear(); mForceStatusBar = false; mForceStatusBarFromKeyguard = false; + mForceStatusBarTransparent = false; mForcingShowNavBar = false; mForcingShowNavBarLayer = -1; @@ -4154,8 +4156,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { mForcingShowNavBar = true; mForcingShowNavBarLayer = win.getSurfaceLayer(); } - if (attrs.type == TYPE_STATUS_BAR && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { - mForceStatusBarFromKeyguard = true; + if (attrs.type == TYPE_STATUS_BAR) { + if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { + mForceStatusBarFromKeyguard = true; + } + if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) { + mForceStatusBarTransparent = true; + } } boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW @@ -4302,7 +4309,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar + " forcefkg=" + mForceStatusBarFromKeyguard + " top=" + mTopFullscreenOpaqueWindowState); - if (mForceStatusBar || mForceStatusBarFromKeyguard) { + boolean shouldBeTransparent = mForceStatusBarTransparent + && !mForceStatusBar + && !mForceStatusBarFromKeyguard; + if (!shouldBeTransparent) { + mStatusBarController.setShowTransparent(false /* transparent */); + } else if (!mStatusBar.isVisibleLw()) { + mStatusBarController.setShowTransparent(true /* transparent */); + } + if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent) { if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced"); if (mStatusBarController.setBarShowingLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 2897c61..fcdb6d6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -754,7 +754,7 @@ public class VoiceInteractionManagerService extends SystemService { public boolean activeServiceSupportsAssist() { enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); synchronized (this) { - return mImpl != null && mImpl.mInfo.getSupportsAssist(); + return mImpl != null && mImpl.mInfo != null && mImpl.mInfo.getSupportsAssist(); } } @@ -762,7 +762,8 @@ public class VoiceInteractionManagerService extends SystemService { public boolean activeServiceSupportsLaunchFromKeyguard() throws RemoteException { enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); synchronized (this) { - return mImpl != null && mImpl.mInfo.getSupportsLaunchFromKeyguard(); + return mImpl != null && mImpl.mInfo != null + && mImpl.mInfo.getSupportsLaunchFromKeyguard(); } } diff --git a/tests/HierarchyViewerTest/.gitignore b/tests/HierarchyViewerTest/.gitignore new file mode 100644 index 0000000..75eec98 --- /dev/null +++ b/tests/HierarchyViewerTest/.gitignore @@ -0,0 +1,6 @@ +.gradle +.idea +*.iml +gradle* +build +local.properties diff --git a/tests/HierarchyViewerTest/Android.mk b/tests/HierarchyViewerTest/Android.mk new file mode 100644 index 0000000..07b90f0 --- /dev/null +++ b/tests/HierarchyViewerTest/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := HierarchyViewerTest + +LOCAL_JAVA_LIBRARIES := android.test.runner + +include $(BUILD_PACKAGE) diff --git a/tests/HierarchyViewerTest/AndroidManifest.xml b/tests/HierarchyViewerTest/AndroidManifest.xml new file mode 100644 index 0000000..65f2fd3 --- /dev/null +++ b/tests/HierarchyViewerTest/AndroidManifest.xml @@ -0,0 +1,36 @@ +<!-- + ~ 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 + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.hierarchyviewer"> + + <application> + <uses-library android:name="android.test.runner" /> + + <activity + android:name=".MainActivity" + android:label="HvTest" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + + <instrumentation + android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.test.hierarchyviewer" /> +</manifest> diff --git a/tests/HierarchyViewerTest/build.gradle b/tests/HierarchyViewerTest/build.gradle new file mode 100644 index 0000000..e8cdfa2 --- /dev/null +++ b/tests/HierarchyViewerTest/build.gradle @@ -0,0 +1,31 @@ +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.1.0+' + + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "22.0.0" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + res.srcDirs = ['res'] + } + } +} diff --git a/tests/HierarchyViewerTest/res/layout/activity_main.xml b/tests/HierarchyViewerTest/res/layout/activity_main.xml new file mode 100644 index 0000000..410a776 --- /dev/null +++ b/tests/HierarchyViewerTest/res/layout/activity_main.xml @@ -0,0 +1,12 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/textView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:scaleX="10" + android:text="@string/test" /> + +</RelativeLayout> diff --git a/tests/HierarchyViewerTest/res/menu/menu_main.xml b/tests/HierarchyViewerTest/res/menu/menu_main.xml new file mode 100644 index 0000000..9b78a1e --- /dev/null +++ b/tests/HierarchyViewerTest/res/menu/menu_main.xml @@ -0,0 +1,5 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> + <item android:id="@+id/action_settings" android:title="Settings" + android:orderInCategory="100" android:showAsAction="never" /> +</menu> diff --git a/tests/HierarchyViewerTest/res/values/strings.xml b/tests/HierarchyViewerTest/res/values/strings.xml new file mode 100644 index 0000000..800ee1c --- /dev/null +++ b/tests/HierarchyViewerTest/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="test">Hello World</string> +</resources>
\ No newline at end of file diff --git a/tests/HierarchyViewerTest/run_tests.sh b/tests/HierarchyViewerTest/run_tests.sh new file mode 100644 index 0000000..094bb4c --- /dev/null +++ b/tests/HierarchyViewerTest/run_tests.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# Runs the tests in this apk +adb install $OUT/data/app/HierarchyViewerTest/HierarchyViewerTest.apk +adb shell am instrument -w com.android.test.hierarchyviewer/android.test.InstrumentationTestRunner diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/Decoder.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/Decoder.java new file mode 100644 index 0000000..c6f1470 --- /dev/null +++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/Decoder.java @@ -0,0 +1,101 @@ +package com.android.test.hierarchyviewer; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +public class Decoder { + // Prefixes for simple primitives. These match the JNI definitions. + public static final byte SIG_BOOLEAN = 'Z'; + public static final byte SIG_BYTE = 'B'; + public static final byte SIG_SHORT = 'S'; + public static final byte SIG_INT = 'I'; + public static final byte SIG_LONG = 'J'; + public static final byte SIG_FLOAT = 'F'; + public static final byte SIG_DOUBLE = 'D'; + + // Prefixes for some commonly used objects + public static final byte SIG_STRING = 'R'; + + public static final byte SIG_MAP = 'M'; // a map with an short key + public static final short SIG_END_MAP = 0; + + private final ByteBuffer mBuf; + + public Decoder(byte[] buf) { + this(ByteBuffer.wrap(buf)); + } + + public Decoder(ByteBuffer buf) { + mBuf = buf; + } + + public boolean hasRemaining() { + return mBuf.hasRemaining(); + } + + public Object readObject() { + byte sig = mBuf.get(); + + switch (sig) { + case SIG_BOOLEAN: + return mBuf.get() == 0 ? Boolean.FALSE : Boolean.TRUE; + case SIG_BYTE: + return mBuf.get(); + case SIG_SHORT: + return mBuf.getShort(); + case SIG_INT: + return mBuf.getInt(); + case SIG_LONG: + return mBuf.getLong(); + case SIG_FLOAT: + return mBuf.getFloat(); + case SIG_DOUBLE: + return mBuf.getDouble(); + case SIG_STRING: + return readString(); + case SIG_MAP: + return readMap(); + default: + throw new DecoderException(sig, mBuf.position() - 1); + } + } + + private String readString() { + short len = mBuf.getShort(); + byte[] b = new byte[len]; + mBuf.get(b, 0, len); + return new String(b, Charset.forName("utf-8")); + } + + private Map<Short, Object> readMap() { + Map<Short, Object> m = new HashMap<Short, Object>(); + + while (true) { + Object o = readObject(); + if (!(o instanceof Short)) { + throw new DecoderException("Expected short key, got " + o.getClass()); + } + + Short key = (Short)o; + if (key == SIG_END_MAP) { + break; + } + + m.put(key, readObject()); + } + + return m; + } + + public static class DecoderException extends RuntimeException { + public DecoderException(byte seen, int pos) { + super(String.format("Unexpected byte %c seen at position %d", (char)seen, pos)); + } + + public DecoderException(String msg) { + super(msg); + } + } +} diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivity.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivity.java new file mode 100644 index 0000000..3a67273 --- /dev/null +++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivity.java @@ -0,0 +1,44 @@ +package com.android.test.hierarchyviewer; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + + +public class MainActivity extends Activity { + private static final String TAG = "Main"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + View textView = findViewById(R.id.textView); + Log.d(TAG, "x, y = " + textView.getX() + ", " + textView.getY()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivityTest.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivityTest.java new file mode 100644 index 0000000..ea3710d --- /dev/null +++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivityTest.java @@ -0,0 +1,81 @@ +package com.android.test.hierarchyviewer; + +import android.test.ActivityInstrumentationTestCase2; +import android.view.View; + +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> { + private MainActivity mActivity; + private View mTextView; + + + public MainActivityTest() { + super(MainActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mActivity = getActivity(); + mTextView = mActivity.findViewById(R.id.textView); + } + + private byte[] encode(View view) throws ClassNotFoundException, NoSuchMethodException, + IllegalAccessException, InstantiationException, InvocationTargetException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 1024); + + Object encoder = createEncoder(baos); + invokeMethod(View.class, view, "encode", encoder); + invokeMethod(encoder.getClass(), encoder, "endStream"); + + return baos.toByteArray(); + } + + private Object invokeMethod(Class targetClass, Object target, String methodName, Object... params) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class[] paramClasses = new Class[params.length]; + for (int i = 0; i < params.length; i++) { + paramClasses[i] = params[i].getClass(); + } + Method method = targetClass.getDeclaredMethod(methodName, paramClasses); + method.setAccessible(true); + return method.invoke(target, params); + } + + private Object createEncoder(ByteArrayOutputStream baos) throws ClassNotFoundException, + NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { + Class clazz = Class.forName("android.view.ViewHierarchyEncoder"); + Constructor constructor = clazz.getConstructor(ByteArrayOutputStream.class); + return constructor.newInstance(baos); + } + + public void testTextView() throws Exception { + byte[] data = encode(mTextView); + assertNotNull(data); + assertTrue(data.length > 0); + + ViewDumpParser parser = new ViewDumpParser(); + parser.parse(data); + + List<Map<Short, Object>> views = parser.getViews(); + Map<String, Short> propertyNameTable = parser.getIds(); + + assertEquals(1, views.size()); + assertNotNull(propertyNameTable); + + Map<Short, Object> textViewProperties = views.get(0); + assertEquals("android.widget.TextView", + textViewProperties.get(propertyNameTable.get("meta:__name__"))); + + assertEquals(mActivity.getString(R.string.test), + textViewProperties.get(propertyNameTable.get("text:text"))); + } +} diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java new file mode 100644 index 0000000..0111bc6 --- /dev/null +++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java @@ -0,0 +1,73 @@ +package com.android.test.hierarchyviewer; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class ViewDumpParser { + private Map<String, Short> mIds; + private List<Map<Short,Object>> mViews; + + public void parse(byte[] data) { + Decoder d = new Decoder(ByteBuffer.wrap(data)); + + mViews = new ArrayList<>(100); + while (d.hasRemaining()) { + Object o = d.readObject(); + if (o instanceof Map) { + //noinspection unchecked + mViews.add((Map<Short, Object>) o); + } + } + + if (mViews.isEmpty()) { + return; + } + + // the last one is the property map + Map<Short,Object> idMap = mViews.remove(mViews.size() - 1); + mIds = reverse(idMap); + } + + public String getFirstView() { + if (mViews.isEmpty()) { + return null; + } + + Map<Short, Object> props = mViews.get(0); + Object name = getProperty(props, "__name__"); + Object hash = getProperty(props, "__hash__"); + + if (name instanceof String && hash instanceof Integer) { + return String.format(Locale.US, "%s@%x", name, hash); + } else { + return null; + } + } + + private Object getProperty(Map<Short, Object> props, String key) { + return props.get(mIds.get(key)); + } + + private static Map<String, Short> reverse(Map<Short, Object> m) { + Map<String, Short> r = new HashMap<String, Short>(m.size()); + + for (Map.Entry<Short, Object> e : m.entrySet()) { + r.put((String)e.getValue(), e.getKey()); + } + + return r; + } + + public List<Map<Short, Object>> getViews() { + return mViews; + } + + public Map<String, Short> getIds() { + return mIds; + } + +} |