diff options
31 files changed, 760 insertions, 189 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index 1142fb631891..795d873ec69c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3323,6 +3323,7 @@ package android.provider { field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; field public static final String NOTIFICATION_BADGING = "notification_badging"; field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content"; + field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds"; field public static final String USER_SETUP_COMPLETE = "user_setup_complete"; field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service"; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 2bbce11da2ff..e998711ef3c0 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -470,6 +470,14 @@ message Atom { NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(module) = "network_stack"]; NetworkValidationReported network_validation_reported = 292 [(module) = "network_stack"]; NetworkStackQuirkReported network_stack_quirk_reported = 293 [(module) = "network_stack"]; + MediametricsAudioRecordDeviceUsageReported mediametrics_audiorecorddeviceusage_reported = + 294; + MediametricsAudioThreadDeviceUsageReported mediametrics_audiothreaddeviceusage_reported = + 295; + MediametricsAudioTrackDeviceUsageReported mediametrics_audiotrackdeviceusage_reported = + 296; + MediametricsAudioDeviceConnectionReported mediametrics_audiodeviceconnection_reported = + 297; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. @@ -10549,3 +10557,237 @@ message AssistantInvocationReported { // Whether the Assistant handles were showing at the time of invocation. optional bool assistant_handles_showing = 6; } + +/** + * Logs when an AudioRecord finishes running on an audio device + * + * Logged from: + * frameworks/av/services/mediametrics/AudioAnalytics.cpp + */ +message MediametricsAudioRecordDeviceUsageReported { + // The devices connected to this AudioRecord. + // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". + // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string devices = 1; + + // The name of the remote device attached to the device, typically available for USB or BT. + // This may be empty for a fixed device, or separated by "|" if more than one. + optional string device_names = 2; + + // The amount of time spent in the device as measured by the active track in AudioFlinger. + optional int64 device_time_nanos = 3; + + // The audio data format used for encoding. + // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t + optional string encoding = 4; + + // The client-server buffer framecount. + // The framecount is generally between 960 - 48000 for PCM encoding. + // The framecount represents raw buffer size in bytes for non-PCM encoding. + optional int32 frame_count = 5; + + // The number of audio intervals (contiguous, continuous playbacks). + optional int32 interval_count = 6; + + // The sample rate of the AudioRecord. + // A number generally between 8000-96000 (frames per second). + optional int32 sample_rate = 7; + + // The audio input flags used to construct the AudioRecord. + // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t + optional string flags = 8; + + // The santized package name of the audio client associated with the AudioRecord. + // See getSanitizedPackageNameAndVersionCode() in + // frameworks/av/services/mediametrics/MediaMetricsService.cpp + optional string package_name = 9; + + // The selected device id (nonzero if a non-default device is selected) + optional int32 selected_device_id = 10; + + // The caller of the AudioRecord. + // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp + optional string caller = 11; + + // The audio source for AudioRecord. + // An enumeration from system/media/audio/include/system/audio-base.h audio_source_t + optional string source = 12; +} + +/** + * Logs when an AudioThread finishes running on an audio device + * + * Logged from: + * frameworks/av/services/mediametrics/AudioAnalytics.cpp + */ +message MediametricsAudioThreadDeviceUsageReported { + // The devices connected to this audio thread. + // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". + // (for record threads): + // See lookup<INPUT_DEVICE> in frameworks/av/services/mediametrics/AudioTypes.cpp + // (for playback threads): + // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string devices = 1; + + // The name of the remote device attached to the device, typically available for USB or BT. + // This may be empty for a fixed device, or separated by "|" if more than one. + optional string device_names = 2; + + // The amount of time spent in the device as measured by the active track in AudioFlinger. + optional int64 device_time_nanos = 3; + + // The audio data format used for encoding. + // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t + optional string encoding = 4; + + // The framecount of the buffer delivered to (or from) the HAL. + // The framecount is generally ~960 for PCM encoding. + // The framecount represents raw buffer size in bytes for non-PCM encoding. + optional int32 frame_count = 5; + + // The number of audio intervals (contiguous, continuous playbacks). + optional int32 interval_count = 6; + + // The sample rate of the audio thread. + // A number generally between 8000-96000 (frames per second). + optional int32 sample_rate = 7; + + // The audio flags used to construct the thread + // (for record threads): + // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t + // (for playback threads): + // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t + optional string flags = 8; + + // The number of underruns encountered for a playback thread or the + // number of overruns encountered for a capture thread. + optional int32 xruns = 9; + + // The type of thread + // A thread type enumeration from + // frameworks/av/mediametrics/services/Translate.h + optional string type = 10; +} + +/** + * Logs when an AudioTrack finishes running on an audio device + * + * Logged from: + * frameworks/av/services/mediametrics/AudioAnalytics.cpp + */ +message MediametricsAudioTrackDeviceUsageReported { + // The output devices connected to this AudioTrack. + // A string OR of various output device categories, e.g. "DEVICE1|DEVICE2". + // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string devices = 1; + + // The name of the remote device attached to the device, typically available for USB or BT. + // This may be empty for a fixed device, or separated by "|" if more than one. + optional string device_names = 2; + + // The amount of time spent in the device as measured by the active track in AudioFlinger. + optional int64 device_time_nanos = 3; + + // The audio data format used for encoding. + // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t + optional string encoding = 4; + + // The client-server buffer framecount. + // The framecount is generally between 960 - 48000 for PCM encoding. + // The framecount represents raw buffer size in bytes for non-PCM encoding. + // A static track (see traits) may have a very large framecount. + optional int32 frame_count = 5; + + // The number of audio intervals (contiguous, continuous playbacks). + optional int32 interval_count = 6; + + // The sample rate of the AudioTrack. + // A number generally between 8000-96000 (frames per second). + optional int32 sample_rate = 7; + + // The audio flags used to construct the AudioTrack. + // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t + optional string flags = 8; + + // The number of underruns encountered. + optional int32 xruns = 9; + + // The santized package name of the audio client associated with the AudioTrack. + // See getSanitizedPackageNameAndVersionCode() in + // frameworks/av/services/mediametrics/MediaMetricsService.cpp + optional string package_name = 10; + + // The latency of the last sample in the buffer in milliseconds. + optional float device_latency_millis = 11; + + // The startup time in milliseconds from start() to sample played. + optional float device_startup_millis = 12; + + // The average volume of the track on the device [ 0.f - 1.f ] + optional float device_volume = 13; + + // The selected device id (nonzero if a non-default device is selected) + optional int32 selected_device_id = 14; + + // The stream_type category for the AudioTrack. + // An enumeration from system/media/audio/include/system/audio-base.h audio_stream_type_t + optional string stream_type = 15; + + // The usage for the AudioTrack. + // An enumeration from system/media/audio/include/system/audio-base.h audio_usage_t + optional string usage = 16; + + // The content type of the AudioTrack. + // An enumeration from system/media/audio/include/system/audio-base.h audio_content_type_t + optional string content_type = 17; + + // The caller of the AudioTrack. + // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp + optional string caller = 18; + + // The traits of the AudioTrack. + // A string OR of different traits, may be empty string. + // Only "static" is supported for R. + // See lookup<TRACK_TRAITS>() in frameworks/av/services/mediametrics/AudioTypes.cpp + optional string traits = 19; +} + +/** + * Logs the status of an audio device connection attempt. + * + * Logged from: + * frameworks/av/services/mediametrics/AudioAnalytics.cpp + */ +message MediametricsAudioDeviceConnectionReported { + // The input devices represented by this report. + // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". + // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string input_devices = 1; + + // The output devices represented by this report. + // A string OR of various output device categories. + // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp + // See audio_device_t in system/media/audio/include/system/audio-base.h + optional string output_devices = 2; + + // The name of the remote device attached to the device, typically available for USB or BT. + // This may be empty for a fixed device, or separated by "|" if more than one. + optional string device_names = 3; + + // The result of the audio device connection. + // 0 indicates success: connection verified. + // 1 indicates unknown: connection not verified or not known if diverted properly. + // Other values indicate specific status. + // See DeviceConnectionResult in frameworks/av/services/mediametrics/AudioTypes.h + optional int32 result = 4; + + // Average milliseconds of time to connect + optional float time_to_connect_millis = 5; + + // Number of connections if aggregated statistics, otherwise 1. + optional int32 connection_count = 6; +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 1f90e401dee5..322cac81d58b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4247,6 +4247,12 @@ public class DevicePolicyManager { * device. After this method is called, the device must be unlocked using strong authentication * (PIN, pattern, or password). This API is intended for use only by device admins. * <p> + * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have + * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is + * true, then the method will return without completing any action. Before version + * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature, + * regardless of the caller's permissions. + * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} * to be able to call this method; if it has not, a security exception will be thrown. * <p> @@ -4274,6 +4280,12 @@ public class DevicePolicyManager { * device. After this method is called, the device must be unlocked using strong authentication * (PIN, pattern, or password). This API is intended for use only by device admins. * <p> + * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have + * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is + * true, then the method will return without completing any action. Before version + * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature, + * regardless of the caller's permissions. + * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} * to be able to call this method; if it has not, a security exception will be thrown. * <p> diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e10fceaa5bc7..4f0a9728fcf8 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; @@ -6238,6 +6239,8 @@ public final class Settings { * determines if the IME should be shown when a hard keyboard is attached. * @hide */ + @TestApi + @SuppressLint("NoSettingsProvider") public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; /** diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java index 1e16273c455b..e592fad394b8 100644 --- a/core/java/android/service/controls/templates/ControlTemplate.java +++ b/core/java/android/service/controls/templates/ControlTemplate.java @@ -214,10 +214,13 @@ public abstract class ControlTemplate { } /** - * Get a singleton {@link ControlTemplate} that has no features. + * Get a singleton {@link ControlTemplate}, which supports no direct user input. * - * This template has no distinctive field, not even an identifier. Used for a {@link Control} - * that accepts no type of input, or when there is no known state. + * Used by {@link Control.StatelessBuilder} when there is no known state. Can also be used + * in {@link Control.StatefulBuilder} for conveying information to a user about the + * {@link Control} but direct user interaction is not desired. Since this template has no + * corresponding {@link ControlAction}, any user interaction will launch the + * {@link Control#getAppIntent()}. * * @return a singleton {@link ControlTemplate} to indicate no specific template is used by * this {@link Control} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java index 775ef8152ca2..8aa7b6389d85 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java @@ -23,9 +23,12 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.os.Handler; +import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.util.Log; +import android.view.IWindowManager; import android.widget.ImageView; import android.widget.TextView; @@ -44,13 +47,14 @@ import javax.inject.Singleton; */ @Singleton public class UserSwitchTransitionViewController extends OverlayViewController { - private static final String TAG = "UserSwitchTransitionViewController"; + private static final String TAG = "UserSwitchTransition"; private static final String ENABLE_DEVELOPER_MESSAGE_TRUE = "true"; private final Context mContext; private final Handler mHandler; private final Resources mResources; private final UserManager mUserManager; + private final IWindowManager mWindowManagerService; @GuardedBy("this") private boolean mShowing; @@ -62,6 +66,7 @@ public class UserSwitchTransitionViewController extends OverlayViewController { @Main Handler handler, @Main Resources resources, UserManager userManager, + IWindowManager windowManagerService, OverlayViewGlobalStateController overlayViewGlobalStateController) { super(R.id.user_switching_dialog_stub, overlayViewGlobalStateController); @@ -70,6 +75,7 @@ public class UserSwitchTransitionViewController extends OverlayViewController { mHandler = handler; mResources = resources; mUserManager = userManager; + mWindowManagerService = windowManagerService; } /** @@ -81,6 +87,13 @@ public class UserSwitchTransitionViewController extends OverlayViewController { if (mPreviousUserId == newUserId || mShowing) return; mShowing = true; mHandler.post(() -> { + try { + mWindowManagerService.setSwitchingUser(true); + mWindowManagerService.lockNow(null); + } catch (RemoteException e) { + Log.e(TAG, "unable to notify window manager service regarding user switch"); + } + start(); populateDialog(mPreviousUserId, newUserId); // next time a new user is selected, this current new user will be the previous user. diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java index eab381c92d98..65c556269f13 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java @@ -28,6 +28,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; +import android.view.IWindowManager; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -52,6 +53,8 @@ public class UserSwitchTransitionViewControllerTest extends SysuiTestCase { private TestableResources mTestableResources; @Mock private OverlayViewGlobalStateController mOverlayViewGlobalStateController; + @Mock + private IWindowManager mWindowManagerService; @Before public void setUp() { @@ -62,6 +65,7 @@ public class UserSwitchTransitionViewControllerTest extends SysuiTestCase { Handler.getMain(), mTestableResources.getResources(), (UserManager) mContext.getSystemService(Context.USER_SERVICE), + mWindowManagerService, mOverlayViewGlobalStateController ); @@ -125,8 +129,10 @@ public class UserSwitchTransitionViewControllerTest extends SysuiTestCase { TestableUserSwitchTransitionViewController(Context context, Handler handler, Resources resources, UserManager userManager, + IWindowManager windowManagerService, OverlayViewGlobalStateController overlayViewGlobalStateController) { - super(context, handler, resources, userManager, overlayViewGlobalStateController); + super(context, handler, resources, userManager, windowManagerService, + overlayViewGlobalStateController); mHandler = handler; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java new file mode 100644 index 000000000000..9f26d851f775 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.system; + +import android.app.ActivityManager; +import android.os.SystemProperties; + +public abstract class BlurUtils { + + private static boolean mBlurSupportedSysProp = SystemProperties + .getBoolean("ro.surface_flinger.supports_background_blur", false); + private static boolean mBlurDisabledSysProp = SystemProperties + .getBoolean("persist.sys.sf.disable_blurs", false); + + /** + * If this device can render blurs. + * + * @return {@code true} when supported. + */ + public static boolean supportsBlursOnWindows() { + return mBlurSupportedSysProp && !mBlurDisabledSysProp && ActivityManager.isHighEndGfx(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 47e6645d347f..6dcc9dcdc63c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -163,6 +163,11 @@ public class BubbleExpandedView extends LinearLayout { Log.d(TAG, "onActivityViewReady: calling startActivity, " + "bubble=" + getBubbleKey()); } + if (mActivityView == null) { + mBubbleController.removeBubble(getBubbleKey(), + BubbleController.DISMISS_INVALID_INTENT); + return; + } try { if (!mIsOverflow && mBubble.usingShortcutInfo()) { options.setApplyActivityFlagsForBubbles(true); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 015079ff900a..d5fe9b2953de 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -1822,6 +1822,10 @@ public class BubbleStackView extends FrameLayout mExpandedBubble.getExpandedView().hideImeIfVisible(); } + // Let the expanded animation controller know that it shouldn't animate child adds/reorders + // since we're about to animate collapsed. + mExpandedAnimationController.notifyPreparingToCollapse(); + final long startDelay = (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f); postDelayed(() -> mExpandedAnimationController.collapseBackToStack( diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index 86fe10dddc2c..cb8995a72dc3 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -92,6 +92,14 @@ public class ExpandedAnimationController private int mScreenOrientation; private boolean mAnimatingExpand = false; + + /** + * Whether we are animating other Bubbles UI elements out in preparation for a call to + * {@link #collapseBackToStack}. If true, we won't animate bubbles in response to adds or + * reorders. + */ + private boolean mPreparingToCollapse = false; + private boolean mAnimatingCollapse = false; private @Nullable Runnable mAfterExpand; private Runnable mAfterCollapse; @@ -150,6 +158,7 @@ public class ExpandedAnimationController */ public void expandFromStack( @Nullable Runnable after, @Nullable Runnable leadBubbleEndAction) { + mPreparingToCollapse = false; mAnimatingCollapse = false; mAnimatingExpand = true; mAfterExpand = after; @@ -165,9 +174,20 @@ public class ExpandedAnimationController expandFromStack(after, null /* leadBubbleEndAction */); } + /** + * Sets that we're animating the stack collapsed, but haven't yet called + * {@link #collapseBackToStack}. This will temporarily suspend animations for bubbles that are + * added or re-ordered, since the upcoming collapse animation will handle positioning those + * bubbles in the collapsed stack. + */ + public void notifyPreparingToCollapse() { + mPreparingToCollapse = true; + } + /** Animate collapsing the bubbles back to their stacked position. */ public void collapseBackToStack(PointF collapsePoint, Runnable after) { mAnimatingExpand = false; + mPreparingToCollapse = false; mAnimatingCollapse = true; mAfterCollapse = after; mCollapsePoint = collapsePoint; @@ -501,12 +521,18 @@ public class ExpandedAnimationController startOrUpdatePathAnimation(false /* expanding */); } else { child.setTranslationX(getBubbleLeft(index)); - animationForChild(child) - .translationY( - getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */ - getExpandedY() /* to */) - .start(); - updateBubblePositions(); + + // If we're preparing to collapse, don't start animations since the collapse animation + // will take over and animate the new bubble into the correct (stacked) position. + if (!mPreparingToCollapse) { + animationForChild(child) + .translationY( + getExpandedY() + - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */ + getExpandedY() /* to */) + .start(); + updateBubblePositions(); + } } } @@ -532,12 +558,20 @@ public class ExpandedAnimationController @Override void onChildReordered(View child, int oldIndex, int newIndex) { - updateBubblePositions(); + if (mPreparingToCollapse) { + // If a re-order is received while we're preparing to collapse, ignore it. Once started, + // the collapse animation will animate all of the bubbles to their correct (stacked) + // position. + return; + } - // We expect reordering during collapse, since we'll put the last selected bubble on top. - // Update the collapse animation so they end up in the right stacked positions. if (mAnimatingCollapse) { + // If a re-order is received during collapse, update the animation so that the bubbles + // end up in the correct (stacked) position. startOrUpdatePathAnimation(false /* expanding */); + } else { + // Otherwise, animate the bubbles around to reflect their new order. + updateBubblePositions(); } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt index d4d4d2a7d8fe..eed55315e836 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt @@ -20,7 +20,7 @@ import android.content.ComponentName import android.service.controls.Control import android.service.controls.ControlsProviderService import android.service.controls.actions.ControlAction -import com.android.systemui.controls.UserAwareController +import com.android.systemui.util.UserAwareController import java.util.function.Consumer /** diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt index 45ba1e6012fe..496741b1cd6f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt @@ -21,7 +21,7 @@ import android.service.controls.Control import android.service.controls.ControlsProviderService import android.service.controls.actions.ControlAction import com.android.systemui.controls.ControlStatus -import com.android.systemui.controls.UserAwareController +import com.android.systemui.util.UserAwareController import com.android.systemui.controls.management.ControlsFavoritingActivity import com.android.systemui.controls.ui.ControlsUiController import java.util.function.Consumer diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt index 647daccca8bd..b9f16665944f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt @@ -18,7 +18,7 @@ package com.android.systemui.controls.management import android.content.ComponentName import com.android.systemui.controls.ControlsServiceInfo -import com.android.systemui.controls.UserAwareController +import com.android.systemui.util.UserAwareController import com.android.systemui.statusbar.policy.CallbackController /** @@ -26,7 +26,7 @@ import com.android.systemui.statusbar.policy.CallbackController */ interface ControlsListingController : CallbackController<ControlsListingController.ControlsListingCallback>, - UserAwareController { + UserAwareController { /** * @return the current list of services that satisfies the [ServiceListing]. diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 353367ead7e6..ee02b85e4a00 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -89,6 +89,7 @@ class ControlViewHolder( return when { status != Control.STATUS_OK -> StatusBehavior::class deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class + template == ControlTemplate.NO_TEMPLATE -> TouchBehavior::class template is ToggleTemplate -> ToggleBehavior::class template is StatelessTemplate -> TouchBehavior::class template is ToggleRangeTemplate -> ToggleRangeBehavior::class @@ -235,7 +236,10 @@ class ControlViewHolder( controlsController.action(cws.componentName, cws.ci, action) } - fun usePanel(): Boolean = deviceType in ControlViewHolder.FORCE_PANEL_DEVICES + fun usePanel(): Boolean { + return deviceType in ControlViewHolder.FORCE_PANEL_DEVICES || + controlTemplate == ControlTemplate.NO_TEMPLATE + } fun bindBehavior( existingBehavior: Behavior?, diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java index 2365e67fec2f..0a84f5ee1bb9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java +++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java @@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.phone.AutoTileManager.WORK; import android.content.Context; import android.database.ContentObserver; import android.os.Handler; +import android.os.UserHandle; import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.ArraySet; @@ -30,6 +31,7 @@ import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Prefs; import com.android.systemui.Prefs.Key; +import com.android.systemui.util.UserAwareController; import java.util.Arrays; import java.util.Collection; @@ -37,7 +39,7 @@ import java.util.Collections; import javax.inject.Inject; -public class AutoAddTracker { +public class AutoAddTracker implements UserAwareController { private static final String[][] CONVERT_PREFS = { {Key.QS_HOTSPOT_ADDED, HOTSPOT}, @@ -49,20 +51,39 @@ public class AutoAddTracker { private final ArraySet<String> mAutoAdded; private final Context mContext; + private int mUserId; - @Inject - public AutoAddTracker(Context context) { + public AutoAddTracker(Context context, int userId) { mContext = context; + mUserId = userId; mAutoAdded = new ArraySet<>(getAdded()); // TODO: remove migration code and shared preferences keys after P release - for (String[] convertPref : CONVERT_PREFS) { - if (Prefs.getBoolean(context, convertPref[0], false)) { - setTileAdded(convertPref[1]); - Prefs.remove(context, convertPref[0]); + if (mUserId == UserHandle.USER_SYSTEM) { + for (String[] convertPref : CONVERT_PREFS) { + if (Prefs.getBoolean(context, convertPref[0], false)) { + setTileAdded(convertPref[1]); + Prefs.remove(context, convertPref[0]); + } } } mContext.getContentResolver().registerContentObserver( - Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver); + Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver, + UserHandle.USER_ALL); + } + + @Override + public void changeUser(UserHandle newUser) { + if (newUser.getIdentifier() == mUserId) { + return; + } + mUserId = newUser.getIdentifier(); + mAutoAdded.clear(); + mAutoAdded.addAll(getAdded()); + } + + @Override + public int getCurrentUserId() { + return mUserId; } public boolean isAdded(String tile) { @@ -86,12 +107,13 @@ public class AutoAddTracker { } private void saveTiles() { - Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, - TextUtils.join(",", mAutoAdded)); + Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, + TextUtils.join(",", mAutoAdded), mUserId); } private Collection<String> getAdded() { - String current = Secure.getString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES); + String current = Secure.getStringForUser(mContext.getContentResolver(), + Secure.QS_AUTO_ADDED_TILES, mUserId); if (current == null) { return Collections.emptyList(); } @@ -102,7 +124,27 @@ public class AutoAddTracker { protected final ContentObserver mObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { + mAutoAdded.clear(); mAutoAdded.addAll(getAdded()); } }; + + public static class Builder { + private final Context mContext; + private int mUserId; + + @Inject + public Builder(Context context) { + mContext = context; + } + + public Builder setUserId(int userId) { + mUserId = userId; + return this; + } + + public AutoAddTracker build() { + return new AutoAddTracker(mContext, mUserId); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 65d3572d04a3..858a7e2e30e0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -255,6 +255,9 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D int currentUser = ActivityManager.getCurrentUser(); if (currentUser != mCurrentUser) { mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0); + if (mAutoTiles != null) { + mAutoTiles.changeUser(UserHandle.of(currentUser)); + } } if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return; mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index fc8c8dbba7fd..825919f17661 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -19,6 +19,7 @@ import android.content.res.Resources; import android.hardware.display.ColorDisplayManager; import android.hardware.display.NightDisplayListener; import android.os.Handler; +import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -34,6 +35,7 @@ import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DataSaverController.Listener; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.HotspotController.Callback; +import com.android.systemui.util.UserAwareController; import java.util.ArrayList; import java.util.Objects; @@ -43,7 +45,7 @@ import javax.inject.Inject; /** * Manages which tiles should be automatically added to QS. */ -public class AutoTileManager { +public class AutoTileManager implements UserAwareController { private static final String TAG = "AutoTileManager"; public static final String HOTSPOT = "hotspot"; @@ -52,7 +54,9 @@ public class AutoTileManager { public static final String WORK = "work"; public static final String NIGHT = "night"; public static final String CAST = "cast"; - public static final String SETTING_SEPARATOR = ":"; + static final String SETTING_SEPARATOR = ":"; + + private UserHandle mCurrentUser; private final Context mContext; private final QSTileHost mHost; @@ -66,43 +70,56 @@ public class AutoTileManager { private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>(); @Inject - public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host, + public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder, + QSTileHost host, @Background Handler handler, HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, NightDisplayListener nightDisplayListener, CastController castController) { - mAutoTracker = autoAddTracker; mContext = context; mHost = host; + mCurrentUser = mHost.getUserContext().getUser(); + mAutoTracker = autoAddTrackerBuilder.setUserId(mCurrentUser.getIdentifier()).build(); mHandler = handler; mHotspotController = hotspotController; mDataSaverController = dataSaverController; mManagedProfileController = managedProfileController; mNightDisplayListener = nightDisplayListener; mCastController = castController; + + populateSettingsList(); + startControllersAndSettingsListeners(); + } + + protected void startControllersAndSettingsListeners() { if (!mAutoTracker.isAdded(HOTSPOT)) { - hotspotController.addCallback(mHotspotCallback); + mHotspotController.addCallback(mHotspotCallback); } if (!mAutoTracker.isAdded(SAVER)) { - dataSaverController.addCallback(mDataSaverListener); + mDataSaverController.addCallback(mDataSaverListener); } if (!mAutoTracker.isAdded(WORK)) { - managedProfileController.addCallback(mProfileCallback); + mManagedProfileController.addCallback(mProfileCallback); } if (!mAutoTracker.isAdded(NIGHT) && ColorDisplayManager.isNightDisplayAvailable(mContext)) { - nightDisplayListener.setCallback(mNightDisplayCallback); + mNightDisplayListener.setCallback(mNightDisplayCallback); } if (!mAutoTracker.isAdded(CAST)) { - castController.addCallback(mCastCallback); + mCastController.addCallback(mCastCallback); + } + + int settingsN = mAutoAddSettingList.size(); + for (int i = 0; i < settingsN; i++) { + if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) { + mAutoAddSettingList.get(i).setListening(true); + } } - populateSettingsList(); } - public void destroy() { - mAutoTracker.destroy(); + protected void stopListening() { mHotspotController.removeCallback(mHotspotCallback); mDataSaverController.removeCallback(mDataSaverListener); mManagedProfileController.removeCallback(mProfileCallback); @@ -116,6 +133,11 @@ public class AutoTileManager { } } + public void destroy() { + stopListening(); + mAutoTracker.destroy(); + } + /** * Populates a list with the pairs setting:spec in the config resource. * <p> @@ -137,17 +159,39 @@ public class AutoTileManager { if (split.length == 2) { String setting = split[0]; String spec = split[1]; - if (!mAutoTracker.isAdded(spec)) { - AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec); - mAutoAddSettingList.add(s); - s.setListening(true); - } + // Populate all the settings. As they may not have been added in other users + AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec); + mAutoAddSettingList.add(s); } else { Log.w(TAG, "Malformed item in array: " + tile); } } } + @Override + public void changeUser(UserHandle newUser) { + if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) { + mHandler.post(() -> changeUser(newUser)); + return; + } + if (newUser.getIdentifier() == mCurrentUser.getIdentifier()) { + return; + } + stopListening(); + mCurrentUser = newUser; + int settingsN = mAutoAddSettingList.size(); + for (int i = 0; i < settingsN; i++) { + mAutoAddSettingList.get(i).setUserId(newUser.getIdentifier()); + } + mAutoTracker.changeUser(newUser); + startControllersAndSettingsListeners(); + } + + @Override + public int getCurrentUserId() { + return mCurrentUser.getIdentifier(); + } + public void unmarkTileAsAutoAdded(String tabSpec) { mAutoTracker.setTileRemoved(tabSpec); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java index daefef5e826d..c211de08cb8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java @@ -16,14 +16,19 @@ package com.android.systemui.statusbar.phone; +import static android.content.Intent.ACTION_OVERLAY_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.om.IOverlayManager; import android.content.om.OverlayInfo; import android.content.pm.PackageManager; import android.content.res.ApkAssets; +import android.os.PatternMatcher; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -79,6 +84,19 @@ public class NavigationModeController implements Dumpable { } }; + // The primary user SysUI process doesn't get AppInfo changes from overlay package changes for + // the secondary user (b/158613864), so we need to update the interaction mode here as well + // as a fallback if we don't receive the configuration change + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) { + Log.d(TAG, "ACTION_OVERLAY_CHANGED"); + } + updateCurrentInteractionMode(true /* notify */); + } + }; + @Inject public NavigationModeController(Context context, @@ -92,6 +110,11 @@ public class NavigationModeController implements Dumpable { mUiBgExecutor = uiBgExecutor; deviceProvisionedController.addCallback(mDeviceProvisionedCallback); + IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED); + overlayFilter.addDataScheme("package"); + overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL); + mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null); + configurationController.addCallback(new ConfigurationController.ConfigurationListener() { @Override public void onOverlayChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt index d2776d27ae62..693c2708b0f7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt +++ b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.controls +package com.android.systemui.util import android.os.UserHandle @@ -23,6 +23,8 @@ import android.os.UserHandle * changes. */ interface UserAwareController { + @JvmDefault fun changeUser(newUser: UserHandle) {} + val currentUserId: Int }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java index 0ae9461d38b1..61f5a7bdd3b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.phone.AutoTileManager.WORK; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.os.UserHandle; import android.provider.Settings.Secure; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -40,6 +41,8 @@ import org.junit.runner.RunWith; @SmallTest public class AutoAddTrackerTest extends SysuiTestCase { + private static final int USER = 0; + private AutoAddTracker mAutoTracker; @Before @@ -51,7 +54,7 @@ public class AutoAddTrackerTest extends SysuiTestCase { public void testMigration() { Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true); Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true); - mAutoTracker = new AutoAddTracker(mContext); + mAutoTracker = new AutoAddTracker(mContext, USER); assertTrue(mAutoTracker.isAdded(SAVER)); assertTrue(mAutoTracker.isAdded(WORK)); @@ -68,7 +71,7 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testChangeFromBackup() { - mAutoTracker = new AutoAddTracker(mContext); + mAutoTracker = new AutoAddTracker(mContext, USER); assertFalse(mAutoTracker.isAdded(SAVER)); @@ -82,7 +85,7 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testSetAdded() { - mAutoTracker = new AutoAddTracker(mContext); + mAutoTracker = new AutoAddTracker(mContext, USER); assertFalse(mAutoTracker.isAdded(SAVER)); mAutoTracker.setTileAdded(SAVER); @@ -94,16 +97,35 @@ public class AutoAddTrackerTest extends SysuiTestCase { @Test public void testPersist() { - mAutoTracker = new AutoAddTracker(mContext); + mAutoTracker = new AutoAddTracker(mContext, USER); assertFalse(mAutoTracker.isAdded(SAVER)); mAutoTracker.setTileAdded(SAVER); mAutoTracker.destroy(); - mAutoTracker = new AutoAddTracker(mContext); + mAutoTracker = new AutoAddTracker(mContext, USER); assertTrue(mAutoTracker.isAdded(SAVER)); mAutoTracker.destroy(); } + + @Test + public void testIndependentUsers() { + mAutoTracker = new AutoAddTracker(mContext, USER); + mAutoTracker.setTileAdded(SAVER); + + mAutoTracker = new AutoAddTracker(mContext, USER + 1); + assertFalse(mAutoTracker.isAdded(SAVER)); + } + + @Test + public void testChangeUser() { + mAutoTracker = new AutoAddTracker(mContext, USER); + mAutoTracker.setTileAdded(SAVER); + + mAutoTracker = new AutoAddTracker(mContext, USER + 1); + mAutoTracker.changeUser(UserHandle.of(USER)); + assertTrue(mAutoTracker.isAdded(SAVER)); + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java index 05cdd802167a..31779cdf9e71 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java @@ -16,18 +16,34 @@ package com.android.systemui.statusbar.phone; +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.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.isNotNull; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.ContextWrapper; import android.hardware.display.ColorDisplayManager; import android.hardware.display.NightDisplayListener; import android.os.Handler; +import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; +import android.testing.TestableContentResolver; +import android.testing.TestableContext; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -38,14 +54,18 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.SecureSetting; +import com.android.systemui.statusbar.phone.AutoTileManagerTest.MyContextWrapper; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.HotspotController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -64,9 +84,18 @@ public class AutoTileManagerTest extends SysuiTestCase { private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")"; private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR; + private static final int USER = 0; + @Mock private QSTileHost mQsTileHost; @Mock private AutoAddTracker mAutoAddTracker; @Mock private CastController mCastController; + @Mock private HotspotController mHotspotController; + @Mock private DataSaverController mDataSaverController; + @Mock private ManagedProfileController mManagedProfileController; + @Mock private NightDisplayListener mNightDisplayListener; + @Mock(answer = Answers.RETURNS_SELF) + private AutoAddTracker.Builder mAutoAddTrackerBuilder; + @Mock private Context mUserContext; private AutoTileManager mAutoTileManager; @@ -82,20 +111,110 @@ public class AutoTileManagerTest extends SysuiTestCase { } ); - mAutoTileManager = createAutoTileManager(); + when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker); + when(mQsTileHost.getUserContext()).thenReturn(mUserContext); + when(mUserContext.getUser()).thenReturn(UserHandle.of(USER)); + + mAutoTileManager = createAutoTileManager(new + MyContextWrapper(mContext)); } - private AutoTileManager createAutoTileManager() { - return new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost, + @After + public void tearDown() { + mAutoTileManager.destroy(); + } + + private AutoTileManager createAutoTileManager(Context context) { + return new AutoTileManager(context, mAutoAddTrackerBuilder, mQsTileHost, Handler.createAsync(TestableLooper.get(this).getLooper()), - mock(HotspotController.class), - mock(DataSaverController.class), - mock(ManagedProfileController.class), - mock(NightDisplayListener.class), + mHotspotController, + mDataSaverController, + mManagedProfileController, + mNightDisplayListener, mCastController); } @Test + public void testChangeUserCallbacksStoppedAndStarted() throws Exception { + TestableLooper.get(this).runWithLooper(() -> + mAutoTileManager.changeUser(UserHandle.of(USER + 1)) + ); + + InOrder inOrderHotspot = inOrder(mHotspotController); + inOrderHotspot.verify(mHotspotController).removeCallback(any()); + inOrderHotspot.verify(mHotspotController).addCallback(any()); + + InOrder inOrderDataSaver = inOrder(mDataSaverController); + inOrderDataSaver.verify(mDataSaverController).removeCallback(any()); + inOrderDataSaver.verify(mDataSaverController).addCallback(any()); + + InOrder inOrderManagedProfile = inOrder(mManagedProfileController); + inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any()); + inOrderManagedProfile.verify(mManagedProfileController).addCallback(any()); + + InOrder inOrderNightDisplay = inOrder(mNightDisplayListener); + inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull()); + inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull()); + + InOrder inOrderCast = inOrder(mCastController); + inOrderCast.verify(mCastController).removeCallback(any()); + inOrderCast.verify(mCastController).addCallback(any()); + + SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); + assertEquals(USER + 1, setting.getCurrentUser()); + assertTrue(setting.isListening()); + } + + @Test + public void testChangeUserSomeCallbacksNotAdded() throws Exception { + when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true); + when(mAutoAddTracker.isAdded("work")).thenReturn(true); + when(mAutoAddTracker.isAdded("cast")).thenReturn(true); + when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true); + + TestableLooper.get(this).runWithLooper(() -> + mAutoTileManager.changeUser(UserHandle.of(USER + 1)) + ); + + verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1)); + + InOrder inOrderHotspot = inOrder(mHotspotController); + inOrderHotspot.verify(mHotspotController).removeCallback(any()); + inOrderHotspot.verify(mHotspotController, never()).addCallback(any()); + + InOrder inOrderDataSaver = inOrder(mDataSaverController); + inOrderDataSaver.verify(mDataSaverController).removeCallback(any()); + inOrderDataSaver.verify(mDataSaverController).addCallback(any()); + + InOrder inOrderManagedProfile = inOrder(mManagedProfileController); + inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any()); + inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any()); + + InOrder inOrderNightDisplay = inOrder(mNightDisplayListener); + inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull()); + inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull()); + + InOrder inOrderCast = inOrder(mCastController); + inOrderCast.verify(mCastController).removeCallback(any()); + inOrderCast.verify(mCastController, never()).addCallback(any()); + + SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); + assertEquals(USER + 1, setting.getCurrentUser()); + assertFalse(setting.isListening()); + } + + @Test + public void testGetCurrentUserId() throws Exception { + assertEquals(USER, mAutoTileManager.getCurrentUserId()); + + TestableLooper.get(this).runWithLooper(() -> + mAutoTileManager.changeUser(UserHandle.of(USER + 100)) + ); + + assertEquals(USER + 100, mAutoTileManager.getCurrentUserId()); + } + + @Test public void nightTileAdded_whenActivated() { if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { return; @@ -213,14 +332,14 @@ public class AutoTileManagerTest extends SysuiTestCase { public void testEmptyArray_doesNotCrash() { mContext.getOrCreateTestableResources().addOverride( R.array.config_quickSettingsAutoAdd, new String[0]); - createAutoTileManager(); + createAutoTileManager(mContext).destroy(); } @Test public void testMissingConfig_doesNotCrash() { mContext.getOrCreateTestableResources().addOverride( R.array.config_quickSettingsAutoAdd, null); - createAutoTileManager(); + createAutoTileManager(mContext).destroy(); } // Will only notify if it's listening @@ -231,4 +350,22 @@ public class AutoTileManagerTest extends SysuiTestCase { s.onChange(false); } } + + class MyContextWrapper extends ContextWrapper { + + private TestableContentResolver mSpiedTCR; + + MyContextWrapper(TestableContext context) { + super(context); + mSpiedTCR = spy(context.getContentResolver()); + doNothing().when(mSpiedTCR).registerContentObserver(any(), anyBoolean(), any(), + anyInt()); + doNothing().when(mSpiedTCR).unregisterContentObserver(any()); + } + + @Override + public ContentResolver getContentResolver() { + return mSpiedTCR; + } + } } diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index f14def6a3a02..fd6f171487a9 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -37,8 +37,8 @@ public final class TetheringConstants { private TetheringConstants() { } /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Includes the type of tethering to enable if any. + * Extra used for communicating with the TetherService. Includes the type of tethering to + * enable if any. */ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; /** @@ -56,38 +56,8 @@ public final class TetheringConstants { */ public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the {@link ResultReceiver} which will receive provisioning results. - * Can not be empty. + * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} + * which will receive provisioning results. Can be left empty. */ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; - - /** - * Extra used for communicating with the TetherService and TetherProvisioningActivity. - * Contains the subId of current active cellular upstream. - * @hide - */ - public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; - - /** - * Extra used for telling TetherProvisioningActivity the entitlement package name and class - * name to start UI entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME = - "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME"; - - /** - * Extra used for telling TetherService the intent action to start silent entitlement check. - * @hide - */ - public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = - "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; - - /** - * Extra used for TetherService to receive the response of provisioning check. - * @hide - */ - public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = - "android.net.extra.TETHER_PROVISIONING_RESPONSE"; } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 9dace709d734..3c6e8d88ed13 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -19,10 +19,6 @@ package com.android.networkstack.tethering; import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; @@ -73,6 +69,7 @@ public class EntitlementManager { protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final String ACTION_PROVISIONING_ALARM = "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; + private static final String EXTRA_SUBID = "subId"; private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; @@ -200,9 +197,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config); + runUiTetherProvisioning(downstreamType, config.activeDataSubId); } else { - runSilentTetherProvisioning(downstreamType, config); + runSilentTetherProvisioning(downstreamType, config.activeDataSubId); } mNeedReRunProvisioningUi = false; } else { @@ -265,9 +262,9 @@ public class EntitlementManager { if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { if (mNeedReRunProvisioningUi) { mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config); + runUiTetherProvisioning(downstream, config.activeDataSubId); } else { - runSilentTetherProvisioning(downstream, config); + runSilentTetherProvisioning(downstream, config.activeDataSubId); } } } @@ -364,7 +361,7 @@ public class EntitlementManager { * @param subId default data subscription ID. */ @VisibleForTesting - protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { + protected void runSilentTetherProvisioning(int type, int subId) { if (DBG) mLog.i("runSilentTetherProvisioning: " + type); // For silent provisioning, settings would stop tethering when entitlement fail. ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); @@ -372,20 +369,17 @@ public class EntitlementManager { Intent intent = new Intent(); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); intent.putExtra(EXTRA_RUN_PROVISION, true); - intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); - intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); + intent.putExtra(EXTRA_SUBID, subId); intent.setComponent(mSilentProvisioningService); // Only admin user can change tethering and SilentTetherProvisioning don't need to // show UI, it is fine to always start setting's background service as system user. mContext.startService(intent); - return intent; } - private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { + private void runUiTetherProvisioning(int type, int subId) { ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); - runUiTetherProvisioning(type, config, receiver); + runUiTetherProvisioning(type, subId, receiver); } /** @@ -395,20 +389,17 @@ public class EntitlementManager { * @param receiver to receive entitlement check result. */ @VisibleForTesting - protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, - ResultReceiver receiver) { + protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { if (DBG) mLog.i("runUiTetherProvisioning: " + type); Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); - intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); + intent.putExtra(EXTRA_SUBID, subId); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Only launch entitlement UI for system user. Entitlement UI should not appear for other // user because only admin user is allowed to change tethering. mContext.startActivity(intent); - return intent; } // Not needed to check if this don't run on the handler thread because it's private. @@ -640,7 +631,7 @@ public class EntitlementManager { receiver.send(cacheValue, null); } else { ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config, proxy); + runUiTetherProvisioning(downstream, config.activeDataSubId, proxy); } } } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 1d45f129b51b..48a600dfe6e1 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -107,7 +107,6 @@ public class TetheringConfiguration { public final String[] provisioningApp; public final String provisioningAppNoUi; public final int provisioningCheckPeriod; - public final String provisioningResponse; public final int activeDataSubId; @@ -142,13 +141,10 @@ public class TetheringConfiguration { enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); - provisioningAppNoUi = getResourceString(res, - R.string.config_mobile_hotspot_provision_app_no_ui); + provisioningAppNoUi = getProvisioningAppNoUi(res); provisioningCheckPeriod = getResourceInteger(res, R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); - provisioningResponse = getResourceString(res, - R.string.config_mobile_hotspot_provision_response); mOffloadPollInterval = getResourceInteger(res, R.integer.config_tether_offload_poll_interval, @@ -341,9 +337,9 @@ public class TetheringConfiguration { return copy(LEGACY_DHCP_DEFAULT_RANGE); } - private static String getResourceString(Resources res, final int resId) { + private static String getProvisioningAppNoUi(Resources res) { try { - return res.getString(resId); + return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui); } catch (Resources.NotFoundException e) { return ""; } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 354e75356e9f..72fa916b9e42 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -16,16 +16,8 @@ package com.android.networkstack.tethering; -import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; -import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; -import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; -import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; -import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; -import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; -import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; -import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -52,7 +44,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; @@ -62,7 +53,6 @@ import android.os.ResultReceiver; import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.DeviceConfig; -import android.provider.Settings; import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; @@ -86,7 +76,6 @@ public final class EntitlementManagerTest { private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private Context mContext; @@ -133,51 +122,15 @@ public final class EntitlementManagerTest { } @Override - protected Intent runUiTetherProvisioning(int type, - final TetheringConfiguration config, final ResultReceiver receiver) { - Intent intent = super.runUiTetherProvisioning(type, config, receiver); - assertUiTetherProvisioningIntent(type, config, receiver, intent); + protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { uiProvisionCount++; receiver.send(fakeEntitlementResult, null); - return intent; - } - - private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config, - final ResultReceiver receiver, final Intent intent) { - assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction()); - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - final String[] appName = intent.getStringArrayExtra( - EXTRA_TETHER_UI_PROVISIONING_APP_NAME); - assertEquals(PROVISIONING_APP_NAME.length, appName.length); - for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) { - assertEquals(PROVISIONING_APP_NAME[i], appName[i]); - } - assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } @Override - protected Intent runSilentTetherProvisioning(int type, - final TetheringConfiguration config) { - Intent intent = super.runSilentTetherProvisioning(type, config); - assertSilentTetherProvisioning(type, config, intent); + protected void runSilentTetherProvisioning(int type, int subId) { silentProvisionCount++; addDownstreamMapping(type, fakeEntitlementResult); - return intent; - } - - private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config, - final Intent intent) { - assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); - assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)); - assertEquals(PROVISIONING_NO_UI_APP_NAME, - intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION)); - assertEquals(PROVISIONING_APP_RESPONSE, - intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE)); - assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK)); - assertEquals(config.activeDataSubId, - intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } } @@ -234,8 +187,6 @@ public final class EntitlementManagerTest { .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 312186391d5f..1999ad786ed4 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -61,8 +61,6 @@ public class TetheringConfigurationTest { private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; - private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; - private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private Context mContext; @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; @@ -390,8 +388,6 @@ public class TetheringConfigurationTest { new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); - assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); - assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); } private void setUpResourceForSubId() { @@ -407,10 +403,6 @@ public class TetheringConfigurationTest { new int[0]); when(mResourcesForSubId.getStringArray( R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); - when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) - .thenReturn(PROVISIONING_NO_UI_APP_NAME); - when(mResourcesForSubId.getString( - R.string.config_mobile_hotspot_provision_response)).thenReturn( - PROVISIONING_APP_RESPONSE); } + } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 8d4efed8604b..5787f7c48138 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -26,6 +26,7 @@ import android.content.pm.UserInfo; import android.hardware.rebootescrow.IRebootEscrow; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.UserManager; import android.provider.Settings; @@ -244,6 +245,9 @@ class RebootEscrowManager { } catch (RemoteException e) { Slog.w(TAG, "Could not retrieve escrow data"); return null; + } catch (ServiceSpecificException e) { + Slog.w(TAG, "Got service-specific exception: " + e.errorCode); + return null; } } @@ -335,7 +339,7 @@ class RebootEscrowManager { try { rebootEscrow.storeKey(new byte[32]); - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { Slog.w(TAG, "Could not call RebootEscrow HAL to shred key"); } @@ -373,7 +377,7 @@ class RebootEscrowManager { rebootEscrow.storeKey(escrowKey.getKeyBytes()); armedRebootEscrow = true; Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL"); - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e); } diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index 098b2ef6439d..4e1a23416330 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -301,6 +301,7 @@ private: JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper); Mutex mLock; + Mutex mStreamLock; jweak mThiz; sp<Looper> mLooper; @@ -338,6 +339,7 @@ JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Loo } int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) { + Mutex::Autolock autoLock(&mStreamLock); KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); if (connections.indexOfKey(streamId) < 0) { connections.add(streamId, Connection()); @@ -412,6 +414,7 @@ int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface> } int JTvInputHal::removeStream(int deviceId, int streamId) { + Mutex::Autolock autoLock(&mStreamLock); KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); if (connections.indexOfKey(streamId) < 0) { return BAD_VALUE; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 10ad07cff847..7b624cae8141 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -631,7 +631,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * Whether or not device admin feature is supported. If it isn't return defaults for all - * public methods. + * public methods, unless the caller has the appropriate permission for a particular method. */ final boolean mHasFeature; @@ -6032,7 +6032,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void lockNow(int flags, boolean parent) { - if (!mHasFeature) { + if (!mHasFeature && mContext.checkCallingPermission(android.Manifest.permission.LOCK_DEVICE) + != PackageManager.PERMISSION_GRANTED) { return; } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index 4127fece17bd..c4d121170624 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -43,6 +43,7 @@ import android.content.ContextWrapper; import android.content.pm.UserInfo; import android.hardware.rebootescrow.IRebootEscrow; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.os.UserManager; import android.platform.test.annotations.Presubmit; @@ -178,6 +179,13 @@ public class RebootEscrowManagerTests { } @Test + public void clearCredentials_HalFailure_NonFatal() throws Exception { + doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any()); + mService.clearRebootEscrow(); + verify(mRebootEscrow).storeKey(eq(new byte[32])); + } + + @Test public void armService_Success() throws Exception { RebootEscrowListener mockListener = mock(RebootEscrowListener.class); mService.setRebootEscrowListener(mockListener); @@ -200,6 +208,24 @@ public class RebootEscrowManagerTests { } @Test + public void armService_HalFailure_NonFatal() throws Exception { + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mRebootEscrow); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mRebootEscrow, never()).storeKey(any()); + + assertNull( + mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); + doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any()); + assertFalse(mService.armRebootEscrowIfNeeded()); + verify(mRebootEscrow).storeKey(any()); + } + + @Test public void armService_MultipleUsers_Success() throws Exception { RebootEscrowListener mockListener = mock(RebootEscrowListener.class); mService.setRebootEscrowListener(mockListener); |