Loading core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -287,6 +287,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setDreamOverlay(@Nullable android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setScreensaverEnabled(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setSystemDreamComponent(@Nullable android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@Nullable android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(); method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream(); } Loading core/java/android/app/DreamManager.java +19 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,25 @@ public class DreamManager { com.android.internal.R.bool.config_dreamsSupported); } /** * Starts dreaming. * * This API is equivalent to {@link DreamManager#startDream()} but with a nullable component * name to be compatible with TM CTS tests. * * <p>This is only used for testing the dream service APIs. * * @see DreamManager#startDream() * * @hide */ @TestApi @UserHandleAware @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@Nullable ComponentName name) { startDream(); } /** * Starts dreaming. * Loading core/java/android/app/PropertyInvalidatedCache.java +62 −36 Original line number Diff line number Diff line Loading @@ -391,7 +391,12 @@ public class PropertyInvalidatedCache<Query, Result> { private static final boolean DEBUG = false; private static final boolean VERIFY = false; // Per-Cache performance counters. As some cache instances are declared static, /** * The object-private lock. */ private final Object mLock = new Object(); // Per-Cache performance counters. @GuardedBy("mLock") private long mHits = 0; Loading @@ -410,25 +415,19 @@ public class PropertyInvalidatedCache<Query, Result> { @GuardedBy("mLock") private long mClears = 0; // Most invalidation is done in a static context, so the counters need to be accessible. @GuardedBy("sCorkLock") private static final HashMap<String, Long> sInvalidates = new HashMap<>(); /** * Record the number of invalidate or cork calls that were nops because * the cache was already corked. This is static because invalidation is * done in a static context. * Protect objects that support corking. mLock and sGlobalLock must never be taken while this * is held. */ @GuardedBy("sCorkLock") private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>(); private static final Object sCorkLock = new Object(); /** * If sEnabled is false then all cache operations are stubbed out. Set * it to false inside test processes. * Record the number of invalidate or cork calls that were nops because the cache was already * corked. This is static because invalidation is done in a static context. Entries are * indexed by the cache property. */ private static boolean sEnabled = true; private static final Object sCorkLock = new Object(); @GuardedBy("sCorkLock") private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>(); /** * A map of cache keys that we've "corked". (The values are counts.) When a cache key is Loading @@ -439,22 +438,40 @@ public class PropertyInvalidatedCache<Query, Result> { @GuardedBy("sCorkLock") private static final HashMap<String, Integer> sCorks = new HashMap<>(); /** * A lock for the global list of caches and cache keys. This must never be taken inside mLock * or sCorkLock. */ private static final Object sGlobalLock = new Object(); /** * A map of cache keys that have been disabled in the local process. When a key is * disabled locally, existing caches are disabled and the key is saved in this map. * Future cache instances that use the same key will be disabled in their constructor. */ @GuardedBy("sCorkLock") @GuardedBy("sGlobalLock") private static final HashSet<String> sDisabledKeys = new HashSet<>(); /** * Weakly references all cache objects in the current process, allowing us to iterate over * them all for purposes like issuing debug dumps and reacting to memory pressure. */ @GuardedBy("sCorkLock") @GuardedBy("sGlobalLock") private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>(); private final Object mLock = new Object(); /** * Counts of the number of times a cache key was invalidated. Invalidation occurs in a static * context with no cache object available, so this is a static map. Entries are indexed by * the cache property. */ @GuardedBy("sGlobalLock") private static final HashMap<String, Long> sInvalidates = new HashMap<>(); /** * If sEnabled is false then all cache operations are stubbed out. Set * it to false inside test processes. */ private static boolean sEnabled = true; /** * Name of the property that holds the unique value that we use to invalidate the cache. Loading Loading @@ -595,14 +612,17 @@ public class PropertyInvalidatedCache<Query, Result> { }; } // Register the map in the global list. If the cache is disabled globally, disable it // now. /** * Register the map in the global list. If the cache is disabled globally, disable it * now. This method is only ever called from the constructor, which means no other thread has * access to the object yet. It can safely be modified outside any lock. */ private void registerCache() { synchronized (sCorkLock) { sCaches.put(this, null); synchronized (sGlobalLock) { if (sDisabledKeys.contains(mCacheName)) { disableInstance(); } sCaches.put(this, null); } } Loading Loading @@ -797,8 +817,9 @@ public class PropertyInvalidatedCache<Query, Result> { } /** * Disable the use of this cache in this process. This method is using during * testing. To disable a cache in normal code, use disableLocal(). * Disable the use of this cache in this process. This method is using internally and during * testing. To disable a cache in normal code, use disableLocal(). A disabled cache cannot * be re-enabled. * @hide */ @TestApi Loading @@ -811,17 +832,23 @@ public class PropertyInvalidatedCache<Query, Result> { /** * Disable the local use of all caches with the same name. All currently registered caches * using the key will be disabled now, and all future cache instances that use the key will be * disabled in their constructor. * with the name will be disabled now, and all future cache instances that use the name will * be disabled in their constructor. */ private static final void disableLocal(@NonNull String name) { synchronized (sCorkLock) { sDisabledKeys.add(name); synchronized (sGlobalLock) { if (sDisabledKeys.contains(name)) { // The key is already in recorded so there is no further work to be done. return; } for (PropertyInvalidatedCache cache : sCaches.keySet()) { if (name.equals(cache.mCacheName)) { cache.disableInstance(); } } // Record the disabled key after the iteration. If an exception occurs during the // iteration above, and the code is retried, the function should not exit early. sDisabledKeys.add(name); } } Loading @@ -834,7 +861,7 @@ public class PropertyInvalidatedCache<Query, Result> { */ @TestApi public final void forgetDisableLocal() { synchronized (sCorkLock) { synchronized (sGlobalLock) { sDisabledKeys.remove(mCacheName); } } Loading @@ -851,9 +878,9 @@ public class PropertyInvalidatedCache<Query, Result> { } /** * Disable this cache in the current process, and all other caches that use the same * name. This does not affect caches that have a different name but use the same * property. * Disable this cache in the current process, and all other present and future caches that use * the same name. This does not affect caches that have a different name but use the same * property. Once disabled, a cache cannot be reenabled. * @hide */ @TestApi Loading Loading @@ -1381,11 +1408,10 @@ public class PropertyInvalidatedCache<Query, Result> { /** * Returns a list of caches alive at the current time. */ @GuardedBy("sGlobalLock") private static @NonNull ArrayList<PropertyInvalidatedCache> getActiveCaches() { synchronized (sCorkLock) { return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet()); } } /** * Returns a list of the active corks in a process. Loading Loading @@ -1540,7 +1566,7 @@ public class PropertyInvalidatedCache<Query, Result> { boolean detail = anyDetailed(args); ArrayList<PropertyInvalidatedCache> activeCaches; synchronized (sCorkLock) { synchronized (sGlobalLock) { activeCaches = getActiveCaches(); if (!detail) { dumpCorkInfo(pw); Loading core/java/android/ddm/DdmHandleViewDebug.java +234 −63 Original line number Diff line number Diff line Loading @@ -16,12 +16,16 @@ package android.ddm; import static com.android.internal.util.Preconditions.checkArgument; import android.util.Log; import android.view.View; import android.view.ViewDebug; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; import com.android.internal.annotations.VisibleForTesting; import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; Loading @@ -34,6 +38,7 @@ import java.io.OutputStreamWriter; import java.lang.reflect.Method; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; /** * Handle various requests related to profiling / debugging of the view system. Loading Loading @@ -123,15 +128,16 @@ public class DdmHandleViewDebug extends DdmHandle { } if (type == CHUNK_VURT) { if (op == VURT_DUMP_HIERARCHY) if (op == VURT_DUMP_HIERARCHY) { return dumpHierarchy(rootView, in); else if (op == VURT_CAPTURE_LAYERS) } else if (op == VURT_CAPTURE_LAYERS) { return captureLayers(rootView); else if (op == VURT_DUMP_THEME) } else if (op == VURT_DUMP_THEME) { return dumpTheme(rootView); else } else { return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op); } } final View targetView = getTargetView(rootView, in); if (targetView == null) { Loading Loading @@ -305,16 +311,46 @@ public class DdmHandleViewDebug extends DdmHandle { * The method name and its arguments are passed in as inputs via the byte buffer. * The buffer contains:<ol> * <li> len(method name) </li> * <li> method name </li> * <li> method name (encoded as UTF-16 2-byte characters) </li> * <li> # of args </li> * <li> arguments: Each argument comprises of a type specifier followed by the actual argument. * The type specifier is a single character as used in JNI: * (Z - boolean, B - byte, C - char, S - short, I - int, J - long, * F - float, D - double). <p> * The type specifier is followed by the actual value of argument. * Booleans are encoded via bytes with 0 indicating false.</li> * The type specifier is one character modelled after JNI signatures: * <ul> * <li>[ - array<br> * This is followed by a second character according to this spec, indicating the * array type, then the array length as an Int, followed by a repeated encoding * of the actual data. * WARNING: Only <b>byte[]</b> is supported currently. * </li> * <li>Z - boolean<br> * Booleans are encoded via bytes with 0 indicating false</li> * <li>B - byte</li> * <li>C - char</li> * <li>S - short</li> * <li>I - int</li> * <li>J - long</li> * <li>F - float</li> * <li>D - double</li> * <li>V - void<br> * NOT followed by a value. Only used for return types</li> * <li>R - String (not a real JNI type, but added for convenience)<br> * Strings are encoded as an unsigned short of the number of <b>bytes</b>, * followed by the actual UTF-8 encoded bytes. * WARNING: This is the same encoding as produced by * ViewHierarchyEncoder#writeString. However, note that this encoding is * different to what DdmHandle#getString() expects, which is used in other places * in this class. * WARNING: Since the length is the number of UTF-8 encoded bytes, Strings can * contain up to 64k ASCII characters, yet depending on the actual data, the true * maximum might be as little as 21844 unicode characters. * <b>null</b> String objects are encoded as an empty string * </li> * </ul> * </li> * </ol> * Methods that take no arguments need only specify the method name. * * The return value is encoded the same way as a single parameter (type + value) */ private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) { int l = in.getInt(); Loading @@ -327,54 +363,17 @@ public class DdmHandleViewDebug extends DdmHandle { args = new Object[0]; } else { int nArgs = in.getInt(); argTypes = new Class<?>[nArgs]; args = new Object[nArgs]; for (int i = 0; i < nArgs; i++) { char c = in.getChar(); switch (c) { case 'Z': argTypes[i] = boolean.class; args[i] = in.get() == 0 ? false : true; break; case 'B': argTypes[i] = byte.class; args[i] = in.get(); break; case 'C': argTypes[i] = char.class; args[i] = in.getChar(); break; case 'S': argTypes[i] = short.class; args[i] = in.getShort(); break; case 'I': argTypes[i] = int.class; args[i] = in.getInt(); break; case 'J': argTypes[i] = long.class; args[i] = in.getLong(); break; case 'F': argTypes[i] = float.class; args[i] = in.getFloat(); break; case 'D': argTypes[i] = double.class; args[i] = in.getDouble(); break; default: Log.e(TAG, "arg " + i + ", unrecognized type: " + c); return createFailChunk(ERR_INVALID_PARAM, "Unsupported parameter type (" + c + ") to invoke view method."); } try { deserializeMethodParameters(args, argTypes, in); } catch (ViewMethodInvocationSerializationException e) { return createFailChunk(ERR_INVALID_PARAM, e.getMessage()); } } Method method = null; Method method; try { method = targetView.getClass().getMethod(methodName, argTypes); } catch (NoSuchMethodException e) { Loading @@ -384,7 +383,10 @@ public class DdmHandleViewDebug extends DdmHandle { } try { ViewDebug.invokeViewMethod(targetView, method, args); Object result = ViewDebug.invokeViewMethod(targetView, method, args); Class<?> returnType = method.getReturnType(); byte[] returnValue = serializeReturnValue(returnType, returnType.cast(result)); return new Chunk(CHUNK_VUOP, returnValue, 0, returnValue.length); } catch (Exception e) { Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage()); String msg = e.getCause().getMessage(); Loading @@ -393,8 +395,6 @@ public class DdmHandleViewDebug extends DdmHandle { } return createFailChunk(ERR_EXCEPTION, msg); } return null; } private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) { Loading Loading @@ -431,4 +431,175 @@ public class DdmHandleViewDebug extends DdmHandle { byte[] data = b.toByteArray(); return new Chunk(CHUNK_VUOP, data, 0, data.length); } /** * Deserializes parameters according to the VUOP_INVOKE_VIEW_METHOD protocol the {@code in} * buffer. * * The length of {@code args} determines how many arguments are read. The {@code argTypes} must * be the same length, and will be set to the argument types of the data read. * * @hide */ @VisibleForTesting public static void deserializeMethodParameters( Object[] args, Class<?>[] argTypes, ByteBuffer in) throws ViewMethodInvocationSerializationException { checkArgument(args.length == argTypes.length); for (int i = 0; i < args.length; i++) { char typeSignature = in.getChar(); boolean isArray = typeSignature == SIG_ARRAY; if (isArray) { char arrayType = in.getChar(); if (arrayType != SIG_BYTE) { // This implementation only supports byte-arrays for now. throw new ViewMethodInvocationSerializationException( "Unsupported array parameter type (" + typeSignature + ") to invoke view method @argument " + i); } int arrayLength = in.getInt(); if (arrayLength > in.remaining()) { // The sender did not actually sent the specified amount of bytes. This // avoids a malformed packet to trigger an out-of-memory error. throw new BufferUnderflowException(); } byte[] byteArray = new byte[arrayLength]; in.get(byteArray); argTypes[i] = byte[].class; args[i] = byteArray; } else { switch (typeSignature) { case SIG_BOOLEAN: argTypes[i] = boolean.class; args[i] = in.get() != 0; break; case SIG_BYTE: argTypes[i] = byte.class; args[i] = in.get(); break; case SIG_CHAR: argTypes[i] = char.class; args[i] = in.getChar(); break; case SIG_SHORT: argTypes[i] = short.class; args[i] = in.getShort(); break; case SIG_INT: argTypes[i] = int.class; args[i] = in.getInt(); break; case SIG_LONG: argTypes[i] = long.class; args[i] = in.getLong(); break; case SIG_FLOAT: argTypes[i] = float.class; args[i] = in.getFloat(); break; case SIG_DOUBLE: argTypes[i] = double.class; args[i] = in.getDouble(); break; case SIG_STRING: { argTypes[i] = String.class; int stringUtf8ByteCount = Short.toUnsignedInt(in.getShort()); byte[] rawStringBuffer = new byte[stringUtf8ByteCount]; in.get(rawStringBuffer); args[i] = new String(rawStringBuffer, StandardCharsets.UTF_8); break; } default: Log.e(TAG, "arg " + i + ", unrecognized type: " + typeSignature); throw new ViewMethodInvocationSerializationException( "Unsupported parameter type (" + typeSignature + ") to invoke view method."); } } } } /** * Serializes {@code value} to the wire protocol of VUOP_INVOKE_VIEW_METHOD. * @hide */ @VisibleForTesting public static byte[] serializeReturnValue(Class<?> type, Object value) throws ViewMethodInvocationSerializationException, IOException { ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(1024); DataOutputStream dos = new DataOutputStream(byteOutStream); if (type.isArray()) { if (!type.equals(byte[].class)) { // Only byte arrays are supported currently. throw new ViewMethodInvocationSerializationException( "Unsupported array return type (" + type + ")"); } byte[] byteArray = (byte[]) value; dos.writeChar(SIG_ARRAY); dos.writeChar(SIG_BYTE); dos.writeInt(byteArray.length); dos.write(byteArray); } else if (boolean.class.equals(type)) { dos.writeChar(SIG_BOOLEAN); dos.write((boolean) value ? 1 : 0); } else if (byte.class.equals(type)) { dos.writeChar(SIG_BYTE); dos.writeByte((byte) value); } else if (char.class.equals(type)) { dos.writeChar(SIG_CHAR); dos.writeChar((char) value); } else if (short.class.equals(type)) { dos.writeChar(SIG_SHORT); dos.writeShort((short) value); } else if (int.class.equals(type)) { dos.writeChar(SIG_INT); dos.writeInt((int) value); } else if (long.class.equals(type)) { dos.writeChar(SIG_LONG); dos.writeLong((long) value); } else if (double.class.equals(type)) { dos.writeChar(SIG_DOUBLE); dos.writeDouble((double) value); } else if (float.class.equals(type)) { dos.writeChar(SIG_FLOAT); dos.writeFloat((float) value); } else if (String.class.equals(type)) { dos.writeChar(SIG_STRING); dos.writeUTF(value != null ? (String) value : ""); } else { dos.writeChar(SIG_VOID); } return byteOutStream.toByteArray(); } // Prefixes for simple primitives. These match the JNI definitions. private static final char SIG_ARRAY = '['; private static final char SIG_BOOLEAN = 'Z'; private static final char SIG_BYTE = 'B'; private static final char SIG_SHORT = 'S'; private static final char SIG_CHAR = 'C'; private static final char SIG_INT = 'I'; private static final char SIG_LONG = 'J'; private static final char SIG_FLOAT = 'F'; private static final char SIG_DOUBLE = 'D'; private static final char SIG_VOID = 'V'; // Prefixes for some commonly used objects private static final char SIG_STRING = 'R'; /** * @hide */ @VisibleForTesting public static class ViewMethodInvocationSerializationException extends Exception { ViewMethodInvocationSerializationException(String message) { super(message); } } } core/java/android/ddm/OWNERS 0 → 100644 +1 −0 Original line number Diff line number Diff line per-file DdmHandleViewDebug.java = michschn@google.com Loading
core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -287,6 +287,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setDreamOverlay(@Nullable android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setScreensaverEnabled(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setSystemDreamComponent(@Nullable android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@Nullable android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(); method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream(); } Loading
core/java/android/app/DreamManager.java +19 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,25 @@ public class DreamManager { com.android.internal.R.bool.config_dreamsSupported); } /** * Starts dreaming. * * This API is equivalent to {@link DreamManager#startDream()} but with a nullable component * name to be compatible with TM CTS tests. * * <p>This is only used for testing the dream service APIs. * * @see DreamManager#startDream() * * @hide */ @TestApi @UserHandleAware @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@Nullable ComponentName name) { startDream(); } /** * Starts dreaming. * Loading
core/java/android/app/PropertyInvalidatedCache.java +62 −36 Original line number Diff line number Diff line Loading @@ -391,7 +391,12 @@ public class PropertyInvalidatedCache<Query, Result> { private static final boolean DEBUG = false; private static final boolean VERIFY = false; // Per-Cache performance counters. As some cache instances are declared static, /** * The object-private lock. */ private final Object mLock = new Object(); // Per-Cache performance counters. @GuardedBy("mLock") private long mHits = 0; Loading @@ -410,25 +415,19 @@ public class PropertyInvalidatedCache<Query, Result> { @GuardedBy("mLock") private long mClears = 0; // Most invalidation is done in a static context, so the counters need to be accessible. @GuardedBy("sCorkLock") private static final HashMap<String, Long> sInvalidates = new HashMap<>(); /** * Record the number of invalidate or cork calls that were nops because * the cache was already corked. This is static because invalidation is * done in a static context. * Protect objects that support corking. mLock and sGlobalLock must never be taken while this * is held. */ @GuardedBy("sCorkLock") private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>(); private static final Object sCorkLock = new Object(); /** * If sEnabled is false then all cache operations are stubbed out. Set * it to false inside test processes. * Record the number of invalidate or cork calls that were nops because the cache was already * corked. This is static because invalidation is done in a static context. Entries are * indexed by the cache property. */ private static boolean sEnabled = true; private static final Object sCorkLock = new Object(); @GuardedBy("sCorkLock") private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>(); /** * A map of cache keys that we've "corked". (The values are counts.) When a cache key is Loading @@ -439,22 +438,40 @@ public class PropertyInvalidatedCache<Query, Result> { @GuardedBy("sCorkLock") private static final HashMap<String, Integer> sCorks = new HashMap<>(); /** * A lock for the global list of caches and cache keys. This must never be taken inside mLock * or sCorkLock. */ private static final Object sGlobalLock = new Object(); /** * A map of cache keys that have been disabled in the local process. When a key is * disabled locally, existing caches are disabled and the key is saved in this map. * Future cache instances that use the same key will be disabled in their constructor. */ @GuardedBy("sCorkLock") @GuardedBy("sGlobalLock") private static final HashSet<String> sDisabledKeys = new HashSet<>(); /** * Weakly references all cache objects in the current process, allowing us to iterate over * them all for purposes like issuing debug dumps and reacting to memory pressure. */ @GuardedBy("sCorkLock") @GuardedBy("sGlobalLock") private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>(); private final Object mLock = new Object(); /** * Counts of the number of times a cache key was invalidated. Invalidation occurs in a static * context with no cache object available, so this is a static map. Entries are indexed by * the cache property. */ @GuardedBy("sGlobalLock") private static final HashMap<String, Long> sInvalidates = new HashMap<>(); /** * If sEnabled is false then all cache operations are stubbed out. Set * it to false inside test processes. */ private static boolean sEnabled = true; /** * Name of the property that holds the unique value that we use to invalidate the cache. Loading Loading @@ -595,14 +612,17 @@ public class PropertyInvalidatedCache<Query, Result> { }; } // Register the map in the global list. If the cache is disabled globally, disable it // now. /** * Register the map in the global list. If the cache is disabled globally, disable it * now. This method is only ever called from the constructor, which means no other thread has * access to the object yet. It can safely be modified outside any lock. */ private void registerCache() { synchronized (sCorkLock) { sCaches.put(this, null); synchronized (sGlobalLock) { if (sDisabledKeys.contains(mCacheName)) { disableInstance(); } sCaches.put(this, null); } } Loading Loading @@ -797,8 +817,9 @@ public class PropertyInvalidatedCache<Query, Result> { } /** * Disable the use of this cache in this process. This method is using during * testing. To disable a cache in normal code, use disableLocal(). * Disable the use of this cache in this process. This method is using internally and during * testing. To disable a cache in normal code, use disableLocal(). A disabled cache cannot * be re-enabled. * @hide */ @TestApi Loading @@ -811,17 +832,23 @@ public class PropertyInvalidatedCache<Query, Result> { /** * Disable the local use of all caches with the same name. All currently registered caches * using the key will be disabled now, and all future cache instances that use the key will be * disabled in their constructor. * with the name will be disabled now, and all future cache instances that use the name will * be disabled in their constructor. */ private static final void disableLocal(@NonNull String name) { synchronized (sCorkLock) { sDisabledKeys.add(name); synchronized (sGlobalLock) { if (sDisabledKeys.contains(name)) { // The key is already in recorded so there is no further work to be done. return; } for (PropertyInvalidatedCache cache : sCaches.keySet()) { if (name.equals(cache.mCacheName)) { cache.disableInstance(); } } // Record the disabled key after the iteration. If an exception occurs during the // iteration above, and the code is retried, the function should not exit early. sDisabledKeys.add(name); } } Loading @@ -834,7 +861,7 @@ public class PropertyInvalidatedCache<Query, Result> { */ @TestApi public final void forgetDisableLocal() { synchronized (sCorkLock) { synchronized (sGlobalLock) { sDisabledKeys.remove(mCacheName); } } Loading @@ -851,9 +878,9 @@ public class PropertyInvalidatedCache<Query, Result> { } /** * Disable this cache in the current process, and all other caches that use the same * name. This does not affect caches that have a different name but use the same * property. * Disable this cache in the current process, and all other present and future caches that use * the same name. This does not affect caches that have a different name but use the same * property. Once disabled, a cache cannot be reenabled. * @hide */ @TestApi Loading Loading @@ -1381,11 +1408,10 @@ public class PropertyInvalidatedCache<Query, Result> { /** * Returns a list of caches alive at the current time. */ @GuardedBy("sGlobalLock") private static @NonNull ArrayList<PropertyInvalidatedCache> getActiveCaches() { synchronized (sCorkLock) { return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet()); } } /** * Returns a list of the active corks in a process. Loading Loading @@ -1540,7 +1566,7 @@ public class PropertyInvalidatedCache<Query, Result> { boolean detail = anyDetailed(args); ArrayList<PropertyInvalidatedCache> activeCaches; synchronized (sCorkLock) { synchronized (sGlobalLock) { activeCaches = getActiveCaches(); if (!detail) { dumpCorkInfo(pw); Loading
core/java/android/ddm/DdmHandleViewDebug.java +234 −63 Original line number Diff line number Diff line Loading @@ -16,12 +16,16 @@ package android.ddm; import static com.android.internal.util.Preconditions.checkArgument; import android.util.Log; import android.view.View; import android.view.ViewDebug; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; import com.android.internal.annotations.VisibleForTesting; import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; Loading @@ -34,6 +38,7 @@ import java.io.OutputStreamWriter; import java.lang.reflect.Method; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; /** * Handle various requests related to profiling / debugging of the view system. Loading Loading @@ -123,15 +128,16 @@ public class DdmHandleViewDebug extends DdmHandle { } if (type == CHUNK_VURT) { if (op == VURT_DUMP_HIERARCHY) if (op == VURT_DUMP_HIERARCHY) { return dumpHierarchy(rootView, in); else if (op == VURT_CAPTURE_LAYERS) } else if (op == VURT_CAPTURE_LAYERS) { return captureLayers(rootView); else if (op == VURT_DUMP_THEME) } else if (op == VURT_DUMP_THEME) { return dumpTheme(rootView); else } else { return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op); } } final View targetView = getTargetView(rootView, in); if (targetView == null) { Loading Loading @@ -305,16 +311,46 @@ public class DdmHandleViewDebug extends DdmHandle { * The method name and its arguments are passed in as inputs via the byte buffer. * The buffer contains:<ol> * <li> len(method name) </li> * <li> method name </li> * <li> method name (encoded as UTF-16 2-byte characters) </li> * <li> # of args </li> * <li> arguments: Each argument comprises of a type specifier followed by the actual argument. * The type specifier is a single character as used in JNI: * (Z - boolean, B - byte, C - char, S - short, I - int, J - long, * F - float, D - double). <p> * The type specifier is followed by the actual value of argument. * Booleans are encoded via bytes with 0 indicating false.</li> * The type specifier is one character modelled after JNI signatures: * <ul> * <li>[ - array<br> * This is followed by a second character according to this spec, indicating the * array type, then the array length as an Int, followed by a repeated encoding * of the actual data. * WARNING: Only <b>byte[]</b> is supported currently. * </li> * <li>Z - boolean<br> * Booleans are encoded via bytes with 0 indicating false</li> * <li>B - byte</li> * <li>C - char</li> * <li>S - short</li> * <li>I - int</li> * <li>J - long</li> * <li>F - float</li> * <li>D - double</li> * <li>V - void<br> * NOT followed by a value. Only used for return types</li> * <li>R - String (not a real JNI type, but added for convenience)<br> * Strings are encoded as an unsigned short of the number of <b>bytes</b>, * followed by the actual UTF-8 encoded bytes. * WARNING: This is the same encoding as produced by * ViewHierarchyEncoder#writeString. However, note that this encoding is * different to what DdmHandle#getString() expects, which is used in other places * in this class. * WARNING: Since the length is the number of UTF-8 encoded bytes, Strings can * contain up to 64k ASCII characters, yet depending on the actual data, the true * maximum might be as little as 21844 unicode characters. * <b>null</b> String objects are encoded as an empty string * </li> * </ul> * </li> * </ol> * Methods that take no arguments need only specify the method name. * * The return value is encoded the same way as a single parameter (type + value) */ private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) { int l = in.getInt(); Loading @@ -327,54 +363,17 @@ public class DdmHandleViewDebug extends DdmHandle { args = new Object[0]; } else { int nArgs = in.getInt(); argTypes = new Class<?>[nArgs]; args = new Object[nArgs]; for (int i = 0; i < nArgs; i++) { char c = in.getChar(); switch (c) { case 'Z': argTypes[i] = boolean.class; args[i] = in.get() == 0 ? false : true; break; case 'B': argTypes[i] = byte.class; args[i] = in.get(); break; case 'C': argTypes[i] = char.class; args[i] = in.getChar(); break; case 'S': argTypes[i] = short.class; args[i] = in.getShort(); break; case 'I': argTypes[i] = int.class; args[i] = in.getInt(); break; case 'J': argTypes[i] = long.class; args[i] = in.getLong(); break; case 'F': argTypes[i] = float.class; args[i] = in.getFloat(); break; case 'D': argTypes[i] = double.class; args[i] = in.getDouble(); break; default: Log.e(TAG, "arg " + i + ", unrecognized type: " + c); return createFailChunk(ERR_INVALID_PARAM, "Unsupported parameter type (" + c + ") to invoke view method."); } try { deserializeMethodParameters(args, argTypes, in); } catch (ViewMethodInvocationSerializationException e) { return createFailChunk(ERR_INVALID_PARAM, e.getMessage()); } } Method method = null; Method method; try { method = targetView.getClass().getMethod(methodName, argTypes); } catch (NoSuchMethodException e) { Loading @@ -384,7 +383,10 @@ public class DdmHandleViewDebug extends DdmHandle { } try { ViewDebug.invokeViewMethod(targetView, method, args); Object result = ViewDebug.invokeViewMethod(targetView, method, args); Class<?> returnType = method.getReturnType(); byte[] returnValue = serializeReturnValue(returnType, returnType.cast(result)); return new Chunk(CHUNK_VUOP, returnValue, 0, returnValue.length); } catch (Exception e) { Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage()); String msg = e.getCause().getMessage(); Loading @@ -393,8 +395,6 @@ public class DdmHandleViewDebug extends DdmHandle { } return createFailChunk(ERR_EXCEPTION, msg); } return null; } private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) { Loading Loading @@ -431,4 +431,175 @@ public class DdmHandleViewDebug extends DdmHandle { byte[] data = b.toByteArray(); return new Chunk(CHUNK_VUOP, data, 0, data.length); } /** * Deserializes parameters according to the VUOP_INVOKE_VIEW_METHOD protocol the {@code in} * buffer. * * The length of {@code args} determines how many arguments are read. The {@code argTypes} must * be the same length, and will be set to the argument types of the data read. * * @hide */ @VisibleForTesting public static void deserializeMethodParameters( Object[] args, Class<?>[] argTypes, ByteBuffer in) throws ViewMethodInvocationSerializationException { checkArgument(args.length == argTypes.length); for (int i = 0; i < args.length; i++) { char typeSignature = in.getChar(); boolean isArray = typeSignature == SIG_ARRAY; if (isArray) { char arrayType = in.getChar(); if (arrayType != SIG_BYTE) { // This implementation only supports byte-arrays for now. throw new ViewMethodInvocationSerializationException( "Unsupported array parameter type (" + typeSignature + ") to invoke view method @argument " + i); } int arrayLength = in.getInt(); if (arrayLength > in.remaining()) { // The sender did not actually sent the specified amount of bytes. This // avoids a malformed packet to trigger an out-of-memory error. throw new BufferUnderflowException(); } byte[] byteArray = new byte[arrayLength]; in.get(byteArray); argTypes[i] = byte[].class; args[i] = byteArray; } else { switch (typeSignature) { case SIG_BOOLEAN: argTypes[i] = boolean.class; args[i] = in.get() != 0; break; case SIG_BYTE: argTypes[i] = byte.class; args[i] = in.get(); break; case SIG_CHAR: argTypes[i] = char.class; args[i] = in.getChar(); break; case SIG_SHORT: argTypes[i] = short.class; args[i] = in.getShort(); break; case SIG_INT: argTypes[i] = int.class; args[i] = in.getInt(); break; case SIG_LONG: argTypes[i] = long.class; args[i] = in.getLong(); break; case SIG_FLOAT: argTypes[i] = float.class; args[i] = in.getFloat(); break; case SIG_DOUBLE: argTypes[i] = double.class; args[i] = in.getDouble(); break; case SIG_STRING: { argTypes[i] = String.class; int stringUtf8ByteCount = Short.toUnsignedInt(in.getShort()); byte[] rawStringBuffer = new byte[stringUtf8ByteCount]; in.get(rawStringBuffer); args[i] = new String(rawStringBuffer, StandardCharsets.UTF_8); break; } default: Log.e(TAG, "arg " + i + ", unrecognized type: " + typeSignature); throw new ViewMethodInvocationSerializationException( "Unsupported parameter type (" + typeSignature + ") to invoke view method."); } } } } /** * Serializes {@code value} to the wire protocol of VUOP_INVOKE_VIEW_METHOD. * @hide */ @VisibleForTesting public static byte[] serializeReturnValue(Class<?> type, Object value) throws ViewMethodInvocationSerializationException, IOException { ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(1024); DataOutputStream dos = new DataOutputStream(byteOutStream); if (type.isArray()) { if (!type.equals(byte[].class)) { // Only byte arrays are supported currently. throw new ViewMethodInvocationSerializationException( "Unsupported array return type (" + type + ")"); } byte[] byteArray = (byte[]) value; dos.writeChar(SIG_ARRAY); dos.writeChar(SIG_BYTE); dos.writeInt(byteArray.length); dos.write(byteArray); } else if (boolean.class.equals(type)) { dos.writeChar(SIG_BOOLEAN); dos.write((boolean) value ? 1 : 0); } else if (byte.class.equals(type)) { dos.writeChar(SIG_BYTE); dos.writeByte((byte) value); } else if (char.class.equals(type)) { dos.writeChar(SIG_CHAR); dos.writeChar((char) value); } else if (short.class.equals(type)) { dos.writeChar(SIG_SHORT); dos.writeShort((short) value); } else if (int.class.equals(type)) { dos.writeChar(SIG_INT); dos.writeInt((int) value); } else if (long.class.equals(type)) { dos.writeChar(SIG_LONG); dos.writeLong((long) value); } else if (double.class.equals(type)) { dos.writeChar(SIG_DOUBLE); dos.writeDouble((double) value); } else if (float.class.equals(type)) { dos.writeChar(SIG_FLOAT); dos.writeFloat((float) value); } else if (String.class.equals(type)) { dos.writeChar(SIG_STRING); dos.writeUTF(value != null ? (String) value : ""); } else { dos.writeChar(SIG_VOID); } return byteOutStream.toByteArray(); } // Prefixes for simple primitives. These match the JNI definitions. private static final char SIG_ARRAY = '['; private static final char SIG_BOOLEAN = 'Z'; private static final char SIG_BYTE = 'B'; private static final char SIG_SHORT = 'S'; private static final char SIG_CHAR = 'C'; private static final char SIG_INT = 'I'; private static final char SIG_LONG = 'J'; private static final char SIG_FLOAT = 'F'; private static final char SIG_DOUBLE = 'D'; private static final char SIG_VOID = 'V'; // Prefixes for some commonly used objects private static final char SIG_STRING = 'R'; /** * @hide */ @VisibleForTesting public static class ViewMethodInvocationSerializationException extends Exception { ViewMethodInvocationSerializationException(String message) { super(message); } } }
core/java/android/ddm/OWNERS 0 → 100644 +1 −0 Original line number Diff line number Diff line per-file DdmHandleViewDebug.java = michschn@google.com