diff options
57 files changed, 863 insertions, 450 deletions
diff --git a/api/current.txt b/api/current.txt index f50fc3025249..a5d8a930e40f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -944,6 +944,8 @@ package android { field public static final int popupBackground = 16843126; // 0x1010176 field public static final int popupCharacters = 16843332; // 0x1010244 field public static final int popupElevation = 16843916; // 0x101048c + field public static final int popupEnterTransition = 16844065; // 0x1010521 + field public static final int popupExitTransition = 16844066; // 0x1010522 field public static final int popupKeyboard = 16843331; // 0x1010243 field public static final int popupLayout = 16843323; // 0x101023b field public static final int popupMenuStyle = 16843520; // 0x1010300 @@ -2754,14 +2756,10 @@ package android.accessibilityservice { } public final class GestureDescription { - method public static android.accessibilityservice.GestureDescription createClick(int, int); - method public static android.accessibilityservice.GestureDescription createLongClick(int, int); - method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long); - method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long); + method public static long getMaxGestureDuration(); + method public static int getMaxStrokeCount(); method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int); method public int getStrokeCount(); - field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L - field public static final int MAX_STROKE_COUNT = 10; // 0xa } public static class GestureDescription.Builder { @@ -8019,8 +8017,6 @@ package android.content { method public abstract java.lang.String getPackageResourcePath(); method public abstract android.content.res.Resources getResources(); method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int); - method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); - method public abstract java.io.File getSharedPreferencesPath(java.lang.String); method public final java.lang.String getString(int); method public final java.lang.String getString(int, java.lang.Object...); method public abstract java.lang.Object getSystemService(java.lang.String); @@ -29598,6 +29594,7 @@ package android.os.storage { method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryVolume(); method public android.os.storage.StorageVolume[] getVolumeList(); + method public boolean isEncrypted(java.io.File); method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); @@ -47197,6 +47194,8 @@ package android.widget { method public android.graphics.drawable.Drawable getBackground(); method public android.view.View getContentView(); method public float getElevation(); + method public android.transition.Transition getEnterTransition(); + method public android.transition.Transition getExitTransition(); method public int getHeight(); method public int getInputMethodMode(); method public int getMaxAvailableHeight(android.view.View); @@ -50690,6 +50689,7 @@ package java.lang { method public float floatValue(); method public static int hashCode(double); method public int intValue(); + method public static boolean isFinite(double); method public static boolean isInfinite(double); method public boolean isInfinite(); method public static boolean isNaN(double); @@ -50771,6 +50771,7 @@ package java.lang { method public float floatValue(); method public static float intBitsToFloat(int); method public int intValue(); + method public static boolean isFinite(float); method public static boolean isInfinite(float); method public boolean isInfinite(); method public static boolean isNaN(float); diff --git a/api/removed.txt b/api/removed.txt index 9b5d3ab0d81c..36c8ce5cdf50 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -22,6 +22,8 @@ package android.content { public abstract class Context { method public deprecated android.content.Context createCredentialEncryptedStorageContext(); method public deprecated android.content.Context createDeviceEncryptedStorageContext(); + method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); + method public abstract java.io.File getSharedPreferencesPath(java.lang.String); method public deprecated boolean isCredentialEncryptedStorage(); method public deprecated boolean isDeviceEncryptedStorage(); method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String); diff --git a/api/system-current.txt b/api/system-current.txt index 00603bbf864e..97b7710edec8 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1039,6 +1039,8 @@ package android { field public static final int popupBackground = 16843126; // 0x1010176 field public static final int popupCharacters = 16843332; // 0x1010244 field public static final int popupElevation = 16843916; // 0x101048c + field public static final int popupEnterTransition = 16844065; // 0x1010521 + field public static final int popupExitTransition = 16844066; // 0x1010522 field public static final int popupKeyboard = 16843331; // 0x1010243 field public static final int popupLayout = 16843323; // 0x101023b field public static final int popupMenuStyle = 16843520; // 0x1010300 @@ -2856,14 +2858,10 @@ package android.accessibilityservice { } public final class GestureDescription { - method public static android.accessibilityservice.GestureDescription createClick(int, int); - method public static android.accessibilityservice.GestureDescription createLongClick(int, int); - method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long); - method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long); + method public static long getMaxGestureDuration(); + method public static int getMaxStrokeCount(); method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int); method public int getStrokeCount(); - field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L - field public static final int MAX_STROKE_COUNT = 10; // 0xa } public static class GestureDescription.Builder { @@ -8318,8 +8316,6 @@ package android.content { method public abstract java.lang.String getPackageResourcePath(); method public abstract android.content.res.Resources getResources(); method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int); - method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); - method public abstract java.io.File getSharedPreferencesPath(java.lang.String); method public final java.lang.String getString(int); method public final java.lang.String getString(int, java.lang.Object...); method public abstract java.lang.Object getSystemService(java.lang.String); @@ -31911,6 +31907,7 @@ package android.os.storage { method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryVolume(); method public android.os.storage.StorageVolume[] getVolumeList(); + method public boolean isEncrypted(java.io.File); method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); @@ -50261,6 +50258,8 @@ package android.widget { method public android.graphics.drawable.Drawable getBackground(); method public android.view.View getContentView(); method public float getElevation(); + method public android.transition.Transition getEnterTransition(); + method public android.transition.Transition getExitTransition(); method public int getHeight(); method public int getInputMethodMode(); method public int getMaxAvailableHeight(android.view.View); @@ -53754,6 +53753,7 @@ package java.lang { method public float floatValue(); method public static int hashCode(double); method public int intValue(); + method public static boolean isFinite(double); method public static boolean isInfinite(double); method public boolean isInfinite(); method public static boolean isNaN(double); @@ -53835,6 +53835,7 @@ package java.lang { method public float floatValue(); method public static float intBitsToFloat(int); method public int intValue(); + method public static boolean isFinite(float); method public static boolean isInfinite(float); method public boolean isInfinite(); method public static boolean isNaN(float); diff --git a/api/system-removed.txt b/api/system-removed.txt index 8bf59df2d890..d48b9b3cd403 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -20,6 +20,8 @@ package android.content { public abstract class Context { method public deprecated android.content.Context createCredentialEncryptedStorageContext(); method public deprecated android.content.Context createDeviceEncryptedStorageContext(); + method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); + method public abstract java.io.File getSharedPreferencesPath(java.lang.String); method public deprecated boolean isCredentialEncryptedStorage(); method public deprecated boolean isDeviceEncryptedStorage(); method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index c4b01dbbc678..fa2669c26435 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -944,6 +944,8 @@ package android { field public static final int popupBackground = 16843126; // 0x1010176 field public static final int popupCharacters = 16843332; // 0x1010244 field public static final int popupElevation = 16843916; // 0x101048c + field public static final int popupEnterTransition = 16844065; // 0x1010521 + field public static final int popupExitTransition = 16844066; // 0x1010522 field public static final int popupKeyboard = 16843331; // 0x1010243 field public static final int popupLayout = 16843323; // 0x101023b field public static final int popupMenuStyle = 16843520; // 0x1010300 @@ -2754,14 +2756,10 @@ package android.accessibilityservice { } public final class GestureDescription { - method public static android.accessibilityservice.GestureDescription createClick(int, int); - method public static android.accessibilityservice.GestureDescription createLongClick(int, int); - method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long); - method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long); + method public static long getMaxGestureDuration(); + method public static int getMaxStrokeCount(); method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int); method public int getStrokeCount(); - field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L - field public static final int MAX_STROKE_COUNT = 10; // 0xa } public static class GestureDescription.Builder { @@ -8024,8 +8022,6 @@ package android.content { method public abstract java.lang.String getPackageResourcePath(); method public abstract android.content.res.Resources getResources(); method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int); - method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); - method public abstract java.io.File getSharedPreferencesPath(java.lang.String); method public final java.lang.String getString(int); method public final java.lang.String getString(int, java.lang.Object...); method public abstract java.lang.Object getSystemService(java.lang.String); @@ -29664,6 +29660,7 @@ package android.os.storage { method public java.lang.String getMountedObbPath(java.lang.String); method public android.os.storage.StorageVolume getPrimaryVolume(); method public android.os.storage.StorageVolume[] getVolumeList(); + method public boolean isEncrypted(java.io.File); method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); @@ -47270,6 +47267,8 @@ package android.widget { method public android.graphics.drawable.Drawable getBackground(); method public android.view.View getContentView(); method public float getElevation(); + method public android.transition.Transition getEnterTransition(); + method public android.transition.Transition getExitTransition(); method public int getHeight(); method public int getInputMethodMode(); method public int getMaxAvailableHeight(android.view.View); @@ -50763,6 +50762,7 @@ package java.lang { method public float floatValue(); method public static int hashCode(double); method public int intValue(); + method public static boolean isFinite(double); method public static boolean isInfinite(double); method public boolean isInfinite(); method public static boolean isNaN(double); @@ -50844,6 +50844,7 @@ package java.lang { method public float floatValue(); method public static float intBitsToFloat(int); method public int intValue(); + method public static boolean isFinite(float); method public static boolean isInfinite(float); method public boolean isInfinite(); method public static boolean isNaN(float); diff --git a/api/test-removed.txt b/api/test-removed.txt index 9b5d3ab0d81c..36c8ce5cdf50 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -22,6 +22,8 @@ package android.content { public abstract class Context { method public deprecated android.content.Context createCredentialEncryptedStorageContext(); method public deprecated android.content.Context createDeviceEncryptedStorageContext(); + method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); + method public abstract java.io.File getSharedPreferencesPath(java.lang.String); method public deprecated boolean isCredentialEncryptedStorage(); method public deprecated boolean isDeviceEncryptedStorage(); method public deprecated boolean migrateDatabaseFrom(android.content.Context, java.lang.String); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index ac3b8e360b2e..ddc5b0c7eb08 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -851,6 +851,7 @@ public abstract class AccessibilityService extends Service { return connection.getMagnificationScale(); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain scale", re); + re.rethrowFromSystemServer(); } } return 1.0f; @@ -879,6 +880,7 @@ public abstract class AccessibilityService extends Service { return connection.getMagnificationCenterX(); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain center X", re); + re.rethrowFromSystemServer(); } } return 0.0f; @@ -907,6 +909,7 @@ public abstract class AccessibilityService extends Service { return connection.getMagnificationCenterY(); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain center Y", re); + re.rethrowFromSystemServer(); } } return 0.0f; @@ -933,6 +936,7 @@ public abstract class AccessibilityService extends Service { return connection.getMagnifiedRegion(); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain magnified region", re); + re.rethrowFromSystemServer(); } } return Region.obtain(); @@ -961,6 +965,7 @@ public abstract class AccessibilityService extends Service { return connection.resetMagnification(animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to reset", re); + re.rethrowFromSystemServer(); } } return false; @@ -989,6 +994,7 @@ public abstract class AccessibilityService extends Service { scale, Float.NaN, Float.NaN, animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to set scale", re); + re.rethrowFromSystemServer(); } } return false; @@ -1020,6 +1026,7 @@ public abstract class AccessibilityService extends Service { Float.NaN, centerX, centerY, animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to set center", re); + re.rethrowFromSystemServer(); } } return false; @@ -1254,10 +1261,7 @@ public abstract class AccessibilityService extends Service { Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re); re.rethrowFromSystemServer(); } - } else { - throw new RuntimeException("AccessibilityServiceConnection is null"); } - return false; } @@ -1301,6 +1305,7 @@ public abstract class AccessibilityService extends Service { return connection.performGlobalAction(action); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while calling performGlobalAction", re); + re.rethrowFromSystemServer(); } } return false; @@ -1349,6 +1354,7 @@ public abstract class AccessibilityService extends Service { return connection.getServiceInfo(); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); + re.rethrowFromSystemServer(); } } return null; @@ -1382,6 +1388,7 @@ public abstract class AccessibilityService extends Service { AccessibilityInteractionClient.getInstance().clearCache(); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); + re.rethrowFromSystemServer(); } } } diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java index 14aabcf69a6c..7a0c89b75a4a 100644 --- a/core/java/android/accessibilityservice/GestureDescription.java +++ b/core/java/android/accessibilityservice/GestureDescription.java @@ -43,135 +43,32 @@ import java.util.List; */ public final class GestureDescription { /** Gestures may contain no more than this many strokes */ - public static final int MAX_STROKE_COUNT = 10; + private static final int MAX_STROKE_COUNT = 10; /** * Upper bound on total gesture duration. Nearly all gestures will be much shorter. */ - public static final long MAX_GESTURE_DURATION_MS = 60 * 1000; + private static final long MAX_GESTURE_DURATION_MS = 60 * 1000; private final List<StrokeDescription> mStrokes = new ArrayList<>(); private final float[] mTempPos = new float[2]; /** - * Create a description of a click gesture + * Get the upper limit for the number of strokes a gesture may contain. * - * @param x The x coordinate to click. Must not be negative. - * @param y The y coordinate to click. Must not be negative. - * - * @return A description of a click at (x, y) + * @return The maximum number of strokes. */ - public static GestureDescription createClick(@IntRange(from = 0) int x, - @IntRange(from = 0) int y) { - Path clickPath = new Path(); - clickPath.moveTo(x, y); - clickPath.lineTo(x + 1, y); - return new GestureDescription( - new StrokeDescription(clickPath, 0, ViewConfiguration.getTapTimeout())); + public static int getMaxStrokeCount() { + return MAX_STROKE_COUNT; } /** - * Create a description of a long click gesture - * - * @param x The x coordinate to click. Must not be negative. - * @param y The y coordinate to click. Must not be negative. + * Get the upper limit on a gesture's duration. * - * @return A description of a click at (x, y) + * @return The maximum duration in milliseconds. */ - public static GestureDescription createLongClick(@IntRange(from = 0) int x, - @IntRange(from = 0) int y) { - Path clickPath = new Path(); - clickPath.moveTo(x, y); - clickPath.lineTo(x + 1, y); - int longPressTime = ViewConfiguration.getLongPressTimeout(); - return new GestureDescription( - new StrokeDescription(clickPath, 0, longPressTime + (longPressTime / 2))); - } - - /** - * Create a description of a swipe gesture - * - * @param startX The x coordinate of the starting point. Must not be negative. - * @param startY The y coordinate of the starting point. Must not be negative. - * @param endX The x coordinate of the ending point. Must not be negative. - * @param endY The y coordinate of the ending point. Must not be negative. - * @param duration The time, in milliseconds, to complete the gesture. Must not be negative. - * - * @return A description of a swipe from ({@code startX}, {@code startY}) to - * ({@code endX}, {@code endY}) that takes {@code duration} milliseconds. Returns {@code null} - * if the path specified for the swipe is invalid. - */ - public static GestureDescription createSwipe(@IntRange(from = 0) int startX, - @IntRange(from = 0) int startY, - @IntRange(from = 0) int endX, - @IntRange(from = 0) int endY, - @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) { - Path swipePath = new Path(); - swipePath.moveTo(startX, startY); - swipePath.lineTo(endX, endY); - return new GestureDescription(new StrokeDescription(swipePath, 0, duration)); - } - - /** - * Create a description for a pinch (or zoom) gesture. - * - * @param centerX The x coordinate of the center of the pinch. Must not be negative. - * @param centerY The y coordinate of the center of the pinch. Must not be negative. - * @param startSpacing The spacing of the touch points at the beginning of the gesture. Must not - * be negative. - * @param endSpacing The spacing of the touch points at the end of the gesture. Must not be - * negative. - * @param orientation The angle, in degrees, of the gesture. 0 represents a horizontal pinch - * @param duration The time, in milliseconds, to complete the gesture. Must not be negative. - * - * @return A description of a pinch centered at ({code centerX}, {@code centerY}) that starts - * with the touch points spaced by {@code startSpacing} and ends with them spaced by - * {@code endSpacing} that lasts {@code duration} ms. Returns {@code null} if either path - * specified for the pinch is invalid. - */ - public static GestureDescription createPinch(@IntRange(from = 0) int centerX, - @IntRange(from = 0) int centerY, - @IntRange(from = 0) int startSpacing, - @IntRange(from = 0) int endSpacing, - float orientation, - @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) { - if ((startSpacing < 0) || (endSpacing < 0)) { - throw new IllegalArgumentException("Pinch spacing cannot be negative"); - } - float[] startPoint1 = new float[2]; - float[] endPoint1 = new float[2]; - float[] startPoint2 = new float[2]; - float[] endPoint2 = new float[2]; - - /* Build points for a horizontal gesture centered at the origin */ - startPoint1[0] = startSpacing / 2; - startPoint1[1] = 0; - endPoint1[0] = endSpacing / 2; - endPoint1[1] = 0; - startPoint2[0] = -startSpacing / 2; - startPoint2[1] = 0; - endPoint2[0] = -endSpacing / 2; - endPoint2[1] = 0; - - /* Rotate and translate the points */ - Matrix matrix = new Matrix(); - matrix.setRotate(orientation); - matrix.postTranslate(centerX, centerY); - matrix.mapPoints(startPoint1); - matrix.mapPoints(endPoint1); - matrix.mapPoints(startPoint2); - matrix.mapPoints(endPoint2); - - Path path1 = new Path(); - path1.moveTo(startPoint1[0], startPoint1[1]); - path1.lineTo(endPoint1[0], endPoint1[1]); - Path path2 = new Path(); - path2.moveTo(startPoint2[0], startPoint2[1]); - path2.lineTo(endPoint2[0], endPoint2[1]); - - return new GestureDescription(Arrays.asList( - new StrokeDescription(path1, 0, duration), - new StrokeDescription(path2, 0, duration))); + public static long getMaxGestureDuration() { + return MAX_GESTURE_DURATION_MS; } private GestureDescription() {} @@ -180,10 +77,6 @@ public final class GestureDescription { mStrokes.addAll(strokes); } - private GestureDescription(StrokeDescription stroke) { - mStrokes.add(stroke); - } - /** * Get the number of stroke in the gesture. * @@ -278,21 +171,23 @@ public final class GestureDescription { */ public Builder addStroke(@NonNull StrokeDescription strokeDescription) { if (mStrokes.size() >= MAX_STROKE_COUNT) { - throw new RuntimeException("Attempting to add too many strokes to a gesture"); + throw new IllegalStateException( + "Attempting to add too many strokes to a gesture"); } mStrokes.add(strokeDescription); if (getTotalDuration(mStrokes) > MAX_GESTURE_DURATION_MS) { mStrokes.remove(strokeDescription); - throw new RuntimeException("Gesture would exceed maximum duration with new stroke"); + throw new IllegalStateException( + "Gesture would exceed maximum duration with new stroke"); } return this; } public GestureDescription build() { if (mStrokes.size() == 0) { - throw new RuntimeException("Gestures must have at least one stroke"); + throw new IllegalStateException("Gestures must have at least one stroke"); } return new GestureDescription(mStrokes); } @@ -317,8 +212,8 @@ public final class GestureDescription { * Must not be negative. */ public StrokeDescription(@NonNull Path path, - @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long startTime, - @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) { + @IntRange(from = 0) long startTime, + @IntRange(from = 0) long duration) { if (duration <= 0) { throw new IllegalArgumentException("Duration must be positive"); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 33fd1dbda63d..13e8e75a4db7 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.IntDef; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; @@ -49,6 +50,8 @@ import android.view.Window; import com.android.internal.content.ReferrerIntent; import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -78,7 +81,15 @@ public class Instrumentation { public static final String REPORT_KEY_STREAMRESULT = "stream"; private static final String TAG = "Instrumentation"; - + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES}) + public @interface UiAutomationFlags {}; + + private final Object mSync = new Object(); private ActivityThread mThread = null; private MessageQueue mMessageQueue = null; @@ -1876,7 +1887,7 @@ public class Instrumentation { * * @see UiAutomation */ - public UiAutomation getUiAutomation(int flags) { + public UiAutomation getUiAutomation(@UiAutomationFlags int flags) { if (mUiAutomationConnection != null) { if ((mUiAutomation == null) || (mUiAutomation.isDestroyed())) { mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(), diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ca19b9bc668e..15cc17da2150 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -691,6 +691,7 @@ public abstract class Context { * * @see #getSharedPreferencesPath(String) * @see #MODE_PRIVATE + * @removed */ public abstract SharedPreferences getSharedPreferences(File file, int mode); @@ -813,17 +814,16 @@ public abstract class Context { * to get a path. * @return An absolute path to the given file. * @see #getSharedPreferences(String, int) + * @removed */ public abstract File getSharedPreferencesPath(String name); /** * Returns the absolute path to the directory on the filesystem where all - * private files belonging to this app are stored. This is the top-level - * directory under which {@link #getFilesDir()}, {@link #getCacheDir()}, etc - * are contained. Apps should <em>not</em> create any files or directories - * as direct children of this directory, since it's a reserved namespace - * belonging to the platform. Instead, use {@link #getDir(String, int)} or - * other storage APIs. + * private files belonging to this app are stored. Apps should not use this + * path directly; they should instead use {@link #getFilesDir()}, + * {@link #getCacheDir()}, {@link #getDir(String, int)}, or other storage + * APIs on this class. * <p> * The returned path may change over time if the calling app is moved to an * adopted storage device, so only relative paths should be persisted. @@ -831,7 +831,7 @@ public abstract class Context { * No additional permissions are required for the calling app to read or * write files under the returned path. * - * @see #getDir(String, int) + * @see ApplicationInfo#dataDir */ public abstract File getDataDir(); @@ -4168,7 +4168,7 @@ public abstract class Context { * Return a new Context object for the current Context but whose storage * APIs are backed by device-protected storage. * <p> - * When a device is encrypted, data stored in this location is encrypted + * On devices with direct boot, data stored in this location is encrypted * with a key tied to the physical device, and it can be accessed * immediately after the device has booted successfully, both * <em>before and after</em> the user has authenticated with their @@ -4206,7 +4206,7 @@ public abstract class Context { * storage area for apps unless * {@link android.R.attr#defaultToDeviceProtectedStorage} was requested. * <p> - * When a device is encrypted, data stored in this location is encrypted + * On devices with direct boot, data stored in this location is encrypted * with a key tied to user credentials, which can be accessed * <em>only after</em> the user has entered their credentials (such as a * lock pattern or PIN). diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 09aad3b66428..57ab7a2f937c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -23,12 +23,14 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; @@ -1946,31 +1948,48 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_ALARM_CHANGED = "android.intent.action.ALARM_CHANGED"; /** - * Broadcast Action: This is broadcast once, after the system has finished - * booting and the user is in a "locked" state. A user is locked when their - * credential-encrypted private app data storage is unavailable. Once the - * user has entered their credentials (such as a lock pattern or PIN) for - * the first time, the {@link #ACTION_BOOT_COMPLETED} broadcast will be - * sent. + * Broadcast Action: This is broadcast once, after the user has finished + * booting, but while still in the "locked" state. It can be used to perform + * application-specific initialization, such as installing alarms. You must + * hold the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} + * permission in order to receive this broadcast. * <p> - * You must hold the - * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission in - * order to receive this broadcast. + * This broadcast is sent immediately at boot by all devices (regardless of + * direct boot support) running {@link android.os.Build.VERSION_CODES#N} or + * higher. Upon receipt of this broadcast, the user is still locked and only + * device-protected storage can be accessed safely. If you want to access + * credential-protected storage, you need to wait for the user to be + * unlocked (typically by entering their lock pattern or PIN for the first + * time), after which the {@link #ACTION_USER_UNLOCKED} and + * {@link #ACTION_BOOT_COMPLETED} broadcasts are sent. + * <p> + * To receive this broadcast, your receiver component must be marked as + * being {@link ComponentInfo#directBootAware}. * <p class="note"> * This is a protected intent that can only be sent by the system. + * + * @see Context#createDeviceProtectedStorageContext() */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED"; /** - * Broadcast Action: This is broadcast once, after the system has finished - * booting. It can be used to perform application-specific initialization, - * such as installing alarms. You must hold the - * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission - * in order to receive this broadcast. - * - * <p class="note">This is a protected intent that can only be sent - * by the system. + * Broadcast Action: This is broadcast once, after the user has finished + * booting. It can be used to perform application-specific initialization, + * such as installing alarms. You must hold the + * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission in + * order to receive this broadcast. + * <p> + * This broadcast is sent at boot by all devices (both with and without + * direct boot support). Upon receipt of this broadcast, the user is + * unlocked and both device-protected and credential-protected storage can + * accessed safely. + * <p> + * If you need to run while the user is still locked (before they've entered + * their lock pattern or PIN for the first time), you can listen for the + * {@link #ACTION_LOCKED_BOOT_COMPLETED} broadcast. + * <p class="note"> + * This is a protected intent that can only be sent by the system. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED"; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 1079f974732f..e89cbd7091ae 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2593,12 +2593,15 @@ public abstract class PackageManager { public abstract Intent getLeanbackLaunchIntentForPackage(String packageName); /** - * Return an array of all of the secondary group-ids that have been assigned - * to a package. + * Return an array of all of the POSIX secondary group IDs that have been + * assigned to the given package. + * <p> + * Note that the same package may have different GIDs under different + * {@link UserHandle} on the same device. * * @param packageName The full name (i.e. com.google.apps.contacts) of the - * desired package. - * @return Returns an int array of the assigned gids, or null if there are + * desired package. + * @return Returns an int array of the assigned GIDs, or null if there are * none. * @throws NameNotFoundException if a package with the given name cannot be * found on the system. @@ -2607,8 +2610,11 @@ public abstract class PackageManager { throws NameNotFoundException; /** - * Return an array of all of the secondary group-ids that have been assigned - * to a package. + * Return an array of all of the POSIX secondary group IDs that have been + * assigned to the given package. + * <p> + * Note that the same package may have different GIDs under different + * {@link UserHandle} on the same device. * * @param packageName The full name (i.e. com.google.apps.contacts) of the * desired package. @@ -2622,6 +2628,9 @@ public abstract class PackageManager { /** * Return the UID associated with the given package name. + * <p> + * Note that the same package will have different UIDs under different + * {@link UserHandle} on the same device. * * @param packageName The full name (i.e. com.google.apps.contacts) of the * desired package. @@ -2634,6 +2643,9 @@ public abstract class PackageManager { /** * Return the UID associated with the given package name. + * <p> + * Note that the same package will have different UIDs under different + * {@link UserHandle} on the same device. * * @param packageName The full name (i.e. com.google.apps.contacts) of the * desired package. @@ -2648,6 +2660,9 @@ public abstract class PackageManager { /** * Return the UID associated with the given package name. + * <p> + * Note that the same package will have different UIDs under different + * {@link UserHandle} on the same device. * * @param packageName The full name (i.e. com.google.apps.contacts) of the * desired package. diff --git a/core/java/android/os/DeadSystemException.java b/core/java/android/os/DeadSystemException.java index 595365c55472..8fb53e2f4eb4 100644 --- a/core/java/android/os/DeadSystemException.java +++ b/core/java/android/os/DeadSystemException.java @@ -18,7 +18,7 @@ package android.os; /** * The core Android system has died and is going through a runtime restart. All - * running apps will be promptly be killed. + * running apps will be promptly killed. */ public class DeadSystemException extends DeadObjectException { public DeadSystemException() { diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 307bd2d13f83..f6e8940db408 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -34,6 +34,7 @@ public class Environment { private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE"; private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT"; private static final String ENV_ANDROID_DATA = "ANDROID_DATA"; + private static final String ENV_ANDROID_EXPAND = "ANDROID_EXPAND"; private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE"; private static final String ENV_DOWNLOAD_CACHE = "DOWNLOAD_CACHE"; private static final String ENV_OEM_ROOT = "OEM_ROOT"; @@ -54,6 +55,7 @@ public class Environment { private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system"); private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data"); + private static final File DIR_ANDROID_EXPAND = getDirectory(ENV_ANDROID_EXPAND, "/mnt/expand"); private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage"); private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache"); private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem"); @@ -229,6 +231,11 @@ public class Environment { } /** {@hide} */ + public static File getExpandDirectory() { + return DIR_ANDROID_EXPAND; + } + + /** {@hide} */ public static File getDataSystemDirectory() { return new File(getDataDirectory(), "system"); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 92e18622269b..0ff0154f2ed0 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -918,11 +918,15 @@ public class UserManager { } /** - * Return whether the calling user is running in an "unlocked" state. A user - * is unlocked only after they've entered their credentials (such as a lock - * pattern or PIN), and credential-encrypted private app data storage is - * available. When a user is locked, only device-protected data storage is - * available. + * Return whether the calling user is running in an "unlocked" state. + * <p> + * On devices with direct boot, a user is unlocked only after they've + * entered their credentials (such as a lock pattern or PIN). On devices + * without direct boot, a user is unlocked as soon as it starts. + * <p> + * When a user is locked, only device-protected data storage is available. + * When a user is unlocked, both device-protected and credential-protected + * private app data storage is available. * * @see Intent#ACTION_USER_UNLOCKED * @see Context#createDeviceProtectedStorageContext() @@ -932,11 +936,15 @@ public class UserManager { } /** - * Return whether the given user is running in an "unlocked" state. A user - * is unlocked only after they've entered their credentials (such as a lock - * pattern or PIN), and credential-protected private app data storage is - * available. When a user is locked, only device-protected data storage is - * available. + * Return whether the given user is running in an "unlocked" state. + * <p> + * On devices with direct boot, a user is unlocked only after they've + * entered their credentials (such as a lock pattern or PIN). On devices + * without direct boot, a user is unlocked as soon as it starts. + * <p> + * When a user is locked, only device-protected data storage is available. + * When a user is unlocked, both device-protected and credential-protected + * private app data storage is available. * * @param user to retrieve the unlocked state for. * @see Intent#ACTION_USER_UNLOCKED diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 61e6b95cf89f..22aec63a76a2 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -35,6 +35,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.Settings; +import android.security.KeyStore; import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -1025,6 +1026,20 @@ public class StorageManager { } } + /** + * Return if data stored at the given path will be encrypted while at rest. + * This can help apps avoid the overhead of double-encrypting data. + */ + public boolean isEncrypted(File file) { + if (FileUtils.contains(Environment.getDataDirectory(), file)) { + return isEncrypted(); + } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) { + return true; + } + // TODO: extend to support shared storage + return false; + } + /** {@hide} * Is this device encryptable or already encrypted? * @return true for encryptable or encrypted diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java index c36680cdb210..47dc6c367ebc 100644 --- a/core/java/android/preference/PreferenceManager.java +++ b/core/java/android/preference/PreferenceManager.java @@ -362,8 +362,7 @@ public class PreferenceManager { * Explicitly set the storage location used internally by this class to be * device-protected storage. * <p> - * <p> - * When a device is encrypted, data stored in this location is encrypted + * On devices with direct boot, data stored in this location is encrypted * with a key tied to the physical device, and it can be accessed * immediately after the device has booted successfully, both * <em>before and after</em> the user has authenticated with their @@ -392,7 +391,7 @@ public class PreferenceManager { * credential-protected storage. This is the default storage area for apps * unless {@code forceDeviceProtectedStorage} was requested. * <p> - * When a device is encrypted, data stored in this location is encrypted + * On devices with direct boot, data stored in this location is encrypted * with a key tied to user credentials, which can be accessed * <em>only after</em> the user has entered their credentials (such as a * lock pattern or PIN). diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl index 5697dfc0188c..9434f0ccac4e 100644 --- a/core/java/android/webkit/IWebViewUpdateService.aidl +++ b/core/java/android/webkit/IWebViewUpdateService.aidl @@ -54,6 +54,11 @@ interface IWebViewUpdateService { WebViewProviderInfo[] getValidWebViewPackages(); /** + * Fetch all packages that could potentially implement WebView. + */ + WebViewProviderInfo[] getAllWebViewPackages(); + + /** * Used by DevelopmentSetting to get the name of the WebView provider currently in use. */ String getCurrentWebViewPackageName(); diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index c280b81f73a3..f1bf890b3c86 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -128,98 +128,9 @@ public final class WebViewFactory { public MissingWebViewPackageException(Exception e) { super(e); } } - private static String TAG_START = "webviewproviders"; - private static String TAG_WEBVIEW_PROVIDER = "webviewprovider"; - private static String TAG_PACKAGE_NAME = "packageName"; - private static String TAG_DESCRIPTION = "description"; - // Whether or not the provider must be explicitly chosen by the user to be used. - private static String TAG_AVAILABILITY = "availableByDefault"; - private static String TAG_SIGNATURE = "signature"; - private static String TAG_FALLBACK = "isFallback"; - - /** - * Reads all signatures at the current depth (within the current provider) from the XML parser. - */ - private static String[] readSignatures(XmlResourceParser parser) throws IOException, - XmlPullParserException { - List<String> signatures = new ArrayList<String>(); - int outerDepth = parser.getDepth(); - while(XmlUtils.nextElementWithin(parser, outerDepth)) { - if (parser.getName().equals(TAG_SIGNATURE)) { - // Parse the value within the signature tag - String signature = parser.nextText(); - signatures.add(signature); - } else { - Log.e(LOGTAG, "Found an element in a webview provider that is not a signature"); - } - } - return signatures.toArray(new String[signatures.size()]); - } - - /** - * Returns all packages declared in the framework resources as potential WebView providers. - * @hide - * */ - public static WebViewProviderInfo[] getWebViewPackages() { - int numFallbackPackages = 0; - XmlResourceParser parser = null; - List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>(); - try { - parser = AppGlobals.getInitialApplication().getResources().getXml( - com.android.internal.R.xml.config_webview_packages); - XmlUtils.beginDocument(parser, TAG_START); - while(true) { - XmlUtils.nextElement(parser); - String element = parser.getName(); - if (element == null) { - break; - } - if (element.equals(TAG_WEBVIEW_PROVIDER)) { - String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME); - if (packageName == null) { - throw new MissingWebViewPackageException( - "WebView provider in framework resources missing package name"); - } - String description = parser.getAttributeValue(null, TAG_DESCRIPTION); - if (description == null) { - throw new MissingWebViewPackageException( - "WebView provider in framework resources missing description"); - } - boolean availableByDefault = "true".equals( - parser.getAttributeValue(null, TAG_AVAILABILITY)); - boolean isFallback = "true".equals( - parser.getAttributeValue(null, TAG_FALLBACK)); - WebViewProviderInfo currentProvider = - new WebViewProviderInfo(packageName, description, availableByDefault, - isFallback, readSignatures(parser)); - if (currentProvider.isFallbackPackage()) { - numFallbackPackages++; - if (numFallbackPackages > 1) { - throw new AndroidRuntimeException( - "There can be at most one webview fallback package."); - } - } - webViewProviders.add(currentProvider); - } - else { - Log.e(LOGTAG, "Found an element that is not a webview provider"); - } - } - } catch(XmlPullParserException e) { - throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e); - } catch(IOException e) { - throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e); - } finally { - if (parser != null) parser.close(); - } - return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]); - } - - // TODO (gsennton) remove when committing webview xts test change public static String getWebViewPackageName() { - WebViewProviderInfo[] providers = getWebViewPackages(); - return providers[0].packageName; + return null; } /** diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java index 64c2caa58fd5..75ccf355ecd7 100644 --- a/core/java/android/webkit/WebViewProviderInfo.java +++ b/core/java/android/webkit/WebViewProviderInfo.java @@ -150,6 +150,8 @@ public class WebViewProviderInfo implements Parcelable { private WebViewProviderInfo(Parcel in) { packageName = in.readString(); description = in.readString(); + availableByDefault = (in.readInt() > 0); + isFallback = (in.readInt() > 0); signatures = in.createStringArray(); packageInfo = null; } @@ -163,6 +165,8 @@ public class WebViewProviderInfo implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(packageName); out.writeString(description); + out.writeInt(availableByDefault ? 1 : 0); + out.writeInt(isFallback ? 1 : 0); out.writeStringArray(signatures); } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index e0ef86c26b2b..9e8f778f1819 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -869,10 +869,10 @@ public class LinearLayout extends ViewGroup { // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds. If we skipped // measurement on any children, we need to measure them now. - final int delta = heightSize - mTotalLength + int remainingExcess = heightSize - mTotalLength + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace); - if (skippedMeasure || delta != 0 && totalWeight > 0.0f) { - final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; + if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) { + float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; mTotalLength = 0; @@ -883,9 +883,12 @@ public class LinearLayout extends ViewGroup { } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - final float childExtra = lp.weight; - if (childExtra > 0) { - final int share = (int) (childExtra * delta / weightSum); + final float childWeight = lp.weight; + if (childWeight > 0) { + final int share = (int) (childWeight * remainingExcess / remainingWeightSum); + remainingExcess -= share; + remainingWeightSum -= childWeight; + final int childHeight; if (lp.height == 0 && (!mAllowInconsistentMeasurement || heightMode == MeasureSpec.EXACTLY)) { @@ -1244,10 +1247,10 @@ public class LinearLayout extends ViewGroup { // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds. If we skipped // measurement on any children, we need to measure them now. - final int delta = widthSize - mTotalLength + int remainingExcess = widthSize - mTotalLength + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace); - if (skippedMeasure || delta != 0 && totalWeight > 0.0f) { - final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; + if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) { + float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; @@ -1262,9 +1265,12 @@ public class LinearLayout extends ViewGroup { } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - final float childExtra = lp.weight; - if (childExtra > 0) { - final int share = (int) (childExtra * delta / weightSum); + final float childWeight = lp.weight; + if (childWeight > 0) { + final int share = (int) (childWeight * remainingExcess / remainingWeightSum); + remainingExcess -= share; + remainingWeightSum -= childWeight; + final int childWidth; if (lp.width == 0 && (!mAllowInconsistentMeasurement || widthMode == MeasureSpec.EXACTLY)) { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index dcadb6af7320..36e0c77613aa 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -879,12 +879,13 @@ public class ListPopupWindow implements ShowableListMenu { /** * Filter key down events. By forwarding key down events to this function, * views using non-modal ListPopupWindow can have it handle key selection of items. - * + * * @param keyCode keyCode param passed to the host view's onKeyDown * @param event event param passed to the host view's onKeyDown * @return true if the event was handled, false if it was ignored. - * + * * @see #setModal(boolean) + * @see #onKeyUp(int, KeyEvent) */ public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { // when the drop down is shown, we drive it directly @@ -972,14 +973,15 @@ public class ListPopupWindow implements ShowableListMenu { } /** - * Filter key down events. By forwarding key up events to this function, + * Filter key up events. By forwarding key up events to this function, * views using non-modal ListPopupWindow can have it handle key selection of items. - * + * * @param keyCode keyCode param passed to the host view's onKeyUp * @param event event param passed to the host view's onKeyUp * @return true if the event was handled, false if it was ignored. - * + * * @see #setModal(boolean) + * @see #onKeyDown(int, KeyEvent) */ public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) { @@ -998,11 +1000,11 @@ public class ListPopupWindow implements ShowableListMenu { * Filter pre-IME key events. By forwarding {@link View#onKeyPreIme(int, KeyEvent)} * events to this function, views using ListPopupWindow can have it dismiss the popup * when the back key is pressed. - * + * * @param keyCode keyCode param passed to the host view's onKeyPreIme * @param event event param passed to the host view's onKeyPreIme * @return true if the event was handled, false if it was ignored. - * + * * @see #setModal(boolean) */ public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index dd6a41f9d209..a1417f0cbb68 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -16,12 +16,10 @@ package android.widget; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; - import com.android.internal.R; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.TypedArray; import android.graphics.PixelFormat; @@ -54,10 +52,46 @@ import android.view.WindowManager.LayoutParams; import java.lang.ref.WeakReference; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; + /** - * <p>A popup window that can be used to display an arbitrary view. The popup - * window is a floating container that appears on top of the current - * activity.</p> + * <p> + * This class represents a popup window that can be used to display an + * arbitrary view. The popup window is a floating container that appears on top + * of the current activity. + * </p> + * <a name="Animation"></a> + * <h3>Animation</h3> + * <p> + * On all versions of Android, popup window enter and exit animations may be + * specified by calling {@link #setAnimationStyle(int)} and passing the + * resource ID for an animation style that defines {@code windowEnterAnimation} + * and {@code windowExitAnimation}. For example, passing + * {@link android.R.style#Animation_Dialog} will give a scale and alpha + * animation. + * </br> + * A window animation style may also be specified in the popup window's style + * XML via the {@link android.R.styleable#PopupWindow_popupAnimationStyle popupAnimationStyle} + * attribute. + * </p> + * <p> + * Starting with API 23, more complex popup window enter and exit transitions + * may be specified by calling either {@link #setEnterTransition(Transition)} + * or {@link #setExitTransition(Transition)} and passing a {@link Transition}. + * </br> + * Popup enter and exit transitions may also be specified in the popup window's + * style XML via the {@link android.R.styleable#PopupWindow_popupEnterTransition popupEnterTransition} + * and {@link android.R.styleable#PopupWindow_popupExitTransition popupExitTransition} + * attributes, respectively. + * </p> + * + * @attr ref android.R.styleable#PopupWindow_overlapAnchor + * @attr ref android.R.styleable#PopupWindow_popupAnimationStyle + * @attr ref android.R.styleable#PopupWindow_popupBackground + * @attr ref android.R.styleable#PopupWindow_popupElevation + * @attr ref android.R.styleable#PopupWindow_popupEnterTransition + * @attr ref android.R.styleable#PopupWindow_popupExitTransition * * @see android.widget.AutoCompleteTextView * @see android.widget.Spinner @@ -351,15 +385,54 @@ public class PopupWindow { setFocusable(focusable); } - public void setEnterTransition(Transition enterTransition) { + /** + * Sets the enter transition to be used when the popup window is shown. + * + * @param enterTransition the enter transition, or {@code null} to clear + * @see #getEnterTransition() + * @attr ref android.R.styleable#PopupWindow_popupEnterTransition + */ + public void setEnterTransition(@Nullable Transition enterTransition) { mEnterTransition = enterTransition; } - public void setExitTransition(Transition exitTransition) { + /** + * Returns the enter transition to be used when the popup window is shown. + * + * @return the enter transition, or {@code null} if not set + * @see #setEnterTransition(Transition) + * @attr ref android.R.styleable#PopupWindow_popupEnterTransition + */ + @Nullable + public Transition getEnterTransition() { + return mEnterTransition; + } + + /** + * Sets the exit transition to be used when the popup window is dismissed. + * + * @param exitTransition the exit transition, or {@code null} to clear + * @see #getExitTransition() + * @attr ref android.R.styleable#PopupWindow_popupExitTransition + */ + public void setExitTransition(@Nullable Transition exitTransition) { mExitTransition = exitTransition; } /** + * Returns the exit transition to be used when the popup window is + * dismissed. + * + * @return the exit transition, or {@code null} if not set + * @see #setExitTransition(Transition) + * @attr ref android.R.styleable#PopupWindow_popupExitTransition + */ + @Nullable + public Transition getExitTransition() { + return mExitTransition; + } + + /** * Sets the bounds used as the epicenter of the enter and exit transitions. * <p> * Transitions use a point or Rect, referred to as the epicenter, to orient diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 9897b125dea3..80e1db0527a5 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -128,8 +128,8 @@ public class ResolverActivity extends Activity { com.android.internal.R.string.whichSendApplication, com.android.internal.R.string.whichSendApplicationNamed), SENDTO(Intent.ACTION_SENDTO, - com.android.internal.R.string.whichSendApplication, - com.android.internal.R.string.whichSendApplicationNamed), + com.android.internal.R.string.whichSendToApplication, + com.android.internal.R.string.whichSendToApplicationNamed), SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE, com.android.internal.R.string.whichSendApplication, com.android.internal.R.string.whichSendApplicationNamed), diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 919254a36a76..66cc97598ae8 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -149,6 +149,12 @@ public final class Zygote { native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities); + /** + * Zygote unmount storage space on initializing. + * This method is called once. + */ + native protected static void nativeUnmountStorageOnInit(); + private static void callPostForkChildHooks(int debugFlags, boolean isSystemServer, String instructionSet) { VM_HOOKS.postForkChild(debugFlags, isSystemServer, instructionSet); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 3abea26ab42b..b658f87a9689 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -643,6 +643,9 @@ public class ZygoteInit { // Zygote. Trace.setTracingEnabled(false); + // Zygote process unmounts root storage spaces. + Zygote.nativeUnmountStorageOnInit(); + if (startSystemServer) { startSystemServer(abiList, socketName); } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index ce03bb81c08b..86820306d634 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -308,9 +308,6 @@ public final class FloatingToolbar { private static final int MIN_OVERFLOW_SIZE = 2; private static final int MAX_OVERFLOW_SIZE = 4; - /* The duration of the overflow button vector animation duration. */ - private static final int OVERFLOW_BUTTON_ANIMATION_DELAY = 400; - private final Context mContext; private final View mParent; // Parent for the popup window. private final PopupWindow mPopupWindow; @@ -377,18 +374,6 @@ public final class FloatingToolbar { } }; - /* Runnable to reset the overflow button's drawable after an overflow transition. */ - private final Runnable mResetOverflowButtonDrawable = new Runnable() { - @Override - public void run() { - if (mIsOverflowOpen) { - mOverflowButton.setImageDrawable(mArrow); - } else { - mOverflowButton.setImageDrawable(mOverflow); - } - } - }; - private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing. private boolean mHidden; // tracks whether this popup is hidden or hiding. @@ -902,7 +887,9 @@ public final class FloatingToolbar { final Size containerSize = mOverflowPanelSize; setSize(mContentContainer, containerSize); mMainPanel.setAlpha(0); + mMainPanel.setVisibility(View.INVISIBLE); mOverflowPanel.setAlpha(1); + mOverflowPanel.setVisibility(View.VISIBLE); mOverflowButton.setImageDrawable(mArrow); // Update x-coordinates depending on RTL state. @@ -941,7 +928,9 @@ public final class FloatingToolbar { final Size containerSize = mMainPanelSize; setSize(mContentContainer, containerSize); mMainPanel.setAlpha(1); + mMainPanel.setVisibility(View.VISIBLE); mOverflowPanel.setAlpha(0); + mOverflowPanel.setVisibility(View.INVISIBLE); mOverflowButton.setImageDrawable(mOverflow); if (hasOverflow()) { @@ -1327,8 +1316,6 @@ public final class FloatingToolbar { mToArrow.start(); openOverflow(); } - overflowButton.postDelayed( - mResetOverflowButtonDrawable, OVERFLOW_BUTTON_ANIMATION_DELAY); } }); return overflowButton; @@ -1389,6 +1376,10 @@ public final class FloatingToolbar { // Disable the overflow button while it's animating. // It will be re-enabled when the animation stops. mOverflowButton.setEnabled(false); + // Ensure both panels have visibility turned on when the overflow animation + // starts. + mMainPanel.setVisibility(View.VISIBLE); + mOverflowPanel.setVisibility(View.VISIBLE); } @Override diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 612f4dfd1505..3f4b2a61321b 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -302,9 +302,6 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, return false; } - // Unmount storage provided by root namespace and mount requested view - UnmountTree("/storage"); - String8 storageSource; if (mount_mode == MOUNT_EXTERNAL_DEFAULT) { storageSource = "/mnt/runtime/default"; @@ -667,12 +664,24 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( return pid; } +static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) { + // Zygote process unmount root storage space initially before every child processes are forked. + // Every forked child processes (include SystemServer) only mount their own root storage space + // And no need unmount storage operation in MountEmulatedStorage method. + // Zygote process does not utilize root storage spaces and unshared its mount namespace from the ART. + + UnmountTree("/storage"); + return; +} + static const JNINativeMethod gMethods[] = { { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I", (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize }, { "nativeForkSystemServer", "(II[II[[IJJ)I", - (void *) com_android_internal_os_Zygote_nativeForkSystemServer } + (void *) com_android_internal_os_Zygote_nativeForkSystemServer }, + { "nativeUnmountStorageOnInit", "()V", + (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit } }; int register_com_android_internal_os_Zygote(JNIEnv* env) { diff --git a/core/res/res/anim/progress_indeterminate_rotation_material.xml b/core/res/res/anim/progress_indeterminate_rotation_material.xml index 5d3ba22a29a7..6e12105615ec 100644 --- a/core/res/res/anim/progress_indeterminate_rotation_material.xml +++ b/core/res/res/anim/progress_indeterminate_rotation_material.xml @@ -16,7 +16,7 @@ --> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" - android:duration="6665" + android:duration="4444" android:interpolator="@android:anim/linear_interpolator" android:propertyName="rotation" android:repeatCount="-1" diff --git a/core/res/res/drawable/vector_drawable_progress_bar_large.xml b/core/res/res/drawable/vector_drawable_progress_bar_large.xml index cd678f1c9d81..6448f3ba9c29 100644 --- a/core/res/res/drawable/vector_drawable_progress_bar_large.xml +++ b/core/res/res/drawable/vector_drawable_progress_bar_large.xml @@ -16,22 +16,22 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="76dp" android:width="76dp" - android:viewportHeight="48" - android:viewportWidth="48" + android:viewportHeight="76" + android:viewportWidth="76" android:tint="?attr/colorControlActivated"> <group android:name="root" - android:translateX="24.0" - android:translateY="24.0" > + android:translateX="38.0" + android:translateY="38.0" > <path android:name="progressBar" android:fillColor="#00000000" - android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38" + android:pathData="M0, 0 m 0, -29 a 29,29 0 1,1 0,58 a 29,29 0 1,1 0,-58" android:strokeColor="@color/white" android:strokeLineCap="square" android:strokeLineJoin="miter" - android:strokeWidth="4" + android:strokeWidth="6" android:trimPathEnd="0" android:trimPathOffset="0" android:trimPathStart="0" /> diff --git a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml index 7f038f44b197..80e335563e7b 100644 --- a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml +++ b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml @@ -27,7 +27,7 @@ <path android:name="progressBar" android:fillColor="#00000000" - android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38" + android:pathData="M0, 0 m 0, -18 a 18,18 0 1,1 0,36 a 18,18 0 1,1 0,-36" android:strokeColor="@color/white" android:strokeLineCap="square" android:strokeLineJoin="miter" diff --git a/core/res/res/drawable/vector_drawable_progress_bar_small.xml b/core/res/res/drawable/vector_drawable_progress_bar_small.xml index 562578859be4..ebb632a00a19 100644 --- a/core/res/res/drawable/vector_drawable_progress_bar_small.xml +++ b/core/res/res/drawable/vector_drawable_progress_bar_small.xml @@ -16,22 +16,22 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="16dp" android:width="16dp" - android:viewportHeight="48" - android:viewportWidth="48" + android:viewportHeight="16" + android:viewportWidth="16" android:tint="?attr/colorControlActivated"> <group android:name="root" - android:translateX="24.0" - android:translateY="24.0" > + android:translateX="8.0" + android:translateY="8.0" > <path android:name="progressBar" android:fillColor="#00000000" - android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38" + android:pathData="M0, 0 m 0, -5.9375 a 5.9375,5.9375 0 1,1 0,11.875 a 5.9375,5.9375 0 1,1 0,-11.875" android:strokeColor="@color/white" android:strokeLineCap="square" android:strokeLineJoin="miter" - android:strokeWidth="4" + android:strokeWidth="2.125" android:trimPathEnd="0" android:trimPathOffset="0" android:trimPathStart="0" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 3c5b4c0042bd..7b85c7ed331f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2707,6 +2707,8 @@ <public type="attr" name="tunerCount" /> <public type="attr" name="nfcAntennaPositionDrawable" /> <public type="attr" name="fillType" /> + <public type="attr" name="popupEnterTransition" /> + <public type="attr" name="popupExitTransition" /> <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" /> <public type="style" name="Widget.Material.SeekBar.Discrete" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5d083d7f43c7..ba9bcaa03239 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2567,6 +2567,13 @@ <!-- Title of intent resolver dialog when selecting a sharing application to run and a previously used application is known. --> <string name="whichSendApplicationNamed">Share with %1$s</string> + <!-- Title of intent resolver dialog when selecting an application to run to + send content to a specific recipient. Often used for email. --> + <string name="whichSendToApplication">Send using</string> + <!-- Title of intent resolver dialog when selecting an application to run to + send content to a specific recipient and a previously used application is known. + Often used for email. --> + <string name="whichSendToApplicationNamed">Send using %1$s</string> <!-- Title of intent resolver dialog when selecting a HOME application to run. --> <string name="whichHomeApplication">Select a Home app</string> <!-- Title of intent resolver dialog when selecting a HOME application to run @@ -2936,8 +2943,8 @@ <!-- Title of the pop-up dialog in which the user switches keyboard, also known as input method. --> <string name="select_input_method">Change keyboard</string> - <!-- Title of a button to open the settings to enable or disable keyboards, also known as input methods [CHAR LIMIT=30] --> - <string name="configure_input_methods">Choose keyboards</string> + <!-- Title of a button to open the settings to enable or disable other soft keyboards (also known as input methods) [CHAR LIMIT=30] --> + <string name="configure_input_methods">Other keyboards</string> <!-- Summary text of a toggle switch to enable/disable use of the IME while a physical keyboard is connected --> <string name="show_ime">Keep it on screen while physical keyboard is active</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f01cce320b08..8b7818312495 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2213,6 +2213,8 @@ <java-symbol type="string" name="whichEditApplicationNamed" /> <java-symbol type="string" name="whichSendApplication" /> <java-symbol type="string" name="whichSendApplicationNamed" /> + <java-symbol type="string" name="whichSendToApplication" /> + <java-symbol type="string" name="whichSendToApplicationNamed" /> <java-symbol type="attr" name="lightY" /> <java-symbol type="attr" name="lightZ" /> <java-symbol type="attr" name="lightRadius" /> diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java index 7a6aad6a96f5..2d3935b6c9e7 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java @@ -870,39 +870,56 @@ public final class RemotePrintDocument { // AsyncCommand.AsyncCommandHandler#handleMessage if (isFailed()) { if (DEBUG) { - Log.i(LOG_TAG, "[CALLBACK] on canceled layout command"); + Log.i(LOG_TAG, "[CALLBACK] on failed layout command"); } return; - } else { - if (message.what != MSG_ON_LAYOUT_STARTED) { - // No need to force cancel anymore if layout finished + } + + int sequence; + int what = message.what; + switch (what) { + case MSG_ON_LAYOUT_FINISHED: removeForceCancel(); - } + sequence = message.arg2; + break; + case MSG_ON_LAYOUT_FAILED: + case MSG_ON_LAYOUT_CANCELED: + removeForceCancel(); + // $FALL-THROUGH - message uses the same format as "started" + case MSG_ON_LAYOUT_STARTED: + // Don't remote force-cancel as command is still running and might need to + // be canceled later + sequence = message.arg1; + break; + default: + // not reached + sequence = -1; + } + + // If we are canceling any result is treated as a cancel + if (isCanceling() && what != MSG_ON_LAYOUT_STARTED) { + what = MSG_ON_LAYOUT_CANCELED; } - switch (message.what) { + switch (what) { case MSG_ON_LAYOUT_STARTED: { ICancellationSignal cancellation = (ICancellationSignal) message.obj; - final int sequence = message.arg1; handleOnLayoutStarted(cancellation, sequence); } break; case MSG_ON_LAYOUT_FINISHED: { PrintDocumentInfo info = (PrintDocumentInfo) message.obj; final boolean changed = (message.arg1 == 1); - final int sequence = message.arg2; handleOnLayoutFinished(info, changed, sequence); } break; case MSG_ON_LAYOUT_FAILED: { CharSequence error = (CharSequence) message.obj; - final int sequence = message.arg1; handleOnLayoutFailed(error, sequence); } break; case MSG_ON_LAYOUT_CANCELED: { - final int sequence = message.arg1; handleOnLayoutCanceled(sequence); } break; } @@ -1142,18 +1159,30 @@ public final class RemotePrintDocument { // AsyncCommand.AsyncCommandHandler#handleMessage if (isFailed()) { if (DEBUG) { - Log.i(LOG_TAG, "[CALLBACK] on canceled write command"); + Log.i(LOG_TAG, "[CALLBACK] on failed write command"); } return; - } else { - if (message.what != MSG_ON_WRITE_STARTED) { - // No need to force cancel anymore if write finished + } + + int what = message.what; + switch (what) { + case MSG_ON_WRITE_FINISHED: + case MSG_ON_WRITE_FAILED: + case MSG_ON_WRITE_CANCELED: removeForceCancel(); - } + case MSG_ON_WRITE_STARTED: + // Don't remote force-cancel as command is still running and might need to + // be canceled later + break; + } + + // If we are canceling any result is treated as a cancel + if (isCanceling() && what != MSG_ON_WRITE_STARTED) { + what = MSG_ON_WRITE_CANCELED; } - switch (message.what) { + switch (what) { case MSG_ON_WRITE_STARTED: { ICancellationSignal cancellation = (ICancellationSignal) message.obj; final int sequence = message.arg1; diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index ad4823eb086c..4e1180d051bf 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -159,6 +159,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private static final int STATE_PRINTER_UNAVAILABLE = 6; private static final int STATE_UPDATE_SLOW = 7; private static final int STATE_PRINT_COMPLETED = 8; + private static final int STATE_FINISHING = 9; private static final int UI_STATE_PREVIEW = 0; private static final int UI_STATE_ERROR = 1; @@ -2029,11 +2030,17 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } private void doFinish() { - if (mPrintedDocument.isUpdating()) { + if (mPrintedDocument != null && mPrintedDocument.isUpdating()) { // The printedDocument will call doFinish() when the current command finishes return; } + if (mState == STATE_FINISHING) { + return; + } + + mState = STATE_FINISHING; + if (mPrinterRegistry != null) { mPrinterRegistry.setTrackedPrinter(null); } diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml index 5a6553ff90db..9c2c0ab9faa3 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml @@ -27,29 +27,19 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingEnd="12dp" - android:background="@android:color/white" - android:textColor="#D9000000" + android:textColor="@color/ksh_keyword_color" android:textSize="16sp" android:maxLines="5" android:singleLine="false" android:scrollHorizontally="false" - android:layout_alignParentStart="true" - android:minWidth="100dp" - android:maxWidth="260dp"/> - <!--TODO: introduce and use a layout that allows wrapping and right align --> - <LinearLayout + android:layout_alignParentStart="true"/> + <com.android.systemui.statusbar.KeyboardShortcutKeysLayout android:id="@+id/keyboard_shortcuts_item_container" android:layout_toEndOf="@+id/keyboard_shortcuts_keyword" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@android:color/white" android:layout_alignParentEnd="true" - android:gravity="end" android:textSize="14sp" - android:paddingStart="0dp" - android:paddingEnd="0dp" - android:scrollHorizontally="false" - android:minWidth="100dp" - android:maxWidth="260dp"/> + android:scrollHorizontally="false"/> </RelativeLayout> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml index 80a478a6d3d8..6cb8470eaf21 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml @@ -22,4 +22,4 @@ android:paddingStart="24dp" android:paddingTop="20dp" android:paddingEnd="24dp" - android:paddingBottom="13dp" /> + android:paddingBottom="13dp"/> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml index f73ee1532ba3..7aba1cf6e0dc 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml @@ -17,7 +17,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="488dp" + android:layout_width="@dimen/ksh_layout_width" android:layout_height="wrap_content"> <LinearLayout android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index a2fa3b976922..66963c43e8dc 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -104,4 +104,7 @@ <!-- The side padding for the task stack. --> <dimen name="recents_stack_left_right_padding">64dp</dimen> + + <!-- Keyboard shortcuts helper --> + <dimen name="ksh_layout_width">488dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index eeed0cf62287..a3f8b8551def 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -167,6 +167,7 @@ <!-- Keyboard shortcuts colors --> <color name="ksh_system_group_color">#ff00bcd4</color> <color name="ksh_application_group_color">#fff44336</color> + <color name="ksh_keyword_color">#d9000000</color> <!-- Background color of edit overflow --> <color name="qs_edit_overflow_bg">#455A64</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 6a9f45606bab..8b433f984d65 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -639,4 +639,7 @@ <dimen name="battery_detail_graph_space_top">27dp</dimen> <dimen name="battery_detail_graph_space_bottom">27dp</dimen> + + <!-- Keyboard shortcuts helper --> + <dimen name="ksh_layout_width">@dimen/match_parent</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index f33ef654eaf7..aab45b5d02f6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -749,7 +749,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD public final void onBusEvent(AllTaskViewsDismissedEvent event) { SystemServicesProxy ssp = Recents.getSystemServices(); if (ssp.hasDockedTask()) { - mRecentsView.showEmptyView(R.string.recents_empty_message_dismissed_all); + mRecentsView.showEmptyView(event.msgResId); } else { // Just go straight home (no animation necessary because there are no more task views) dismissRecentsToHome(false /* animateTaskViews */); diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java index cf74519624ea..0352161be570 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java @@ -22,5 +22,10 @@ import com.android.systemui.recents.events.EventBus; * This is sent whenever all the task views in a stack have been dismissed. */ public class AllTaskViewsDismissedEvent extends EventBus.Event { - // Simple event + + public final int msgResId; + + public AllTaskViewsDismissedEvent(int msgResId) { + this.msgResId = msgResId; + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java index 5eeda72637ea..16385c9ab358 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java @@ -245,7 +245,7 @@ public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAd if (row.getViewType() == TASK_ROW_VIEW_TYPE) { TaskRow taskRow = (TaskRow) row; Task task = taskRow.task; - mStack.removeTask(task, AnimationProps.IMMEDIATE); + mStack.removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */); EventBus.getDefault().send(new DeleteTaskDataEvent(task)); i = removeTaskRow(i); } @@ -326,7 +326,7 @@ public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAd public void onTaskRemoved(Task task, int position) { // Since this is removed from the history, we need to update the stack as well to ensure // that the model is correct. Since the stack is hidden, we can update it immediately. - mStack.removeTask(task, AnimationProps.IMMEDIATE); + mStack.removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */); removeTaskRow(position); if (mRows.isEmpty()) { dismissHistory(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 4d1c552f2687..193bfffffd9f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -219,7 +219,7 @@ public class TaskStack { * Notifies when a task has been removed from the stack. */ void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask, - Task newFrontMostTask, AnimationProps animation); + Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture); /** * Notifies when a task has been removed from the history. @@ -513,14 +513,15 @@ public class TaskStack { * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on * how they should update themselves. */ - public void removeTask(Task t, AnimationProps animation) { + public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) { if (mStackTaskList.contains(t)) { boolean wasFrontMostTask = (getStackFrontMostTask(false /* includeFreeform */) == t); removeTaskImpl(mStackTaskList, t); Task newFrontMostTask = getStackFrontMostTask(false /* includeFreeform */); if (mCb != null) { // Notify that a task has been removed - mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask, animation); + mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask, animation, + fromDockGesture); } } else if (mHistoryTaskList.contains(t)) { removeTaskImpl(mHistoryTaskList, t); @@ -558,9 +559,9 @@ public class TaskStack { if (notifyStackChanges) { // If we are notifying, then remove the task now, otherwise the raw task list // will be reset at the end of this method - removeTask(task, AnimationProps.IMMEDIATE); + removeTask(task, AnimationProps.IMMEDIATE, false /* fromDockGesture */); mCb.onStackTaskRemoved(this, task, i == (taskCount - 1), null, - AnimationProps.IMMEDIATE); + AnimationProps.IMMEDIATE, false /* fromDockGesture */); } } task.setGroup(null); diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java index 4458639acfad..cf8c9bb38b7c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java @@ -135,7 +135,7 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T @Override public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask, - Task newFrontMostTask, AnimationProps animation) { + Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture) { getAdapter().notifyItemRemoved(stack.getStackTasks().indexOf(removedTask)); if (mFocusedTask == removedTask) { resetFocusedTask(removedTask); @@ -144,7 +144,9 @@ public class TaskStackHorizontalGridView extends HorizontalGridView implements T if (mStack.getStackTaskCount() == 0) { boolean shouldFinishActivity = (mStack.getStackTaskCount() == 0); if (shouldFinishActivity) { - EventBus.getDefault().send(new AllTaskViewsDismissedEvent()); + EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture + ? R.string.recents_empty_message + : R.string.recents_empty_message_dismissed_all)); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 92356736763e..db97e8f95b55 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -632,7 +632,8 @@ public class RecentsView extends FrameLayout { AnimationProps stackAnim = new AnimationProps( TaskStackView.DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN); - mTaskStackView.getStack().removeTask(event.task, stackAnim); + mTaskStackView.getStack().removeTask(event.task, stackAnim, + true /* fromDockGesture */); } })); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java index 84590f25149f..33d5bb7b0c99 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java @@ -150,8 +150,7 @@ public class RecentsViewTouchHandler { mTaskView.setTranslationY(y); mVisibleDockStates.clear(); - if (ActivityManager.supportsMultiWindow() && - !ssp.hasDockedTask() && mRv.getTaskStack().getTaskCount() > 1) { + if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()) { if (!event.task.isDockable) { Toast.makeText(mRv.getContext(), R.string.recents_drag_non_dockable_task_message, Toast.LENGTH_SHORT).show(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 93b5b6c12904..0b20d21d806c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -1395,7 +1395,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ @Override public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask, - Task newFrontMostTask, AnimationProps animation) { + Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture) { if (mFocusedTask == removedTask) { resetFocusedTask(removedTask); } @@ -1426,7 +1426,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // If there are no remaining tasks, then just close recents if (mStack.getTaskCount() == 0) { - EventBus.getDefault().send(new AllTaskViewsDismissedEvent()); + EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture + ? R.string.recents_empty_message + : R.string.recents_empty_message_dismissed_all)); } } @@ -1599,7 +1601,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal tv.dismissTask(); } else { // Otherwise, remove the task from the stack immediately - mStack.removeTask(t, AnimationProps.IMMEDIATE); + mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */); } } } @@ -1937,7 +1939,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Remove the task from the stack mStack.removeTask(task, new AnimationProps(DEFAULT_SYNC_STACK_DURATION, - Interpolators.FAST_OUT_SLOW_IN)); + Interpolators.FAST_OUT_SLOW_IN), false /* fromDockGesture */); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java new file mode 100644 index 000000000000..ba3e774ea2b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; + +/** + * Layout used as a container for keyboard shortcut keys. It's children are wrapped and right + * aligned. + */ +public final class KeyboardShortcutKeysLayout extends ViewGroup { + private int mLineHeight; + + public KeyboardShortcutKeysLayout(Context context) { + super(context); + } + + public KeyboardShortcutKeysLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); + int childCount = getChildCount(); + int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); + int lineHeight = 0; + int xPos = getPaddingLeft(); + int yPos = getPaddingTop(); + + int childHeightMeasureSpec; + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); + } else { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams layoutParams = (LayoutParams) child.getLayoutParams(); + child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), + childHeightMeasureSpec); + int childWidth = child.getMeasuredWidth(); + lineHeight = Math.max(lineHeight, + child.getMeasuredHeight() + layoutParams.mVerticalSpacing); + + if (xPos + childWidth > width) { + xPos = getPaddingLeft(); + yPos += lineHeight; + } + xPos += childWidth + layoutParams.mHorizontalSpacing; + } + } + this.mLineHeight = lineHeight; + + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { + height = yPos + lineHeight; + } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { + if (yPos + lineHeight < height) { + height = yPos + lineHeight; + } + } + setMeasuredDimension(width, height); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + int spacing = getHorizontalVerticalSpacing(); + return new LayoutParams(spacing, spacing); + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams layoutParams) { + int spacing = getHorizontalVerticalSpacing(); + return new LayoutParams(spacing, spacing, layoutParams); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return (p instanceof LayoutParams); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int childCount = getChildCount(); + int fullRowWidth = r - l; + int xPos = getPaddingLeft(); + int yPos = getPaddingTop(); + int lastHorizontalSpacing = 0; + // The index of the child which starts the current row. + int rowStartIdx = 0; + + // Go through all the children. + for (int i = 0; i < childCount; i++) { + View currentChild = getChildAt(i); + if (currentChild.getVisibility() != GONE) { + int currentChildWidth = currentChild.getMeasuredWidth(); + LayoutParams lp = (LayoutParams) currentChild.getLayoutParams(); + + // If the current child does not fit on this row. + if (xPos + currentChildWidth > fullRowWidth) { + // Layout all the children on this row but the current one. + layoutChildrenOnRow(rowStartIdx, i, fullRowWidth, xPos, yPos, + lastHorizontalSpacing); + // Update the positions for starting on the new row. + xPos = getPaddingLeft(); + yPos += mLineHeight; + rowStartIdx = i; + } + + xPos += currentChildWidth + lp.mHorizontalSpacing; + lastHorizontalSpacing = lp.mHorizontalSpacing; + } + } + + // Lay out the children on the last row. + if (rowStartIdx < childCount) { + layoutChildrenOnRow(rowStartIdx, childCount, fullRowWidth, xPos, yPos, + lastHorizontalSpacing); + } + } + + private int getHorizontalVerticalSpacing() { + DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); + return (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 4, displayMetrics); + } + + private void layoutChildrenOnRow(int startIndex, int endIndex, int fullRowWidth, int xPos, + int yPos, int lastHorizontalSpacing) { + int freeSpace = fullRowWidth - xPos + lastHorizontalSpacing; + xPos = getPaddingLeft() + freeSpace; + + for (int j = startIndex; j < endIndex; ++j) { + View currentChild = getChildAt(j); + currentChild.layout( + xPos, + yPos, + xPos + currentChild.getMeasuredWidth(), + yPos + currentChild.getMeasuredHeight()); + xPos += currentChild.getMeasuredWidth() + + ((LayoutParams) currentChild.getLayoutParams()).mHorizontalSpacing; + } + } + + public static class LayoutParams extends ViewGroup.LayoutParams { + public final int mHorizontalSpacing; + public final int mVerticalSpacing; + + public LayoutParams(int horizontalSpacing, int verticalSpacing, + ViewGroup.LayoutParams viewGroupLayout) { + super(viewGroupLayout); + this.mHorizontalSpacing = horizontalSpacing; + this.mVerticalSpacing = verticalSpacing; + } + + public LayoutParams(int mHorizontalSpacing, int verticalSpacing) { + super(0, 0); + this.mHorizontalSpacing = mHorizontalSpacing; + this.mVerticalSpacing = verticalSpacing; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 0b7bfa8871ee..60c2fa61830c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -29,6 +29,7 @@ import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager.KeyboardShortcutsReceiver; import android.widget.LinearLayout; @@ -153,7 +154,7 @@ public class KeyboardShortcuts { .findViewById(R.id.keyboard_shortcuts_keyword); textView.setText(info.getLabel()); - LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView + ViewGroup shortcutItemsContainer = (ViewGroup) shortcutView .findViewById(R.id.keyboard_shortcuts_item_container); List<String> shortcutKeys = getHumanReadableShortcutKeys(info); final int shortcutKeysSize = shortcutKeys.size(); diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index a4bbb515154f..50699f8ccf4a 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -35,14 +35,14 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; -import android.provider.Settings; import android.provider.Settings.Global; +import android.provider.Settings; import android.util.AndroidRuntimeException; import android.util.Slog; import android.webkit.IWebViewUpdateService; +import android.webkit.WebViewFactory; import android.webkit.WebViewProviderInfo; import android.webkit.WebViewProviderResponse; -import android.webkit.WebViewFactory; import com.android.server.SystemService; @@ -76,9 +76,11 @@ public class WebViewUpdateService extends SystemService { private WebViewProviderInfo[] mCurrentValidWebViewPackages = null; private BroadcastReceiver mWebViewUpdatedReceiver; + private WebViewUtilityInterface mWebViewUtility; public WebViewUpdateService(Context context) { super(context); + mWebViewUtility = new WebViewUtilityImpl(); } @Override @@ -114,7 +116,7 @@ public class WebViewUpdateService extends SystemService { updateFallbackState(context, intent); - for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) { + for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) { String webviewPackage = "package:" + provider.packageName; if (webviewPackage.equals(intent.getDataString())) { @@ -153,11 +155,7 @@ public class WebViewUpdateService extends SystemService { // package that was not the previous provider then we must kill // packages dependent on the old package ourselves. The framework // only kills dependents of packages that are being removed. - try { - ActivityManagerNative.getDefault().killPackageDependents( - oldProviderName, UserHandle.USER_ALL); - } catch (RemoteException e) { - } + mWebViewUtility.killPackageDependents(oldProviderName); } return; } @@ -170,7 +168,7 @@ public class WebViewUpdateService extends SystemService { filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); // Make sure we only receive intents for WebView packages from our config file. - for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) { + for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) { filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL); } getContext().registerReceiver(mWebViewUpdatedReceiver, filter); @@ -210,7 +208,7 @@ public class WebViewUpdateService extends SystemService { void handleNewUser(int userId) { if (!isFallbackLogicEnabled()) return; - WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages(); + WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages(); WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); if (fallbackProvider == null) return; boolean existsValidNonFallbackProvider = @@ -228,7 +226,7 @@ public class WebViewUpdateService extends SystemService { void updateFallbackState(final Context context, final Intent intent) { if (!isFallbackLogicEnabled()) return; - WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages(); + WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages(); if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED) || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) { @@ -319,10 +317,10 @@ public class WebViewUpdateService extends SystemService { return false; } - private static boolean isFallbackPackage(String packageName) { + private boolean isFallbackPackage(String packageName) { if (packageName == null || !isFallbackLogicEnabled()) return false; - WebViewProviderInfo[] webviewPackages = WebViewFactory.getWebViewPackages(); + WebViewProviderInfo[] webviewPackages = mWebViewUtility.getWebViewPackages(); WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages); return (fallbackProvider != null && packageName.equals(fallbackProvider.packageName)); @@ -359,13 +357,13 @@ public class WebViewUpdateService extends SystemService { PackageInfo newPackage = null; synchronized(this) { oldPackage = mCurrentWebViewPackage; - updateUserSetting(newProviderName); + mWebViewUtility.updateUserSetting(getContext(), newProviderName); try { newPackage = findPreferredWebViewPackage(); if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) { // If we don't perform the user change, revert the settings change. - updateUserSetting(newPackage.packageName); + mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName); return newPackage.packageName; } } catch (WebViewFactory.MissingWebViewPackageException e) { @@ -378,12 +376,8 @@ public class WebViewUpdateService extends SystemService { onWebViewProviderChanged(newPackage); } // Kill apps using the old provider - try { - if (oldPackage != null) { - ActivityManagerNative.getDefault().killPackageDependents( - oldPackage.packageName, UserHandle.USER_ALL); - } - } catch (RemoteException e) { + if (oldPackage != null) { + mWebViewUtility.killPackageDependents(oldPackage.packageName); } return newPackage.packageName; } @@ -397,14 +391,14 @@ public class WebViewUpdateService extends SystemService { mAnyWebViewInstalled = true; if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { mCurrentWebViewPackage = newPackage; - updateUserSetting(newPackage.packageName); + mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName); // The relro creations might 'finish' (not start at all) before // WebViewFactory.onWebViewProviderChanged which means we might not know the number // of started creations before they finish. mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; mNumRelroCreationsFinished = 0; - mNumRelroCreationsStarted = WebViewFactory.onWebViewProviderChanged(newPackage); + mNumRelroCreationsStarted = mWebViewUtility.onWebViewProviderChanged(newPackage); // If the relro creations finish before we know the number of started creations we // will have to do any cleanup/notifying here. checkIfRelrosDoneLocked(); @@ -421,7 +415,7 @@ public class WebViewUpdateService extends SystemService { * */ private void updateValidWebViewPackages() { List<WebViewProviderInfo> webViewProviders = - new ArrayList<WebViewProviderInfo>(Arrays.asList(WebViewFactory.getWebViewPackages())); + new ArrayList<WebViewProviderInfo>(Arrays.asList(mWebViewUtility.getWebViewPackages())); Iterator<WebViewProviderInfo> it = webViewProviders.iterator(); // remove non-valid packages while(it.hasNext()) { @@ -435,17 +429,6 @@ public class WebViewUpdateService extends SystemService { } } - private static String getUserChosenWebViewProvider() { - return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(), - Settings.Global.WEBVIEW_PROVIDER); - } - - private void updateUserSetting(String newProviderName) { - Settings.Global.putString(getContext().getContentResolver(), - Settings.Global.WEBVIEW_PROVIDER, - newProviderName == null ? "" : newProviderName); - } - /** * Returns either the package info of the WebView provider determined in the following way: * If the user has chosen a provider then use that if it is valid, @@ -456,7 +439,7 @@ public class WebViewUpdateService extends SystemService { private PackageInfo findPreferredWebViewPackage() { WebViewProviderInfo[] providers = mCurrentValidWebViewPackages; - String userChosenProvider = getUserChosenWebViewProvider(); + String userChosenProvider = mWebViewUtility.getUserChosenWebViewProvider(getContext()); // If the user has chosen provider, use that for (WebViewProviderInfo provider : providers) { @@ -616,6 +599,11 @@ public class WebViewUpdateService extends SystemService { } @Override // Binder call + public WebViewProviderInfo[] getAllWebViewPackages() { + return WebViewUpdateService.this.mWebViewUtility.getWebViewPackages(); + } + + @Override // Binder call public String getCurrentWebViewPackageName() { synchronized(WebViewUpdateService.this) { if (WebViewUpdateService.this.mCurrentWebViewPackage == null) @@ -626,7 +614,7 @@ public class WebViewUpdateService extends SystemService { @Override // Binder call public boolean isFallbackPackage(String packageName) { - return WebViewUpdateService.isFallbackPackage(packageName); + return WebViewUpdateService.this.isFallbackPackage(packageName); } @Override // Binder call diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java new file mode 100644 index 000000000000..4dbd02d1ede5 --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.webkit; + +import android.app.ActivityManagerNative; +import android.app.AppGlobals; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.res.XmlResourceParser; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.AndroidRuntimeException; +import android.util.Log; +import android.webkit.WebViewFactory; +import android.webkit.WebViewFactory.MissingWebViewPackageException; +import android.webkit.WebViewProviderInfo; + +import com.android.internal.util.XmlUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.xmlpull.v1.XmlPullParserException; + +/** + * Default implementation for the WebView preparation Utility interface. + * @hide + */ +public class WebViewUtilityImpl implements WebViewUtilityInterface { + private static final String TAG = WebViewUtilityImpl.class.getSimpleName(); + private static final String TAG_START = "webviewproviders"; + private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider"; + private static final String TAG_PACKAGE_NAME = "packageName"; + private static final String TAG_DESCRIPTION = "description"; + // Whether or not the provider must be explicitly chosen by the user to be used. + private static final String TAG_AVAILABILITY = "availableByDefault"; + private static final String TAG_SIGNATURE = "signature"; + private static final String TAG_FALLBACK = "isFallback"; + + /** + * Returns all packages declared in the framework resources as potential WebView providers. + * @hide + * */ + @Override + public WebViewProviderInfo[] getWebViewPackages() { + int numFallbackPackages = 0; + XmlResourceParser parser = null; + List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>(); + try { + parser = AppGlobals.getInitialApplication().getResources().getXml( + com.android.internal.R.xml.config_webview_packages); + XmlUtils.beginDocument(parser, TAG_START); + while(true) { + XmlUtils.nextElement(parser); + String element = parser.getName(); + if (element == null) { + break; + } + if (element.equals(TAG_WEBVIEW_PROVIDER)) { + String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME); + if (packageName == null) { + throw new MissingWebViewPackageException( + "WebView provider in framework resources missing package name"); + } + String description = parser.getAttributeValue(null, TAG_DESCRIPTION); + if (description == null) { + throw new MissingWebViewPackageException( + "WebView provider in framework resources missing description"); + } + boolean availableByDefault = "true".equals( + parser.getAttributeValue(null, TAG_AVAILABILITY)); + boolean isFallback = "true".equals( + parser.getAttributeValue(null, TAG_FALLBACK)); + WebViewProviderInfo currentProvider = + new WebViewProviderInfo(packageName, description, availableByDefault, + isFallback, readSignatures(parser)); + if (currentProvider.isFallbackPackage()) { + numFallbackPackages++; + if (numFallbackPackages > 1) { + throw new AndroidRuntimeException( + "There can be at most one webview fallback package."); + } + } + webViewProviders.add(currentProvider); + } + else { + Log.e(TAG, "Found an element that is not a webview provider"); + } + } + } catch(XmlPullParserException e) { + throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e); + } catch(IOException e) { + throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e); + } finally { + if (parser != null) parser.close(); + } + return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]); + } + + /** + * Reads all signatures at the current depth (within the current provider) from the XML parser. + */ + private static String[] readSignatures(XmlResourceParser parser) throws IOException, + XmlPullParserException { + List<String> signatures = new ArrayList<String>(); + int outerDepth = parser.getDepth(); + while(XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals(TAG_SIGNATURE)) { + // Parse the value within the signature tag + String signature = parser.nextText(); + signatures.add(signature); + } else { + Log.e(TAG, "Found an element in a webview provider that is not a signature"); + } + } + return signatures.toArray(new String[signatures.size()]); + } + + @Override + public int onWebViewProviderChanged(PackageInfo packageInfo) { + return WebViewFactory.onWebViewProviderChanged(packageInfo); + } + + @Override + public String getUserChosenWebViewProvider(Context context) { + return Settings.Global.getString(context.getContentResolver(), + Settings.Global.WEBVIEW_PROVIDER); + } + + @Override + public void updateUserSetting(Context context, String newProviderName) { + Settings.Global.putString(context.getContentResolver(), + Settings.Global.WEBVIEW_PROVIDER, + newProviderName == null ? "" : newProviderName); + } + + @Override + public void killPackageDependents(String packageName) { + try { + ActivityManagerNative.getDefault().killPackageDependents(packageName, + UserHandle.USER_ALL); + } catch (RemoteException e) { + } + } +} diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java b/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java new file mode 100644 index 000000000000..1919f400c29a --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.webkit; + +import android.webkit.WebViewProviderInfo; +import android.content.Context; +import android.content.pm.PackageInfo; + +/** + * Utility interface for the WebViewUpdateService. + * This interface provides a way to test the WebView preparation mechanism - during normal use this + * interface is implemented using calls to the Android framework, but by providing an alternative + * implementation we can test the WebView preparation logic without reaching other framework code. + * @hide + */ +public interface WebViewUtilityInterface { + public WebViewProviderInfo[] getWebViewPackages(); + public int onWebViewProviderChanged(PackageInfo packageInfo); + + public String getUserChosenWebViewProvider(Context context); + public void updateUserSetting(Context context, String newProviderName); + public void killPackageDependents(String packageName); +} diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index f2f555b553d5..8ab18788beaf 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -443,10 +443,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { @Nullable List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); - // No services - nothing to do. - if (mActiveServices.isEmpty()) { - return; - } + // No session - nothing to do. if (mPrinterDiscoverySession == null) { return; @@ -460,10 +457,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); - // No services - nothing to do. - if (mActiveServices.isEmpty()) { - return; - } + // No session - nothing to do. if (mPrinterDiscoverySession == null) { return; |