Merge "Internal version of isChangeEnabled." into rvc-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index 1142fb6..795d873 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3323,6 +3323,7 @@
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 2bbce11..e998711 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -470,6 +470,14 @@
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 @@
// 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 1f90e40..322cac8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4247,6 +4247,12 @@
* 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 @@
* 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 e10fcea..4f0a972 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -24,6 +24,7 @@
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 @@
* 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 1e16273..e592fad 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -214,10 +214,13 @@
}
/**
- * 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 775ef81..8aa7b63 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.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 @@
*/
@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 @@
@Main Handler handler,
@Main Resources resources,
UserManager userManager,
+ IWindowManager windowManagerService,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
super(R.id.user_switching_dialog_stub, overlayViewGlobalStateController);
@@ -70,6 +75,7 @@
mHandler = handler;
mResources = resources;
mUserManager = userManager;
+ mWindowManagerService = windowManagerService;
}
/**
@@ -81,6 +87,13 @@
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 eab381c..65c5562 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.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 @@
private TestableResources mTestableResources;
@Mock
private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+ @Mock
+ private IWindowManager mWindowManagerService;
@Before
public void setUp() {
@@ -62,6 +65,7 @@
Handler.getMain(),
mTestableResources.getResources(),
(UserManager) mContext.getSystemService(Context.USER_SERVICE),
+ mWindowManagerService,
mOverlayViewGlobalStateController
);
@@ -125,8 +129,10 @@
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 0000000..9f26d85
--- /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 47e6645..6dcc9dc 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 @@
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 015079f..d5fe9b2 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 @@
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 86fe10d..cb8995a 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 @@
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 void expandFromStack(
@Nullable Runnable after, @Nullable Runnable leadBubbleEndAction) {
+ mPreparingToCollapse = false;
mAnimatingCollapse = false;
mAnimatingExpand = true;
mAfterExpand = after;
@@ -165,9 +174,20 @@
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 @@
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 @@
@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 d4d4d2a..eed5531 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.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 45ba1e6..496741b 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.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 647dacc..b9f1666 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 @@
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 @@
*/
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 353367e..ee02b85 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 @@
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 @@
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 2365e67..0a84f5e 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 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 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 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 @@
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 @@
}
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 @@
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 65d3572..858a7e2 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 @@
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 fc8c8db..825919f 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.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.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 @@
/**
* 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 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 @@
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);
}
- populateSettingsList();
+
+ int settingsN = mAutoAddSettingList.size();
+ for (int i = 0; i < settingsN; i++) {
+ if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) {
+ mAutoAddSettingList.get(i).setListening(true);
+ }
+ }
}
- public void destroy() {
- mAutoTracker.destroy();
+ protected void stopListening() {
mHotspotController.removeCallback(mHotspotCallback);
mDataSaverController.removeCallback(mDataSaverListener);
mManagedProfileController.removeCallback(mProfileCallback);
@@ -116,6 +133,11 @@
}
}
+ public void destroy() {
+ stopListening();
+ mAutoTracker.destroy();
+ }
+
/**
* Populates a list with the pairs setting:spec in the config resource.
* <p>
@@ -137,17 +159,39 @@
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 daefef5..c211de0 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 @@
}
};
+ // 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 @@
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
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
rename to packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
index d2776d2..693c270 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 @@
* 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 0ae9461..61f5a7b 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 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 @@
@SmallTest
public class AutoAddTrackerTest extends SysuiTestCase {
+ private static final int USER = 0;
+
private AutoAddTracker mAutoTracker;
@Before
@@ -51,7 +54,7 @@
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 @@
@Test
public void testChangeFromBackup() {
- mAutoTracker = new AutoAddTracker(mContext);
+ mAutoTracker = new AutoAddTracker(mContext, USER);
assertFalse(mAutoTracker.isAdded(SAVER));
@@ -82,7 +85,7 @@
@Test
public void testSetAdded() {
- mAutoTracker = new AutoAddTracker(mContext);
+ mAutoTracker = new AutoAddTracker(mContext, USER);
assertFalse(mAutoTracker.isAdded(SAVER));
mAutoTracker.setTileAdded(SAVER);
@@ -94,16 +97,35 @@
@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 05cdd80..31779cd 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.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 @@
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 @@
}
);
- 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 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 @@
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 f14def6a..fd6f171 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 @@
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 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 9dace70..3c6e8d8 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 @@
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 @@
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 @@
// 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 @@
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 @@
* @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 @@
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 @@
* @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 @@
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 1d45f12..48a600d 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 final String[] provisioningApp;
public final String provisioningAppNoUi;
public final int provisioningCheckPeriod;
- public final String provisioningResponse;
public final int activeDataSubId;
@@ -142,13 +141,10 @@
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 @@
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 354e753..72fa916 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.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.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 @@
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 @@
}
@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 @@
.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 3121863..1999ad7 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 @@
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 @@
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 @@
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 8d4efed..5787f7c4 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.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 @@
} 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 @@
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 @@
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 098b2ef..4e1a234 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 @@
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 @@
}
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::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 10ad07c..7b624ca 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 @@
/**
* 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 @@
@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 4127fec..c4d1211 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.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 @@
}
@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 @@
}
@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);