diff options
44 files changed, 753 insertions, 316 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index 0902fc6daaf1..7256e5cf1cf9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6226,11 +6226,28 @@ package android.app { } public class WindowConfiguration implements java.lang.Comparable android.os.Parcelable { + ctor public WindowConfiguration(); method public int compareTo(android.app.WindowConfiguration); method public int describeContents(); + method public int getActivityType(); + method public android.graphics.Rect getAppBounds(); + method public int getWindowingMode(); + method public void setActivityType(int); + method public void setAppBounds(android.graphics.Rect); + method public void setTo(android.app.WindowConfiguration); + method public void setWindowingMode(int); method public void writeToParcel(android.os.Parcel, int); + field public static final int ACTIVITY_TYPE_ASSISTANT = 4; // 0x4 field public static final int ACTIVITY_TYPE_HOME = 2; // 0x2 field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3 + field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1 + field public static final int ACTIVITY_TYPE_UNDEFINED = 0; // 0x0 + field public static final int WINDOWING_MODE_FREEFORM = 5; // 0x5 + field public static final int WINDOWING_MODE_FULLSCREEN = 1; // 0x1 + field public static final int WINDOWING_MODE_PINNED = 2; // 0x2 + field public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3; // 0x3 + field public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4; // 0x4 + field public static final int WINDOWING_MODE_UNDEFINED = 0; // 0x0 } } @@ -11328,6 +11345,7 @@ package android.content.res { field public int smallestScreenWidthDp; field public int touchscreen; field public int uiMode; + field public final android.app.WindowConfiguration windowConfiguration; } public class ObbInfo implements android.os.Parcelable { diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index ad989dee7b55..79faa1bf97a2 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -636,7 +636,7 @@ public final class Pm { out = session.openWrite(splitName, 0, sizeBytes); int total = 0; - byte[] buffer = new byte[65536]; + byte[] buffer = new byte[1024 * 1024]; int c; while ((c = in.read(buffer)) != -1) { total += c; diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 4a092140ed78..fe7afed8de13 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -14,15 +14,14 @@ * limitations under the License. */ - package android.app; import android.annotation.IntDef; import android.annotation.SystemService; import android.content.Context; import android.os.Binder; -import android.os.RemoteException; import android.os.IBinder; +import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import android.view.View; diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 5d87e1c2a157..07eb5de15d9f 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -17,6 +17,9 @@ package android.app; import static android.app.ActivityThread.isSystem; +import static android.app.WindowConfigurationProto.ACTIVITY_TYPE; +import static android.app.WindowConfigurationProto.APP_BOUNDS; +import static android.app.WindowConfigurationProto.WINDOWING_MODE; import android.annotation.IntDef; import android.annotation.NonNull; @@ -25,6 +28,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; +import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; /** @@ -48,26 +52,20 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** The current windowing mode of the configuration. */ private @WindowingMode int mWindowingMode; - /** Windowing mode is currently not defined. - * @hide */ + /** Windowing mode is currently not defined. */ public static final int WINDOWING_MODE_UNDEFINED = 0; - /** Occupies the full area of the screen or the parent container. - * @hide */ + /** Occupies the full area of the screen or the parent container. */ public static final int WINDOWING_MODE_FULLSCREEN = 1; - /** Always on-top (always visible). of other siblings in its parent container. - * @hide */ + /** Always on-top (always visible). of other siblings in its parent container. */ public static final int WINDOWING_MODE_PINNED = 2; - /** The primary container driving the screen to be in split-screen mode. - * @hide */ + /** The primary container driving the screen to be in split-screen mode. */ public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3; /** * The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in * split-screen mode. - * @hide */ public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4; - /** Can be freely resized within its parent container. - * @hide */ + /** Can be freely resized within its parent container. */ public static final int WINDOWING_MODE_FREEFORM = 5; /** @hide */ @@ -84,18 +82,15 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** The current activity type of the configuration. */ private @ActivityType int mActivityType; - /** Activity type is currently not defined. - * @hide */ + /** Activity type is currently not defined. */ public static final int ACTIVITY_TYPE_UNDEFINED = 0; - /** Standard activity type. Nothing special about the activity... - * @hide */ + /** Standard activity type. Nothing special about the activity... */ public static final int ACTIVITY_TYPE_STANDARD = 1; /** Home/Launcher activity type. */ public static final int ACTIVITY_TYPE_HOME = 2; /** Recents/Overview activity type. */ public static final int ACTIVITY_TYPE_RECENTS = 3; - /** Assistant activity type. - * @hide */ + /** Assistant activity type. */ public static final int ACTIVITY_TYPE_ASSISTANT = 4; /** @hide */ @@ -127,7 +122,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu }) public @interface WindowConfig {} - /** @hide */ public WindowConfiguration() { unset(); } @@ -176,7 +170,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu * Set {@link #mAppBounds} to the input Rect. * @param rect The rect value to set {@link #mAppBounds} to. * @see #getAppBounds() - * @hide */ public void setAppBounds(Rect rect) { if (rect == null) { @@ -200,26 +193,20 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu mAppBounds.set(left, top, right, bottom); } - /** - * @see #setAppBounds(Rect) - * @hide - */ + /** @see #setAppBounds(Rect) */ public Rect getAppBounds() { return mAppBounds; } - /** @hide */ public void setWindowingMode(@WindowingMode int windowingMode) { mWindowingMode = windowingMode; } - /** @hide */ @WindowingMode public int getWindowingMode() { return mWindowingMode; } - /** @hide */ public void setActivityType(@ActivityType int activityType) { if (mActivityType == activityType) { return; @@ -237,13 +224,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu mActivityType = activityType; } - /** @hide */ @ActivityType public int getActivityType() { return mActivityType; } - /** @hide */ public void setTo(WindowConfiguration other) { setAppBounds(other.mAppBounds); setWindowingMode(other.mWindowingMode); @@ -382,6 +367,24 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu } /** + * Write to a protocol buffer output stream. + * Protocol buffer message definition at {@link android.app.WindowConfigurationProto} + * + * @param protoOutputStream Stream to write the WindowConfiguration object to. + * @param fieldId Field Id of the WindowConfiguration as defined in the parent message + * @hide + */ + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + final long token = protoOutputStream.start(fieldId); + if (mAppBounds != null) { + mAppBounds.writeToProto(protoOutputStream, APP_BOUNDS); + } + protoOutputStream.write(WINDOWING_MODE, mWindowingMode); + protoOutputStream.write(ACTIVITY_TYPE, mActivityType); + protoOutputStream.end(token); + } + + /** * Returns true if the activities associated with this window configuration display a shadow * around their border. * @hide diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index 5bfc54d267ca..76cb3f5b548e 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -232,7 +232,7 @@ public final class BluetoothUuid { */ public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); - long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; + long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32; return (int) value; } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 780e6f769290..1310e30300e2 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -16,9 +16,20 @@ package android.content.res; +import static android.content.ConfigurationProto.DENSITY_DPI; +import static android.content.ConfigurationProto.FONT_SCALE; +import static android.content.ConfigurationProto.ORIENTATION; +import static android.content.ConfigurationProto.SCREEN_HEIGHT_DP; +import static android.content.ConfigurationProto.SCREEN_LAYOUT; +import static android.content.ConfigurationProto.SCREEN_WIDTH_DP; +import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP; +import static android.content.ConfigurationProto.UI_MODE; +import static android.content.ConfigurationProto.WINDOW_CONFIGURATION; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.app.WindowConfiguration; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.Config; @@ -27,6 +38,7 @@ import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.proto.ProtoOutputStream; import android.view.View; import com.android.internal.util.XmlUtils; @@ -295,11 +307,12 @@ public final class Configuration implements Parcelable, Comparable<Configuration public int screenLayout; /** - * @hide * Configuration relating to the windowing state of the object associated with this * Configuration. Contents of this field are not intended to affect resources, but need to be * communicated and propagated at the same time as the rest of Configuration. + * @hide */ + @TestApi public final WindowConfiguration windowConfiguration = new WindowConfiguration(); /** @hide */ @@ -1054,6 +1067,28 @@ public final class Configuration implements Parcelable, Comparable<Configuration } /** + * Write to a protocol buffer output stream. + * Protocol buffer message definition at {@link android.content.ConfigurationProto} + * + * @param protoOutputStream Stream to write the Configuration object to. + * @param fieldId Field Id of the Configuration as defined in the parent message + * @hide + */ + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + final long token = protoOutputStream.start(fieldId); + protoOutputStream.write(FONT_SCALE, fontScale); + protoOutputStream.write(SCREEN_LAYOUT, screenLayout); + protoOutputStream.write(ORIENTATION, orientation); + protoOutputStream.write(UI_MODE, uiMode); + protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp); + protoOutputStream.write(SCREEN_HEIGHT_DP, screenHeightDp); + protoOutputStream.write(SMALLEST_SCREEN_WIDTH_DP, smallestScreenWidthDp); + protoOutputStream.write(DENSITY_DPI, densityDpi); + windowConfiguration.writeToProto(protoOutputStream, WINDOW_CONFIGURATION); + protoOutputStream.end(token); + } + + /** * Set this object to the system defaults. */ public void setToDefaults() { diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index fc56455236a2..2c83fc4d9049 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -32,6 +32,7 @@ import android.text.BidiFormatter; import android.text.TextUtils; import android.view.View; +import java.lang.reflect.Constructor; import java.math.BigDecimal; import java.util.Locale; @@ -194,13 +195,29 @@ public final class Formatter { /** * ICU doesn't support PETABYTE yet. Fake it so that we can treat all units the same way. - * {@hide} */ - public static final MeasureUnit PETABYTE = MeasureUnit.internalGetInstance( - "digital", "petabyte"); + private static final MeasureUnit PETABYTE = createPetaByte(); - /** {@hide} */ - public static class RoundedBytesResult { + /** + * Create a petabyte MeasureUnit without registering it with ICU. + * ICU doesn't support user-create MeasureUnit and the only public (but hidden) method to do so + * is {@link MeasureUnit#internalGetInstance(String, String)} which also registers the unit as + * an available type and thus leaks it to code that doesn't expect or support it. + * <p>This method uses reflection to create an instance of MeasureUnit to avoid leaking it. This + * instance is <b>only</b> to be used in this class. + */ + private static MeasureUnit createPetaByte() { + try { + Constructor<MeasureUnit> constructor = MeasureUnit.class + .getDeclaredConstructor(String.class, String.class); + constructor.setAccessible(true); + return constructor.newInstance("digital", "petabyte"); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to create petabyte MeasureUnit", e); + } + } + + private static class RoundedBytesResult { public final float value; public final MeasureUnit units; public final int fractionDigits; @@ -218,7 +235,7 @@ public final class Formatter { * Returns a RoundedBytesResult object based on the input size in bytes and the rounding * flags. The result can be used for formatting. */ - public static RoundedBytesResult roundBytes(long sizeBytes, int flags) { + static RoundedBytesResult roundBytes(long sizeBytes, int flags) { final boolean isNegative = (sizeBytes < 0); float result = isNegative ? -sizeBytes : sizeBytes; MeasureUnit units = MeasureUnit.BYTE; diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 419b7b24fddb..3a4bfd686333 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2718,25 +2718,20 @@ public class WebView extends AbsoluteLayout * {@code IFRAME}, in which case it would be treated the same way as multiple forms described * above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the * {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node. - * <li>If the Android SDK provides a similar View, then should be set with the - * fully-qualified class name of such view. * <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to - * {@link ViewStructure#setAutofillHints(String[])}. - * <li>The {@code type} attribute of {@code INPUT} tags maps to - * {@link ViewStructure#setInputType(int)}. - * <li>The {@code value} attribute of {@code INPUT} tags maps to - * {@link ViewStructure#setText(CharSequence)}. - * <li>If the view is editalbe, the {@link ViewStructure#setAutofillType(int)} and + * {@link ViewStructure#setAutofillHints(String[])}. + * <li>If the view is editable, the {@link ViewStructure#setAutofillType(int)} and * {@link ViewStructure#setAutofillValue(AutofillValue)} must be set. * <li>The {@code placeholder} attribute maps to {@link ViewStructure#setHint(CharSequence)}. * <li>Other HTML attributes can be represented through * {@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}. * </ol> * - * <p>It should also call {@code structure.setDataIsSensitive(false)} for fields whose value - * were not dynamically changed (for example, through Javascript). + * <p>If the WebView implementation can determine that the value of a field was set statically + * (for example, not through Javascript), it should also call + * {@code structure.setDataIsSensitive(false)}. * - * <p>Example1: an HTML form with 2 fields for username and password. + * <p>For example, an HTML form with 2 fields for username and password: * * <pre class="prettyprint"> * <input type="text" name="username" id="user" value="Type your username" autocomplete="username" placeholder="Email or username"> @@ -2749,51 +2744,27 @@ public class WebView extends AbsoluteLayout * int index = structure.addChildCount(2); * ViewStructure username = structure.newChild(index); * username.setAutofillId(structure.getAutofillId(), 1); // id 1 - first child - * username.setClassName("input"); - * username.setInputType("android.widget.EditText"); * username.setAutofillHints("username"); * username.setHtmlInfo(username.newHtmlInfoBuilder("input") * .addAttribute("type", "text") * .addAttribute("name", "username") - * .addAttribute("id", "user") * .build()); * username.setHint("Email or username"); * username.setAutofillType(View.AUTOFILL_TYPE_TEXT); * username.setAutofillValue(AutofillValue.forText("Type your username")); - * username.setText("Type your username"); - * // Value of the field is not sensitive because it was not dynamically changed: + * // Value of the field is not sensitive because it was created statically and not changed. * username.setDataIsSensitive(false); * * ViewStructure password = structure.newChild(index + 1); * username.setAutofillId(structure, 2); // id 2 - second child - * password.setInputType("android.widget.EditText"); - * password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); * password.setAutofillHints("current-password"); * password.setHtmlInfo(password.newHtmlInfoBuilder("input") * .addAttribute("type", "password") * .addAttribute("name", "password") - * .addAttribute("id", "pass") * .build()); * password.setHint("Password"); * password.setAutofillType(View.AUTOFILL_TYPE_TEXT); * </pre> - * - * <p>Example2: an IFRAME tag. - * - * <pre class="prettyprint"> - * <iframe src="https://example.com/login"/> - * </pre> - * - * <p>Would map to: - * - * <pre class="prettyprint"> - * int index = structure.addChildCount(1); - * ViewStructure iframe = structure.newChildFor(index); - * iframe.setAutofillId(structure.getAutofillId(), 1); - * iframe.setHtmlInfo(iframe.newHtmlInfoBuilder("iframe") - * .addAttribute("src", "https://example.com/login") - * .build()); - * </pre> */ @Override public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { diff --git a/core/proto/android/app/window_configuration.proto b/core/proto/android/app/window_configuration.proto new file mode 100644 index 000000000000..03910df09194 --- /dev/null +++ b/core/proto/android/app/window_configuration.proto @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 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. + */ + +syntax = "proto3"; + +option java_package = "android.app"; +option java_multiple_files = true; + +package android.app; + +import "frameworks/base/core/proto/android/graphics/rect.proto"; + +/** Proto representation for WindowConfiguration.java class. */ +message WindowConfigurationProto { + .android.graphics.RectProto app_bounds = 1; + int32 windowing_mode = 2; + int32 activity_type = 3; +} diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto index f46b73afa750..804e0b489c0d 100644 --- a/core/proto/android/content/configuration.proto +++ b/core/proto/android/content/configuration.proto @@ -21,6 +21,7 @@ option java_multiple_files = true; package android.content; +import "frameworks/base/core/proto/android/app/window_configuration.proto"; import "frameworks/base/core/proto/android/content/locale.proto"; /** @@ -43,5 +44,6 @@ message ConfigurationProto { uint32 screen_height_dp = 14; uint32 smallest_screen_width_dp = 15; uint32 density_dpi = 16; + .android.app.WindowConfigurationProto window_configuration = 17; } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index ee8a6dc452b7..d177f1ca8104 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -16,6 +16,7 @@ syntax = "proto3"; +import "frameworks/base/core/proto/android/content/configuration.proto"; import "frameworks/base/core/proto/android/graphics/rect.proto"; import "frameworks/base/core/proto/android/view/displayinfo.proto"; import "frameworks/base/core/proto/android/view/windowlayoutparams.proto"; @@ -26,17 +27,23 @@ option java_multiple_files = true; message WindowManagerServiceProto { WindowManagerPolicyProto policy = 1; - /* window manager hierarchy structure */ + /* window hierarchy root */ + RootWindowContainerProto root_window_container = 2; + IdentifierProto focused_window = 3; + string focused_app = 4; + IdentifierProto input_method_window = 5; + bool display_frozen = 6; + int32 rotation = 7; + int32 last_orientation = 8; + AppTransitionProto app_transition = 9; +} + +/* represents DisplayContent */ +message RootWindowContainerProto { + WindowContainerProto window_container = 1; repeated DisplayProto displays = 2; /* window references in top down z order */ repeated IdentifierProto windows = 3; - IdentifierProto focused_window = 4; - string focused_app = 5; - IdentifierProto input_method_window = 6; - bool display_frozen = 7; - int32 rotation = 8; - int32 last_orientation = 9; - AppTransitionProto app_transition = 10; } /* represents PhoneWindowManager */ @@ -81,18 +88,19 @@ message AppTransitionProto { /* represents DisplayContent */ message DisplayProto { - int32 id = 1; - repeated StackProto stacks = 2; - DockedStackDividerControllerProto docked_stack_divider_controller = 3; - PinnedStackControllerProto pinned_stack_controller = 4; + WindowContainerProto window_container = 1; + int32 id = 2; + repeated StackProto stacks = 3; + DockedStackDividerControllerProto docked_stack_divider_controller = 4; + PinnedStackControllerProto pinned_stack_controller = 5; /* non app windows */ - repeated WindowTokenProto above_app_windows = 5; - repeated WindowTokenProto below_app_windows = 6; - repeated WindowTokenProto ime_windows = 7; - int32 dpi = 8; - .android.view.DisplayInfoProto display_info = 9; - int32 rotation = 10; - ScreenRotationAnimationProto screen_rotation_animation = 11; + repeated WindowTokenProto above_app_windows = 6; + repeated WindowTokenProto below_app_windows = 7; + repeated WindowTokenProto ime_windows = 8; + int32 dpi = 9; + .android.view.DisplayInfoProto display_info = 10; + int32 rotation = 11; + ScreenRotationAnimationProto screen_rotation_animation = 12; } @@ -109,20 +117,22 @@ message PinnedStackControllerProto { /* represents TaskStack */ message StackProto { - int32 id = 1; - repeated TaskProto tasks = 2; - bool fills_parent = 3; - .android.graphics.RectProto bounds = 4; - bool animation_background_surface_is_dimming = 5; + WindowContainerProto window_container = 1; + int32 id = 2; + repeated TaskProto tasks = 3; + bool fills_parent = 4; + .android.graphics.RectProto bounds = 5; + bool animation_background_surface_is_dimming = 6; } /* represents Task */ message TaskProto { - int32 id = 1; - repeated AppWindowTokenProto app_window_tokens = 2; - bool fills_parent = 3; - .android.graphics.RectProto bounds = 4; - .android.graphics.RectProto temp_inset_bounds = 5; + WindowContainerProto window_container = 1; + int32 id = 2; + repeated AppWindowTokenProto app_window_tokens = 3; + bool fills_parent = 4; + .android.graphics.RectProto bounds = 5; + .android.graphics.RectProto temp_inset_bounds = 6; } /* represents AppWindowToken */ @@ -134,26 +144,28 @@ message AppWindowTokenProto { /* represents WindowToken */ message WindowTokenProto { - int32 hash_code = 1; - repeated WindowStateProto windows = 2; + WindowContainerProto window_container = 1; + int32 hash_code = 2; + repeated WindowStateProto windows = 3; } /* represents WindowState */ message WindowStateProto { - IdentifierProto identifier = 1; - int32 display_id = 2; - int32 stack_id = 3; - .android.view.WindowLayoutParamsProto attributes = 4; - .android.graphics.RectProto given_content_insets = 5; - .android.graphics.RectProto frame = 6; - .android.graphics.RectProto containing_frame = 7; - .android.graphics.RectProto parent_frame = 8; - .android.graphics.RectProto content_frame = 9; - .android.graphics.RectProto content_insets = 10; - .android.graphics.RectProto surface_insets = 11; - WindowStateAnimatorProto animator = 12; - bool animating_exit = 13; - repeated WindowStateProto child_windows = 14; + WindowContainerProto window_container = 1; + IdentifierProto identifier = 2; + int32 display_id = 3; + int32 stack_id = 4; + .android.view.WindowLayoutParamsProto attributes = 5; + .android.graphics.RectProto given_content_insets = 6; + .android.graphics.RectProto frame = 7; + .android.graphics.RectProto containing_frame = 8; + .android.graphics.RectProto parent_frame = 9; + .android.graphics.RectProto content_frame = 10; + .android.graphics.RectProto content_insets = 11; + .android.graphics.RectProto surface_insets = 12; + WindowStateAnimatorProto animator = 13; + bool animating_exit = 14; + repeated WindowStateProto child_windows = 15; } message IdentifierProto { @@ -178,4 +190,17 @@ message WindowSurfaceControllerProto { message ScreenRotationAnimationProto { bool started = 1; bool animation_running = 2; -}
\ No newline at end of file +} + +/* represents WindowContainer */ +message WindowContainerProto { + ConfigurationContainerProto configuration_container = 1; + int32 orientation = 2; +} + +/* represents ConfigurationContainer */ +message ConfigurationContainerProto { + .android.content.ConfigurationProto override_configuration = 1; + .android.content.ConfigurationProto full_configuration = 2; + .android.content.ConfigurationProto merged_override_configuration = 3; +} diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index bd5b7116c435..7416113b4e90 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3785,15 +3785,18 @@ a headphone or other wired audio output jack. [CHAR LIMIT=50] --> <string name="default_audio_route_name" product="default">Phone</string> + <!-- Name of the default audio route when an audio dock is connected. [CHAR LIMIT=50] --> + <string name="default_audio_route_name_dock_speakers">Dock speakers</string> + + <!-- Name of the default audio route when HDMI is connected. [CHAR LIMIT=50] --> + <string name="default_audio_route_name_hdmi">HDMI</string> + <!-- Name of the default audio route when wired headphones are connected. [CHAR LIMIT=50] --> <string name="default_audio_route_name_headphones">Headphones</string> - <!-- Name of the default audio route when an audio dock is connected. [CHAR LIMIT=50] --> - <string name="default_audio_route_name_dock_speakers">Dock speakers</string> - - <!-- Name of the default media route when HDMI is connected. [CHAR LIMIT=50] --> - <string name="default_media_route_name_hdmi">HDMI</string> + <!-- Name of the default audio route when USB is connected. [CHAR LIMIT=50] --> + <string name="default_audio_route_name_usb">USB</string> <!-- Name of the default audio route category. [CHAR LIMIT=50] --> <string name="default_audio_route_category_name">System</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 435b122bf744..c4a45ee11098 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1025,9 +1025,10 @@ <java-symbol type="string" name="granularity_label_link" /> <java-symbol type="string" name="granularity_label_line" /> <java-symbol type="string" name="default_audio_route_name" /> - <java-symbol type="string" name="default_audio_route_name_headphones" /> <java-symbol type="string" name="default_audio_route_name_dock_speakers" /> - <java-symbol type="string" name="default_media_route_name_hdmi" /> + <java-symbol type="string" name="default_audio_route_name_hdmi" /> + <java-symbol type="string" name="default_audio_route_name_headphones" /> + <java-symbol type="string" name="default_audio_route_name_usb" /> <java-symbol type="string" name="default_audio_route_category_name" /> <java-symbol type="string" name="stk_cc_ss_to_dial" /> <java-symbol type="string" name="stk_cc_ss_to_ss" /> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 0a89b7409250..b8fa06c6e1c3 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -871,7 +871,7 @@ public class PackageManagerTests extends AndroidTestCase { receiver = new InstallReceiver(ip.pkg.packageName); } try { - invokeInstallPackage(ip.packageURI, flags, receiver, replace); + invokeInstallPackage(ip.packageURI, flags, receiver, true); if (replace) { assertInstall(ip.pkg, flags, ip.pkg.installLocation); } @@ -881,7 +881,7 @@ public class PackageManagerTests extends AndroidTestCase { } @LargeTest - public void testReplaceFailNormalInternal() throws Exception { + public void testReplaceFlagDoesNotNeedToBeSet() throws Exception { sampleReplaceFromRawResource(0); } diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java index ff75c29cc112..24e3646e02fc 100644 --- a/core/tests/coretests/src/android/text/format/FormatterTest.java +++ b/core/tests/coretests/src/android/text/format/FormatterTest.java @@ -17,10 +17,12 @@ package android.text.format; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.icu.util.MeasureUnit; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -32,6 +34,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Locale; +import java.util.Set; + @SmallTest @RunWith(AndroidJUnit4.class) @@ -205,4 +209,24 @@ public class FormatterTest { Locale.setDefault(locale); } + + /** + * Verifies that Formatter doesn't "leak" the locally defined petabyte unit into ICU via the + * {@link MeasureUnit} registry. This test can fail for two reasons: + * 1. we regressed and started leaking again. In this case the code needs to be fixed. + * 2. ICU started supporting petabyte as a unit, in which case change one needs to revert this + * change (I494fb59a3b3742f35cbdf6b8705817f404a2c6b0), remove Formatter.PETABYTE and replace any + * usages of that field with just MeasureUnit.PETABYTE. + */ + // http://b/65632959 + @Test + public void doesNotLeakPetabyte() { + // Ensure that the Formatter class is loaded when we call .getAvailable(). + Formatter.formatFileSize(mContext, Long.MAX_VALUE); + Set<MeasureUnit> digitalUnits = MeasureUnit.getAvailable("digital"); + for (MeasureUnit unit : digitalUnits) { + // This assert can fail if we don't leak PETABYTE, but ICU has added it, see #2 above. + assertNotEquals("petabyte", unit.getSubtype()); + } + } } diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 2894e8956c1c..fe427a73bf96 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -193,7 +193,9 @@ public class MediaRouter { } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { name = com.android.internal.R.string.default_audio_route_name_dock_speakers; } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) { - name = com.android.internal.R.string.default_media_route_name_hdmi; + name = com.android.internal.R.string.default_audio_route_name_hdmi; + } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_USB) != 0) { + name = com.android.internal.R.string.default_audio_route_name_usb; } else { name = com.android.internal.R.string.default_audio_route_name; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 7fe7f399f123..8fa904e10b4a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -2024,14 +2024,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - public int getMinHeight() { - if (mGuts != null && mGuts.isExposed()) { + public int getMinHeight(boolean ignoreTemporaryStates) { + if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) { return mGuts.getIntrinsicHeight(); - } else if (isHeadsUpAllowed() && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) { + } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp + && mHeadsUpManager.isTrackingHeadsUp()) { return getPinnedHeadsUpHeight(false /* atLeastMinHeight */); } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) { return mChildrenContainer.getMinHeight(); - } else if (isHeadsUpAllowed() && mIsHeadsUp) { + } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) { return mHeadsUpHeight; } NotificationContentView showingLayout = getShowingLayout(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index efe5e0c2ade4..aac9af8a0234 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -151,9 +151,21 @@ public abstract class ExpandableView extends FrameLayout { } /** - * @return The minimum content height of this notification. + * @return The minimum content height of this notification. This also respects the temporary + * states of the view. */ public int getMinHeight() { + return getMinHeight(false /* ignoreTemporaryStates */); + } + + /** + * Get the minimum height of this view. + * + * @param ignoreTemporaryStates should temporary states be ignored like the guts or heads-up. + * + * @return The minimum height that this view needs. + */ + public int getMinHeight(boolean ignoreTemporaryStates) { return getHeight(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index ddc7dd063d4e..d0417b59448d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -292,8 +292,8 @@ public class NotificationData { if (mRankingMap != null) { // RankingMap as received from NoMan - mRankingMap.getRanking(a.key, mRankingA); - mRankingMap.getRanking(b.key, mRankingB); + getRanking(a.key, mRankingA); + getRanking(b.key, mRankingB); aImportance = mRankingA.getImportance(); bImportance = mRankingB.getImportance(); aRank = mRankingA.getRank(); @@ -381,7 +381,7 @@ public class NotificationData { public boolean isAmbient(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.isAmbient(); } return false; @@ -389,7 +389,7 @@ public class NotificationData { public int getVisibilityOverride(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getVisibilityOverride(); } return Ranking.VISIBILITY_NO_OVERRIDE; @@ -397,7 +397,7 @@ public class NotificationData { public boolean shouldSuppressScreenOff(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return (mTmpRanking.getSuppressedVisualEffects() & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0; } @@ -406,7 +406,7 @@ public class NotificationData { public boolean shouldSuppressScreenOn(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return (mTmpRanking.getSuppressedVisualEffects() & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON) != 0; } @@ -415,7 +415,7 @@ public class NotificationData { public int getImportance(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getImportance(); } return NotificationManager.IMPORTANCE_UNSPECIFIED; @@ -423,7 +423,7 @@ public class NotificationData { public String getOverrideGroupKey(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getOverrideGroupKey(); } return null; @@ -431,7 +431,7 @@ public class NotificationData { public List<SnoozeCriterion> getSnoozeCriteria(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getSnoozeCriteria(); } return null; @@ -439,7 +439,7 @@ public class NotificationData { public NotificationChannel getChannel(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getChannel(); } return null; @@ -452,6 +452,9 @@ public class NotificationData { final int N = mEntries.size(); for (int i = 0; i < N; i++) { Entry entry = mEntries.valueAt(i); + if (!getRanking(entry.key, mTmpRanking)) { + continue; + } final StatusBarNotification oldSbn = entry.notification.cloneLight(); final String overrideGroupKey = getOverrideGroupKey(entry.key); if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) { @@ -466,6 +469,19 @@ public class NotificationData { filterAndSort(); } + /** + * Get the ranking from the current ranking map. + * + * @param key the key to look up + * @param outRanking the ranking to populate + * + * @return {@code true} if the ranking was properly obtained. + */ + @VisibleForTesting + protected boolean getRanking(String key, Ranking outRanking) { + return mRankingMap.getRanking(key, outRanking); + } + // TODO: This should not be public. Instead the Environment should notify this class when // anything changed, and this class should call back the UI so it updates itself. public void filterAndSort() { @@ -573,7 +589,7 @@ public class NotificationData { } private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) { - mRankingMap.getRanking(e.key, mTmpRanking); + getRanking(e.key, mTmpRanking); pw.print(indent); pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); StatusBarNotification n = e.notification; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index 1f44abea755a..4bca79715422 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -50,7 +50,7 @@ public class BarTransitions { public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6; public static final int LIGHTS_IN_DURATION = 250; - public static final int LIGHTS_OUT_DURATION = 750; + public static final int LIGHTS_OUT_DURATION = 1500; public static final int BACKGROUND_DURATION = 200; private final String mTag; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 078e8189f5b3..c19161839998 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -509,7 +509,8 @@ public class NotificationPanelView extends PanelView implements if (row.isRemoved()) { continue; } - availableSpace -= child.getMinHeight() + notificationPadding; + availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */) + + notificationPadding; if (availableSpace >= 0 && count < maximum) { count++; } else if (availableSpace > -shelfSize) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index efc8d8b97114..200cada972a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -339,7 +339,7 @@ public class StatusBar extends SystemUI implements DemoMode, private static final int STATUS_OR_NAV_TRANSIENT = View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; - private static final long AUTOHIDE_TIMEOUT_MS = 3000; + private static final long AUTOHIDE_TIMEOUT_MS = 2250; /** The minimum delay in ms between reports of notification visibility. */ private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; @@ -3321,7 +3321,6 @@ public class StatusBar extends SystemUI implements DemoMode, } else { cancelAutohide(); } - touchAutoDim(); } protected int computeStatusBarMode(int oldVal, int newVal) { @@ -3404,10 +3403,10 @@ public class StatusBar extends SystemUI implements DemoMode, } // manually dismiss the volume panel when interacting with the nav bar if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) { + touchAutoDim(); dismissVolumeDialog(); } checkBarModes(); - touchAutoDim(); } private void dismissVolumeDialog() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java index 18c5756b3fb6..972eddb46901 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java @@ -135,5 +135,11 @@ public class NotificationDataTest extends SysuiTestCase { public NotificationChannel getChannel(String key) { return new NotificationChannel(null, null, 0); } + + @Override + protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) { + super.getRanking(key, outRanking); + return true; + } } } diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 141f9207b314..c203dff07e89 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -77,6 +77,7 @@ import android.os.RemoteException; import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; @@ -149,6 +150,7 @@ import java.util.Queue; import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; public class RefactoredBackupManagerService implements BackupManagerServiceInterface { @@ -546,9 +548,12 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter @Override public void onUnlockUser(int userId) { if (userId == UserHandle.USER_SYSTEM) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init"); sInstance.initialize(userId); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // Migrate legacy setting + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate"); if (!backupSettingMigrated(userId)) { if (DEBUG) { Slog.i(TAG, "Backup enable apparently not migrated"); @@ -569,12 +574,15 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); try { sInstance.setBackupEnabled(readBackupEnableState(userId)); } catch (RemoteException e) { // can't happen; it's a local object } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } } } @@ -619,6 +627,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter private final SparseArray<Operation> mCurrentOperations = new SparseArray<>(); private final Object mCurrentOpLock = new Object(); private final Random mTokenGenerator = new Random(); + final AtomicInteger mNextToken = new AtomicInteger(); private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>(); @@ -658,15 +667,15 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter @GuardedBy("mQueueLock") private ArrayList<FullBackupEntry> mFullBackupQueue; - // Utility: build a new random integer token + // Utility: build a new random integer token. The low bits are the ordinal of the + // operation for near-time uniqueness, and the upper bits are random for app- + // side unpredictability. @Override public int generateRandomIntegerToken() { - int token; - do { - synchronized (mTokenGenerator) { - token = mTokenGenerator.nextInt(); - } - } while (token < 0); + int token = mTokenGenerator.nextInt(); + if (token < 0) token = -token; + token &= ~0xFF; + token |= (mNextToken.incrementAndGet() & 0xFF); return token; } @@ -1507,7 +1516,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter long token = mAncestralToken; synchronized (mQueueLock) { - if (mProcessedPackagesJournal.hasBeenProcessed(packageName)) { + if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) { if (MORE_DEBUG) { Slog.i(TAG, "App in ever-stored, so using current token"); } @@ -1759,8 +1768,12 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be // called after we receive cancel here. We need this op's state there. - // Remove all pending timeout messages for this operation type. - mBackupHandler.removeMessages(getMessageIdForOperationType(op.type)); + // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and + // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and + // doesn't require cancellation. + if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) { + mBackupHandler.removeMessages(getMessageIdForOperationType(op.type)); + } } mCurrentOpLock.notifyAll(); } @@ -2108,14 +2121,26 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // so tear down any ongoing backup task right away. @Override public void endFullBackup() { - synchronized (mQueueLock) { - if (mRunningFullBackupTask != null) { - if (DEBUG_SCHEDULING) { - Slog.i(TAG, "Telling running backup to stop"); + // offload the mRunningFullBackupTask.handleCancel() call to another thread, + // as we might have to wait for mCancelLock + Runnable endFullBackupRunnable = new Runnable() { + @Override + public void run() { + PerformFullTransportBackupTask pftbt = null; + synchronized (mQueueLock) { + if (mRunningFullBackupTask != null) { + pftbt = mRunningFullBackupTask; + } + } + if (pftbt != null) { + if (DEBUG_SCHEDULING) { + Slog.i(TAG, "Telling running backup to stop"); + } + pftbt.handleCancel(true); } - mRunningFullBackupTask.handleCancel(true); } - } + }; + new Thread(endFullBackupRunnable, "end-full-backup").start(); } // Used by both incremental and full restore @@ -2800,8 +2825,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter final long oldId = Binder.clearCallingIdentity(); try { String prevTransport = mTransportManager.selectTransport(transport); - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.BACKUP_TRANSPORT, transport); + updateStateForTransport(transport); Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName() + " returning " + prevTransport); return prevTransport; @@ -2826,9 +2850,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter @Override public void onSuccess(String transportName) { mTransportManager.selectTransport(transportName); - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.BACKUP_TRANSPORT, - mTransportManager.getCurrentTransportName()); + updateStateForTransport(mTransportManager.getCurrentTransportName()); Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString()); try { @@ -2853,6 +2875,28 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter Binder.restoreCallingIdentity(oldId); } + private void updateStateForTransport(String newTransportName) { + // Publish the name change + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.BACKUP_TRANSPORT, newTransportName); + + // And update our current-dataset bookkeeping + IBackupTransport transport = mTransportManager.getTransportBinder(newTransportName); + if (transport != null) { + try { + mCurrentToken = transport.getCurrentRestoreSet(); + } catch (Exception e) { + // Oops. We can't know the current dataset token, so reset and figure it out + // when we do the next k/v backup operation on this transport. + mCurrentToken = 0; + } + } else { + // The named transport isn't bound at this particular moment, so we can't + // know yet what its current dataset token is. Reset as above. + mCurrentToken = 0; + } + } + // Supply the configuration Intent for the given transport. If the name is not one // of the available transports, or if the transport does not supply any configuration // UI, the method returns null. diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java index ce4f906ecdec..6bbed8cbfdf9 100644 --- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java +++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java @@ -227,9 +227,8 @@ public class PerformBackupTask implements BackupRestoreTask { if (!mFinished) { finalizeBackup(); } else { - Slog.e(TAG, "Duplicate finish"); + Slog.e(TAG, "Duplicate finish of K/V pass"); } - mFinished = true; break; } } @@ -609,6 +608,7 @@ public class PerformBackupTask implements BackupRestoreTask { break; } } + mFinished = true; Slog.i(TAG, "K/V backup pass finished."); // Only once we're entirely finished do we release the wakelock for k/v backup. backupManagerService.getWakelock().release(); diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 21d5dc21c196..283a1f0e93dd 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -779,6 +779,9 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end private void restoreFinished() { + if (DEBUG) { + Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName); + } try { backupManagerService .prepareOperationTimeout(mEphemeralOpToken, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 02eb3b432dfe..c8e2dc57e0ed 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -682,11 +682,6 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityInfo mLastAddedTaskActivity; /** - * List of packages whitelisted by DevicePolicyManager for locktask. Indexed by userId. - */ - SparseArray<String[]> mLockTaskPackages = new SparseArray<>(); - - /** * The package name of the DeviceOwner. This package is not permitted to have its data cleared. */ String mDeviceOwnerName; @@ -10882,7 +10877,6 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void updateLockTaskPackages(int userId, String[] packages) { - // TODO: move this into LockTaskController final int callingUid = Binder.getCallingUid(); if (callingUid != 0 && callingUid != SYSTEM_UID) { enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES, @@ -10891,8 +10885,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" + Arrays.toString(packages)); - mLockTaskPackages.put(userId, packages); - mLockTaskController.onLockTaskPackagesUpdated(); + mLockTaskController.updateLockTaskPackages(userId, packages); } } @@ -13982,10 +13975,10 @@ public class ActivityManagerService extends IActivityManager.Stub mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt( resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; - final boolean supportsPictureInPicture = - mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow(mContext); + final boolean supportsPictureInPicture = supportsMultiWindow && + mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); final boolean supportsSplitScreenMultiWindow = ActivityManager.supportsSplitScreenMultiWindow(mContext); final boolean supportsMultiDisplay = mContext.getPackageManager() diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index fe28956d6923..c8a2a230a7e1 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -166,7 +166,6 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -1639,6 +1638,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } + // Check if caller is already present on display + final boolean uidPresentOnDisplay = activityDisplay.isUidPresent(callingUid); + final int displayOwnerUid = activityDisplay.mDisplay.getOwnerUid(); if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID && displayOwnerUid != aInfo.applicationInfo.uid) { @@ -1651,7 +1653,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } // Check if the caller is allowed to embed activities from other apps. if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid) - == PERMISSION_DENIED) { + == PERMISSION_DENIED && !uidPresentOnDisplay) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" + " disallow activity embedding without permission."); return false; @@ -1672,8 +1674,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - // Check if caller is present on display - if (activityDisplay.isUidPresent(callingUid)) { + if (uidPresentOnDisplay) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" + " allow launch for caller present on the display"); return true; @@ -3556,15 +3557,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser); pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront); pw.print(prefix); pw.println("mStacks=" + mStacks); - // TODO: move this to LockTaskController - final SparseArray<String[]> packages = mService.mLockTaskPackages; - if (packages.size() > 0) { - pw.print(prefix); pw.println("mLockTaskPackages (userId:packages)="); - for (int i = 0; i < packages.size(); ++i) { - pw.print(prefix); pw.print(prefix); pw.print(packages.keyAt(i)); - pw.print(":"); pw.println(Arrays.toString(packages.valueAt(i))); - } - } if (!mWaitingForActivityVisible.isEmpty()) { pw.print(prefix); pw.println("mWaitingForActivityVisible="); for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) { diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java index d8706bcc5e35..241e58391144 100644 --- a/services/core/java/com/android/server/am/LockTaskController.java +++ b/services/core/java/com/android/server/am/LockTaskController.java @@ -31,6 +31,7 @@ import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_CURRENT; import static android.provider.Settings.Secure.LOCK_TO_APP_EXIT_LOCKED; import static android.view.Display.DEFAULT_DISPLAY; + import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; @@ -55,7 +56,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.util.Slog; +import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; @@ -65,6 +68,7 @@ import com.android.server.wm.WindowManagerService; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; /** * Helper class that deals with all things related to task locking. This includes the screen pinning @@ -123,6 +127,11 @@ public class LockTaskController { private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>(); /** + * Packages that are allowed to be launched into the lock task mode for each user. + */ + private final SparseArray<String[]> mLockTaskPackages = new SparseArray<>(); + + /** * Store the current lock task mode. Possible values: * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, * {@link ActivityManager#LOCK_TASK_MODE_PINNED} @@ -159,8 +168,8 @@ public class LockTaskController { } /** - * @return whether the given task can be moved to the back of the stack. Locked tasks cannot be - * moved to the back of the stack. + * @return whether the given task is locked at the moment. Locked tasks cannot be moved to the + * back of the stack. */ boolean checkLockedTask(TaskRecord task) { if (mLockTaskModeTasks.contains(task)) { @@ -452,29 +461,35 @@ public class LockTaskController { } /** - * Called when the list of packages whitelisted for lock task mode is changed. Any currently - * locked tasks that got removed from the whitelist will be finished. + * Update packages that are allowed to be launched in lock task mode. + * @param userId Which user this whitelist is associated with + * @param packages The whitelist of packages allowed in lock task mode + * @see #mLockTaskPackages */ - // TODO: Missing unit tests - void onLockTaskPackagesUpdated() { - boolean didSomething = false; + void updateLockTaskPackages(int userId, String[] packages) { + mLockTaskPackages.put(userId, packages); + + boolean taskChanged = false; for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx); - final boolean wasWhitelisted = - (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) || - (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED); + final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; lockedTask.setLockTaskAuth(); - final boolean isWhitelisted = - (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) || - (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED); - if (wasWhitelisted && !isWhitelisted) { - // Lost whitelisting authorization. End it now. - if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + - lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); - removeLockedTask(lockedTask); - lockedTask.performClearTaskLocked(); - didSomething = true; + final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + + if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED + || lockedTask.userId != userId + || !wasWhitelisted || isWhitelisted) { + continue; } + + // Terminate locked tasks that have recently lost whitelist authorization. + if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + + lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); + removeLockedTask(lockedTask); + lockedTask.performClearTaskLocked(); + taskChanged = true; } for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) { @@ -484,22 +499,40 @@ public class LockTaskController { stack.onLockTaskPackagesUpdatedLocked(); } } + final ActivityRecord r = mSupervisor.topRunningActivityLocked(); - final TaskRecord task = r != null ? r.getTask() : null; - if (mLockTaskModeTasks.isEmpty() && task != null + final TaskRecord task = (r != null) ? r.getTask() : null; + if (mLockTaskModeTasks.isEmpty() && task!= null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { // This task must have just been authorized. if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: starting new locktask task=" + task); - setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", - false); - didSomething = true; + setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false); + taskChanged = true; } - if (didSomething) { + + if (taskChanged) { mSupervisor.resumeFocusedStackTopActivityLocked(); } } + boolean isPackageWhitelisted(int userId, String pkg) { + if (pkg == null) { + return false; + } + String[] whitelist; + whitelist = mLockTaskPackages.get(userId); + if (whitelist == null) { + return false; + } + for (String whitelistedPkg : whitelist) { + if (pkg.equals(whitelistedPkg)) { + return true; + } + } + return false; + } + /** * @return the topmost locked task */ @@ -556,8 +589,18 @@ public class LockTaskController { } public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("mLockTaskModeState=" + lockTaskModeToString()); - pw.println(" mLockTaskModeTasks" + mLockTaskModeTasks); + pw.println(prefix + "LockTaskController"); + prefix = prefix + " "; + pw.println(prefix + "mLockTaskModeState=" + lockTaskModeToString()); + pw.println(prefix + "mLockTaskModeTasks="); + for (int i = 0; i < mLockTaskModeTasks.size(); ++i) { + pw.println(prefix + " #" + i + " " + mLockTaskModeTasks.get(i)); + } + pw.println(prefix + "mLockTaskPackages (userId:packages)="); + for (int i = 0; i < mLockTaskPackages.size(); ++i) { + pw.println(prefix + " u" + mLockTaskPackages.keyAt(i) + + ":" + Arrays.toString(mLockTaskPackages.valueAt(i))); + } } private String lockTaskModeToString() { diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 0e651845df8d..48da6555e75c 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -78,7 +78,6 @@ import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityManager; -import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -857,8 +856,13 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } mResizeMode = info.resizeMode; mSupportsPictureInPicture = info.supportsPictureInPicture(); - mLockTaskMode = info.lockTaskLaunchMode; mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0; + mLockTaskMode = info.lockTaskLaunchMode; + if (!mPrivileged && (mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS + || mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { + // Non-priv apps are not allowed to use always or never, fall back to default + mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; + } setLockTaskAuth(); } @@ -1396,16 +1400,11 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } void setLockTaskAuth() { - if (!mPrivileged && - (mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS || - mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { - // Non-priv apps are not allowed to use always or never, fall back to default - mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; - } + final String pkg = (realActivity != null) ? realActivity.getPackageName() : null; switch (mLockTaskMode) { case LOCK_TASK_LAUNCH_MODE_DEFAULT: - mLockTaskAuth = isLockTaskWhitelistedLocked() ? - LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; + mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg) + ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; break; case LOCK_TASK_LAUNCH_MODE_NEVER: @@ -1417,31 +1416,14 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi break; case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - mLockTaskAuth = isLockTaskWhitelistedLocked() ? - LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; + mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg) + ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; break; } if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this + " mLockTaskAuth=" + lockTaskAuthToString()); } - private boolean isLockTaskWhitelistedLocked() { - String pkg = (realActivity != null) ? realActivity.getPackageName() : null; - if (pkg == null) { - return false; - } - String[] packages = mService.mLockTaskPackages.get(userId); - if (packages == null) { - return false; - } - for (int i = packages.length - 1; i >= 0; --i) { - if (pkg.equals(packages[i])) { - return true; - } - } - return false; - } - boolean isOverHomeStack() { return mTaskToReturnTo == ACTIVITY_TYPE_HOME; } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 930e4f098f5f..1f03e666d072 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -1441,7 +1441,7 @@ class PackageManagerShellCommand extends ShellCommand { out = session.openWrite(splitName, 0, sizeBytes); int total = 0; - byte[] buffer = new byte[65536]; + byte[] buffer = new byte[1024 * 1024]; int c; while ((c = in.read(buffer)) != -1) { total += c; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index a806af46d021..1d7f66f54faf 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3627,10 +3627,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { return -1; } - // If the device is in Vr mode, drop the volume keys and don't - // forward it to the application/dispatch the audio event. + // If the device is in VR mode and keys are "internal" (e.g. on the side of the + // device), then drop the volume keys and don't forward it to the application/dispatch + // the audio event. if (mPersistentVrModeEnabled) { - return -1; + final InputDevice d = event.getDevice(); + if (d != null && !d.isExternal()) { + return -1; + } } } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) { // Pass through keyboard navigation keys. diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 38dc33fa4dbc..f10bf1a3ffb5 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -97,12 +97,58 @@ public class StatusBarManagerService extends IStatusBarService.Stub { int what2; IBinder token; + public DisableRecord(int userId, IBinder token) { + this.userId = userId; + this.token = token; + try { + token.linkToDeath(this, 0); + } catch (RemoteException re) { + // Give up + } + } + + @Override public void binderDied() { Slog.i(TAG, "binder died for pkg=" + pkg); disableForUser(0, token, pkg, userId); disable2ForUser(0, token, pkg, userId); token.unlinkToDeath(this, 0); } + + public void setFlags(int what, int which, String pkg) { + switch (which) { + case 1: + what1 = what; + return; + case 2: + what2 = what; + return; + default: + Slog.w(TAG, "Can't set unsupported disable flag " + which + + ": 0x" + Integer.toHexString(what)); + } + this.pkg = pkg; + } + + public int getFlags(int which) { + switch (which) { + case 1: return what1; + case 2: return what2; + default: + Slog.w(TAG, "Can't get unsupported disable flag " + which); + return 0; + } + } + + public boolean isEmpty() { + return what1 == 0 && what2 == 0; + } + + @Override + public String toString() { + return String.format("userId=%d what1=0x%08X what2=0x%08X pkg=%s token=%s", + userId, what1, what2, pkg, token); + } } /** @@ -970,42 +1016,42 @@ public class StatusBarManagerService extends IStatusBarService.Stub { Slog.d(TAG, "manageDisableList userId=" + userId + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg); } - // update the list + + // Find matching record, if any final int N = mDisableRecords.size(); - DisableRecord tok = null; + DisableRecord record = null; int i; - for (i=0; i<N; i++) { - DisableRecord t = mDisableRecords.get(i); - if (t.token == token && t.userId == userId) { - tok = t; + for (i = 0; i < N; i++) { + DisableRecord r = mDisableRecords.get(i); + if (r.token == token && r.userId == userId) { + record = r; break; } } - if (what == 0 || !token.isBinderAlive()) { - if (tok != null) { + + // Remove record if binder is already dead + if (!token.isBinderAlive()) { + if (record != null) { mDisableRecords.remove(i); - tok.token.unlinkToDeath(tok, 0); - } - } else { - if (tok == null) { - tok = new DisableRecord(); - tok.userId = userId; - try { - token.linkToDeath(tok, 0); - } - catch (RemoteException ex) { - return; // give up - } - mDisableRecords.add(tok); + record.token.unlinkToDeath(record, 0); } - if (which == 1) { - tok.what1 = what; - } else { - tok.what2 = what; + return; + } + + // Update existing record + if (record != null) { + record.setFlags(what, which, pkg); + if (record.isEmpty()) { + mDisableRecords.remove(i); + record.token.unlinkToDeath(record, 0); } - tok.token = token; - tok.pkg = pkg; + return; } + + // Record doesn't exist, so we create a new one + record = new DisableRecord(userId, token); + record.setFlags(what, which, pkg); + mDisableRecords.add(record); } // lock on mDisableRecords @@ -1016,7 +1062,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { for (int i=0; i<N; i++) { final DisableRecord rec = mDisableRecords.get(i); if (rec.userId == userId) { - net |= (which == 1) ? rec.what1 : rec.what2; + net |= rec.getFlags(which); } } return net; @@ -1036,11 +1082,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { pw.println(" mDisableRecords.size=" + N); for (int i=0; i<N; i++) { DisableRecord tok = mDisableRecords.get(i); - pw.println(" [" + i + "] userId=" + tok.userId - + " what1=0x" + Integer.toHexString(tok.what1) - + " what2=0x" + Integer.toHexString(tok.what2) - + " pkg=" + tok.pkg - + " token=" + tok.token); + pw.println(" [" + i + "] " + tok); } pw.println(" mCurrentUserId=" + mCurrentUserId); pw.println(" mIcons="); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index ea2f305989fe..2d1fc912fcfc 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -53,6 +53,7 @@ import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.proto.AppWindowTokenProto.NAME; import static com.android.server.wm.proto.AppWindowTokenProto.WINDOW_TOKEN; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.app.Activity; import android.content.res.Configuration; @@ -1620,8 +1621,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + @CallSuper @Override - void writeToProto(ProtoOutputStream proto, long fieldId) { + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); writeNameToProto(proto, NAME); super.writeToProto(proto, WINDOW_TOKEN); diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 28ba9b3db777..1da94da1b3b6 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -24,9 +24,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.activityTypeToString; +import static com.android.server.wm.proto.ConfigurationContainerProto.FULL_CONFIGURATION; +import static com.android.server.wm.proto.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION; +import static com.android.server.wm.proto.ConfigurationContainerProto.OVERRIDE_CONFIGURATION; +import android.annotation.CallSuper; import android.app.WindowConfiguration; import android.content.res.Configuration; +import android.util.proto.ProtoOutputStream; import java.util.ArrayList; @@ -252,6 +257,24 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { } } + /** + * Write to a protocol buffer output stream. Protocol buffer message definition is at + * {@link com.android.server.wm.proto.ConfigurationContainerProto}. + * + * @param protoOutputStream Stream to write the ConfigurationContainer object to. + * @param fieldId Field Id of the ConfigurationContainer as defined in the parent + * message. + * @hide + */ + @CallSuper + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + final long token = protoOutputStream.start(fieldId); + mOverrideConfiguration.writeToProto(protoOutputStream, OVERRIDE_CONFIGURATION); + mFullConfiguration.writeToProto(protoOutputStream, FULL_CONFIGURATION); + mMergedOverrideConfiguration.writeToProto(protoOutputStream, MERGED_OVERRIDE_CONFIGURATION); + protoOutputStream.end(token); + } + abstract protected int getChildCount(); abstract protected E getChildAt(int index); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 6cf608ab0591..817a01c5c8d7 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -112,7 +112,9 @@ import static com.android.server.wm.proto.DisplayProto.PINNED_STACK_CONTROLLER; import static com.android.server.wm.proto.DisplayProto.ROTATION; import static com.android.server.wm.proto.DisplayProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.proto.DisplayProto.STACKS; +import static com.android.server.wm.proto.DisplayProto.WINDOW_CONTAINER; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.app.ActivityManager.StackId; import android.app.WindowConfiguration; @@ -2119,8 +2121,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); proto.write(ID, mDisplayId); for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) { final TaskStack stack = mTaskStackContainers.get(stackNdx); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 8a749762f993..227e4b2b59af 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.annotation.CallSuper; import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.power.V1_0.PowerHint; @@ -89,8 +90,9 @@ import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN; import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING; import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE; -import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAYS; -import static com.android.server.wm.proto.WindowManagerServiceProto.WINDOWS; +import static com.android.server.wm.proto.RootWindowContainerProto.DISPLAYS; +import static com.android.server.wm.proto.RootWindowContainerProto.WINDOWS; +import static com.android.server.wm.proto.RootWindowContainerProto.WINDOW_CONTAINER; /** Root {@link WindowContainer} for the device. */ class RootWindowContainer extends WindowContainer<DisplayContent> { @@ -1077,7 +1079,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } } - void writeToProto(ProtoOutputStream proto) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); if (mService.mDisplayReady) { final int count = mChildren.size(); for (int i = 0; i < count; ++i) { @@ -1088,6 +1094,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { forAllWindows((w) -> { w.writeIdentifierToProto(proto, WINDOWS); }, true); + proto.end(token); } @Override diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 55b6c912ef90..7464a9b92d40 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -34,7 +34,9 @@ import static com.android.server.wm.proto.TaskProto.BOUNDS; import static com.android.server.wm.proto.TaskProto.FILLS_PARENT; import static com.android.server.wm.proto.TaskProto.ID; import static com.android.server.wm.proto.TaskProto.TEMP_INSET_BOUNDS; +import static com.android.server.wm.proto.TaskProto.WINDOW_CONTAINER; +import android.annotation.CallSuper; import android.app.ActivityManager.StackId; import android.app.ActivityManager.TaskDescription; import android.content.pm.ActivityInfo; @@ -735,8 +737,11 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU return "Task=" + mTaskId; } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); proto.write(ID, mTaskId); for (int i = mChildren.size() - 1; i >= 0; i--) { final AppWindowToken appWindowToken = mChildren.get(i); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 4664dcbbfa75..126d820e69b6 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -41,7 +41,9 @@ import static com.android.server.wm.proto.StackProto.BOUNDS; import static com.android.server.wm.proto.StackProto.FILLS_PARENT; import static com.android.server.wm.proto.StackProto.ID; import static com.android.server.wm.proto.StackProto.TASKS; +import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER; +import android.annotation.CallSuper; import android.app.ActivityManager.StackId; import android.content.res.Configuration; import android.graphics.Rect; @@ -1219,8 +1221,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye return mMinimizeAmount != 0f; } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); proto.write(ID, mStackId); for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) { mChildren.get(taskNdx).writeToProto(proto, TASKS); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 926719ddf318..40923c8216c0 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -19,12 +19,14 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.res.Configuration.EMPTY; +import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER; +import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION; import android.annotation.CallSuper; import android.content.res.Configuration; import android.util.Pools; +import android.util.proto.ProtoOutputStream; import com.android.internal.util.ToBooleanFunction; import java.util.Comparator; @@ -685,6 +687,23 @@ a * Returns whether this child is on top of the window hierarchy. } } + /** + * Write to a protocol buffer output stream. Protocol buffer message definition is at + * {@link com.android.server.wm.proto.WindowContainerProto}. + * + * @param protoOutputStream Stream to write the WindowContainer object to. + * @param fieldId Field Id of the WindowContainer as defined in the parent message. + * @hide + */ + @CallSuper + @Override + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + final long token = protoOutputStream.start(fieldId); + super.writeToProto(protoOutputStream, CONFIGURATION_CONTAINER); + protoOutputStream.write(ORIENTATION, mOrientation); + protoOutputStream.end(token); + } + String getName() { return toString(); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 32ee51c8f751..6f796481cc08 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -109,6 +109,7 @@ import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_WIND import static com.android.server.wm.proto.WindowManagerServiceProto.INPUT_METHOD_WINDOW; import static com.android.server.wm.proto.WindowManagerServiceProto.LAST_ORIENTATION; import static com.android.server.wm.proto.WindowManagerServiceProto.POLICY; +import static com.android.server.wm.proto.WindowManagerServiceProto.ROOT_WINDOW_CONTAINER; import static com.android.server.wm.proto.WindowManagerServiceProto.ROTATION; import android.Manifest; @@ -6505,7 +6506,7 @@ public class WindowManagerService extends IWindowManager.Stub private void writeToProtoLocked(ProtoOutputStream proto) { mPolicy.writeToProto(proto, POLICY); - mRoot.writeToProto(proto); + mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER); if (mCurrentFocus != null) { mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 1b055664093f..8a2cb5a148d8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -116,7 +116,9 @@ import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER; import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME; import static com.android.server.wm.proto.WindowStateProto.STACK_ID; import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS; +import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER; +import android.annotation.CallSuper; import android.app.AppOpsManager; import android.content.Context; import android.content.res.Configuration; @@ -3124,8 +3126,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP || (isChildWindow() && getParentWindow().isDockedResizing()); } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); writeIdentifierToProto(proto, IDENTIFIER); proto.write(DISPLAY_ID, getDisplayId()); proto.write(STACK_ID, getStackId()); diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 422615b10fc1..943448eea2b0 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.annotation.CallSuper; import android.util.proto.ProtoOutputStream; import java.util.Comparator; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; @@ -28,6 +29,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.proto.WindowTokenProto.HASH_CODE; import static com.android.server.wm.proto.WindowTokenProto.WINDOWS; +import static com.android.server.wm.proto.WindowTokenProto.WINDOW_CONTAINER; import android.os.Debug; import android.os.IBinder; @@ -263,8 +265,11 @@ class WindowToken extends WindowContainer<WindowState> { super.onDisplayChanged(dc); } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); proto.write(HASH_CODE, System.identityHashCode(this)); for (int i = 0; i < mChildren.size(); i++) { final WindowState w = mChildren.get(i); diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java index e98e5bfb110e..f9d7f9d4904a 100644 --- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java @@ -20,19 +20,13 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.os.Process.SYSTEM_UID; + import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED; import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import android.app.StatusBarManager; import android.app.admin.IDevicePolicyManager; @@ -72,7 +66,8 @@ import org.mockito.verification.VerificationMode; @SmallTest public class LockTaskControllerTest { private static final String TEST_PACKAGE_NAME = "com.test.package"; - private static final String TEST_CLASS_NAME = TEST_PACKAGE_NAME + ".TestClass"; + private static final String TEST_PACKAGE_NAME_2 = "com.test.package2"; + private static final String TEST_CLASS_NAME = ".TestClass"; private static final int TEST_USER_ID = 123; private static final int TEST_UID = 10467; @@ -309,15 +304,111 @@ public class LockTaskControllerTest { verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL); } + @Test + public void testUpdateLockTaskPackages() throws Exception { + String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + String[] whitelist2 = {TEST_PACKAGE_NAME}; + + // No package is whitelisted initially + for (String pkg : whitelist1) { + assertFalse("Package shouldn't be whitelisted: " + pkg, + mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, + mLockTaskController.isPackageWhitelisted(0, pkg)); + } + + // Apply whitelist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1); + + // Assert the whitelist is applied to the correct user + for (String pkg : whitelist1) { + assertTrue("Package should be whitelisted: " + pkg, + mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, + mLockTaskController.isPackageWhitelisted(0, pkg)); + } + + // Update whitelist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2); + + // Assert the new whitelist is applied + assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME, + mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME)); + assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2, + mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); + } + + @Test + public void testUpdateLockTaskPackages_taskRemoved() throws Exception { + // GIVEN two tasks which are whitelisted initially + TaskRecord tr1 = getTaskRecordForUpdate(TEST_PACKAGE_NAME, true); + TaskRecord tr2 = getTaskRecordForUpdate(TEST_PACKAGE_NAME_2, false); + String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + + // GIVEN the tasks are launched into LockTask mode + mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); + mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); + assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); + assertTrue(mLockTaskController.checkLockedTask(tr1)); + assertTrue(mLockTaskController.checkLockedTask(tr2)); + verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED); + + // WHEN removing one package from whitelist + whitelist = new String[] {TEST_PACKAGE_NAME}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + + // THEN the task running that package should be stopped + verify(tr2).performClearTaskLocked(); + assertFalse(mLockTaskController.checkLockedTask(tr2)); + // THEN the other task should remain locked + assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); + assertTrue(mLockTaskController.checkLockedTask(tr1)); + verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED); + + // WHEN removing the last package from whitelist + whitelist = new String[] {}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + + // THEN the last task should be cleared, and the system should quit LockTask mode + verify(tr1).performClearTaskLocked(); + assertFalse(mLockTaskController.checkLockedTask(tr1)); + assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState()); + verifyLockTaskStopped(times(1)); + } + private TaskRecord getTaskRecord(int lockTaskAuth) { + return getTaskRecord(TEST_PACKAGE_NAME, lockTaskAuth); + } + + private TaskRecord getTaskRecord(String pkg, int lockTaskAuth) { TaskRecord tr = mock(TaskRecord.class); tr.mLockTaskAuth = lockTaskAuth; tr.intent = new Intent() - .setComponent(new ComponentName(TEST_PACKAGE_NAME, TEST_CLASS_NAME)); + .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME)); tr.userId = TEST_USER_ID; return tr; } + /** + * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest + */ + private TaskRecord getTaskRecordForUpdate(String pkg, boolean isAppAware) { + final int authIfWhitelisted = isAppAware + ? TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE + : TaskRecord.LOCK_TASK_AUTH_WHITELISTED; + TaskRecord tr = getTaskRecord(pkg, authIfWhitelisted); + doAnswer((invocation) -> { + boolean isWhitelisted = + mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg); + tr.mLockTaskAuth = isWhitelisted + ? authIfWhitelisted + : TaskRecord.LOCK_TASK_AUTH_PINNABLE; + return null; + }).when(tr).setLockTaskAuth(); + return tr; + } + private void verifyLockTaskStarted(int statusBarMask) throws Exception { // THEN the keyguard should have been disabled verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString()); |