Merge "AOD: Update wakeup animation" into oc-dev
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index d61fb97..fccc877 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -938,7 +938,6 @@
private void sendAuthenticatedFailed() {
if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationFailed();
- mAuthenticationCallback = null;
}
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 28bdacf..c5c743b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -427,7 +427,13 @@
* @param size The new number of bytes in the Parcel.
*/
public final void setDataSize(int size) {
- updateNativeSize(nativeSetDataSize(mNativePtr, size));
+ // STOPSHIP: Try/catch for exception is for temporary debug. Remove once bug resolved
+ try {
+ updateNativeSize(nativeSetDataSize(mNativePtr, size));
+ } catch (IllegalArgumentException iae) {
+ Log.e(TAG,"Caught Exception representing a known bug in Parcel",iae);
+ Log.wtfStack(TAG, "This flow is using SetDataSize incorrectly");
+ }
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0840549..cf44c7d 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8315,6 +8315,17 @@
public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
/**
+ * Value to specify if Wi-Fi Wakeup is available.
+ *
+ * Wi-Fi Wakeup will only operate if it's available
+ * and {@link #WIFI_WAKEUP_ENABLED} is true.
+ *
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ public static final String WIFI_WAKEUP_AVAILABLE = "wifi_wakeup_available";
+
+ /**
* Value to specify whether network quality scores and badging should be shown in the UI.
*
* Type: int (0 for false, 1 for true)
@@ -9222,6 +9233,23 @@
public static final String SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants";
/**
+ * DevicePolicyManager specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * <pre>
+ * das_died_service_reconnect_backoff_sec (long)
+ * das_died_service_reconnect_backoff_increase (float)
+ * das_died_service_reconnect_max_backoff_sec (long)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * see also com.android.server.devicepolicy.DevicePolicyConstants
+ */
+ public static final String DEVICE_POLICY_CONSTANTS = "device_policy_constants";
+
+ /**
* Get the key that retrieves a bluetooth headset's priority.
* @hide
*/
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index d740a76..56f68d4 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -119,7 +119,11 @@
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
const status_t err = parcel->setDataSize(size);
- if (err != NO_ERROR) {
+ //STOPSHIP: check for BADFLO is for a temporary debug using wtf. Remove once bug resolved.
+ if (err == UNKNOWN_ERROR + 0xBADF10) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Attempt to resize (size = %d) Parcel would corrupt object memory", size);
+ } else if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
return parcel->getOpenAshmemSize();
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8b9b0e2..b7e8467 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -393,6 +393,12 @@
<!-- Activity name to enable wifi tethering after provisioning app succeeds -->
<string translatable="false" name="config_wifi_tether_enable">com.android.settings/.TetherService</string>
+ <!-- Controls the WiFi wakeup feature.
+ 0 = Not available.
+ 1 = Available.
+ -->
+ <integer translatable="false" name="config_wifi_wakeup_available">0</integer>
+
<!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
<!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
<!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2bd9ab9..31d13c9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2085,6 +2085,7 @@
<java-symbol type="string" name="config_mobile_hotspot_provision_response" />
<java-symbol type="integer" name="config_mobile_hotspot_provision_check_period" />
<java-symbol type="string" name="config_wifi_tether_enable" />
+ <java-symbol type="integer" name="config_wifi_wakeup_available" />
<java-symbol type="bool" name="config_intrusiveNotificationLed" />
<java-symbol type="dimen" name="preference_fragment_padding_bottom" />
<java-symbol type="dimen" name="preference_fragment_padding_side" />
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 14e2a85..455d9cb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2899,7 +2899,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 144;
+ private static final int SETTINGS_VERSION = 145;
private final int mUserId;
@@ -3481,6 +3481,25 @@
currentVersion = 144;
}
+ if (currentVersion == 144) {
+ // Version 145: Set the default value for WIFI_WAKEUP_AVAILABLE.
+ if (userId == UserHandle.USER_SYSTEM) {
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+ final Setting currentSetting = globalSettings.getSettingLocked(
+ Settings.Global.WIFI_WAKEUP_AVAILABLE);
+ if (currentSetting.isNull()) {
+ final int defaultValue = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_wifi_wakeup_available);
+ globalSettings.insertSettingLocked(
+ Settings.Global.WIFI_WAKEUP_AVAILABLE,
+ String.valueOf(defaultValue),
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
+ currentVersion = 145;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/res/layout/pip_dismiss_view.xml b/packages/SystemUI/res/layout/pip_dismiss_view.xml
index 058f59f..2cc4b22 100644
--- a/packages/SystemUI/res/layout/pip_dismiss_view.xml
+++ b/packages/SystemUI/res/layout/pip_dismiss_view.xml
@@ -17,7 +17,6 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/pip_dismiss_gradient_height"
- android:background="@drawable/pip_dismiss_scrim"
android:alpha="0">
<TextView
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5374673..63abee7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -748,7 +748,7 @@
<dimen name="recents_fast_fling_velocity">600dp</dimen>
<!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
- <dimen name="pip_dismiss_gradient_height">196dp</dimen>
+ <dimen name="pip_dismiss_gradient_height">176dp</dimen>
<!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
<dimen name="pip_dismiss_text_bottom_margin">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 64b5f4b..c656b17 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1892,21 +1892,6 @@
<!-- PiP BTW notification description. [CHAR LIMIT=NONE] -->
<string name="pip_notification_message">If you don’t want <xliff:g id="name" example="Google Maps">%s</xliff:g> to use this feature, tap to open settings and turn it off.</string>
- <!-- PiP section of the tuner. [CHAR LIMIT=NONE] -->
- <string name="picture_in_picture" translatable="false">Picture-in-Picture</string>
-
- <!-- PiP minimize title. [CHAR LIMIT=NONE]-->
- <string name="pip_minimize_title" translatable="false">Minimize</string>
-
- <!-- PiP minimize description. [CHAR LIMIT=NONE] -->
- <string name="pip_minimize_description" translatable="false">Drag or fling the PIP to the edges of the screen to minimize it.</string>
-
- <!-- PiP fling to dismiss title. [CHAR LIMIT=NONE]-->
- <string name="pip_fling_dismiss_title" translatable="false">Fling to dismiss</string>
-
- <!-- PiP fling to dismiss description. [CHAR LIMIT=NONE] -->
- <string name="pip_fling_dismiss_description" translatable="false">Fling from anywhere on the screen to the bottom of the screen to dismiss the PIP.</string>
-
<!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
<string name="pip_play">Play</string>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 24f29c8..223dafd 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -122,24 +122,6 @@
</PreferenceScreen>
<PreferenceScreen
- android:key="picture_in_picture"
- android:title="@string/picture_in_picture">
-
- <com.android.systemui.tuner.TunerSwitch
- android:key="pip_minimize"
- android:title="@string/pip_minimize_title"
- android:summary="@string/pip_minimize_description"
- sysui:defValue="false" />
-
- <com.android.systemui.tuner.TunerSwitch
- android:key="pip_fling_dismiss"
- android:title="@string/pip_fling_dismiss_title"
- android:summary="@string/pip_fling_dismiss_description"
- sysui:defValue="false" />
-
- </PreferenceScreen>
-
- <PreferenceScreen
android:key="doze"
android:title="@string/tuner_doze">
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index d4d69ff..f0ff22d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -280,6 +280,9 @@
}
public void setDark(boolean dark) {
+ if (mDark == dark) {
+ return;
+ }
mDark = dark;
final int N = mClockContainer.getChildCount();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index afb62fc..e1a7e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -21,6 +21,7 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -72,6 +73,11 @@
mDismissView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
mDismissView.forceHasOverlappingRendering(false);
+ // Set the gradient background
+ Drawable gradient = mContext.getResources().getDrawable(R.drawable.pip_dismiss_scrim);
+ gradient.setAlpha((int) (255 * 0.85f));
+ mDismissView.setBackground(gradient);
+
// Adjust bottom margins of the text
View text = mDismissView.findViewById(R.id.pip_dismiss_text);
FrameLayout.LayoutParams tlp = (FrameLayout.LayoutParams) text.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 71d3d35..199b027 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -54,11 +54,13 @@
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
* the PIP.
*/
-public class PipTouchHandler implements TunerService.Tunable {
+public class PipTouchHandler {
private static final String TAG = "PipTouchHandler";
- private static final String TUNER_KEY_MINIMIZE = "pip_minimize";
- private static final String TUNER_KEY_FLING_DISMISS = "pip_fling_dismiss";
+ // Allow the PIP to be dragged to the edge of the screen to be minimized.
+ private static final boolean ENABLE_MINIMIZE = false;
+ // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
+ private static final boolean ENABLE_FLING_DISMISS = false;
// These values are used for metrics and should never change
private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
@@ -113,11 +115,6 @@
}
};
- // Allow the PIP to be dragged to the edge of the screen to be minimized.
- private boolean mEnableMinimize = false;
- // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
- private boolean mEnableFlingToDismiss = false;
-
// Behaviour states
private int mMenuState;
private boolean mIsMinimized;
@@ -196,10 +193,6 @@
mExpandedShortestEdgeSize = context.getResources().getDimensionPixelSize(
R.dimen.pip_expanded_shortest_edge_size);
- // Register any tuner settings changes
- Dependency.get(TunerService.class).addTunable(this, TUNER_KEY_MINIMIZE);
- Dependency.get(TunerService.class).addTunable(this, TUNER_KEY_FLING_DISMISS);
-
// Register the listener for input consumer touch events
inputConsumerController.setTouchListener(this::handleTouchEvent);
inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
@@ -240,18 +233,6 @@
}
}
- @Override
- public void onTuningChanged(String key, String newValue) {
- switch (key) {
- case TUNER_KEY_MINIMIZE:
- mEnableMinimize = newValue == null ? false : Integer.parseInt(newValue) != 0;
- break;
- case TUNER_KEY_FLING_DISMISS:
- mEnableFlingToDismiss = newValue == null ? false : Integer.parseInt(newValue) != 0;
- break;
- }
- }
-
public void onConfigurationChanged() {
mMotionHelper.onConfigurationChanged();
mMotionHelper.synchronizePinnedStackBounds();
@@ -451,7 +432,7 @@
* Sets the minimized state.
*/
void setMinimizedStateInternal(boolean isMinimized) {
- if (!mEnableMinimize) {
+ if (!ENABLE_MINIMIZE) {
return;
}
setMinimizedState(isMinimized, false /* fromController */);
@@ -461,7 +442,7 @@
* Sets the minimized state.
*/
void setMinimizedState(boolean isMinimized, boolean fromController) {
- if (!mEnableMinimize) {
+ if (!ENABLE_MINIMIZE) {
return;
}
if (mIsMinimized != isMinimized) {
@@ -597,7 +578,7 @@
final PointF lastDelta = touchState.getLastTouchDelta();
float left = mTmpBounds.left + lastDelta.x;
float top = mTmpBounds.top + lastDelta.y;
- if (!touchState.allowDraggingOffscreen() || !mEnableMinimize) {
+ if (!touchState.allowDraggingOffscreen() || !ENABLE_MINIMIZE) {
left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
}
if (ENABLE_DISMISS_DRAG_TO_EDGE) {
@@ -645,7 +626,7 @@
final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
final float velocity = PointF.length(vel.x, vel.y);
final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
- final boolean isUpWithinDimiss = mEnableFlingToDismiss
+ final boolean isUpWithinDimiss = ENABLE_FLING_DISMISS
&& touchState.getLastTouchPosition().y >= mMovementBounds.bottom
&& mMotionHelper.isGestureToDismissArea(mMotionHelper.getBounds(), vel.x,
vel.y, isFling);
@@ -666,7 +647,7 @@
if (touchState.isDragging()) {
final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
&& (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
- if (mEnableMinimize &&
+ if (ENABLE_MINIMIZE &&
!mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
// Pip should be minimized
setMinimizedStateInternal(true);
@@ -758,7 +739,7 @@
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + ENABLE_DISMISS_DRAG_TO_EDGE);
- pw.println(innerPrefix + "mEnableMinimize=" + mEnableMinimize);
+ pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
mSnapAlgorithm.dump(pw, innerPrefix);
mTouchState.dump(pw, innerPrefix);
mMotionHelper.dump(pw, innerPrefix);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index 005206f..0b89dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -22,6 +22,8 @@
import android.content.Intent;
import android.content.IntentFilter;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@@ -32,7 +34,12 @@
private Consumer<Integer> mCallback = this::onUserSwitched;
public CurrentUserTracker(Context context) {
- mUserReceiver = UserReceiver.getInstance(context);
+ this(UserReceiver.getInstance(context));
+ }
+
+ @VisibleForTesting
+ CurrentUserTracker(UserReceiver receiver) {
+ mUserReceiver = receiver;
}
public int getCurrentUserId() {
@@ -49,7 +56,8 @@
public abstract void onUserSwitched(int newUserId);
- private static class UserReceiver extends BroadcastReceiver {
+ @VisibleForTesting
+ static class UserReceiver extends BroadcastReceiver {
private static UserReceiver sInstance;
private Context mAppContext;
@@ -58,7 +66,8 @@
private List<Consumer<Integer>> mCallbacks = new ArrayList<>();
- private UserReceiver(Context context) {
+ @VisibleForTesting
+ UserReceiver(Context context) {
mAppContext = context.getApplicationContext();
}
@@ -105,8 +114,12 @@
private void notifyUserSwitched(int newUserId) {
if (mCurrentUserId != newUserId) {
mCurrentUserId = newUserId;
- for (Consumer<Integer> consumer : mCallbacks) {
- consumer.accept(newUserId);
+ List<Consumer<Integer>> callbacks = new ArrayList<>(mCallbacks);
+ for (Consumer<Integer> consumer : callbacks) {
+ // Accepting may modify this list
+ if (mCallbacks.contains(consumer)) {
+ consumer.accept(newUserId);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index c78ec83..7370c03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -22,6 +22,7 @@
import android.view.animation.PathInterpolator;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationUtils;
/**
* Utility class to calculate the clock position and top padding of notifications on Keyguard.
@@ -69,7 +70,7 @@
private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
private int mClockBottom;
- private boolean mDark;
+ private float mDarkAmount;
/**
* Refreshes the dimension values.
@@ -89,7 +90,7 @@
public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount,
- int clockBottom, boolean dark) {
+ int clockBottom, float dark) {
mMaxKeyguardNotifications = maxKeyguardNotifications;
mMaxPanelHeight = maxPanelHeight;
mExpandedHeight = expandedHeight;
@@ -98,7 +99,7 @@
mKeyguardStatusHeight = keyguardStatusHeight;
mEmptyDragAmount = emptyDragAmount;
mClockBottom = clockBottom;
- mDark = dark;
+ mDarkAmount = dark;
}
public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
@@ -120,9 +121,11 @@
result.clockY,
y + getClockNotificationsPadding() + mKeyguardStatusHeight);
result.clockAlpha = getClockAlpha(result.clockScale);
- if (mDark) {
- result.stackScrollerPadding = mClockBottom + y;
- }
+
+ result.stackScrollerPadding = (int) NotificationUtils.interpolate(
+ result.stackScrollerPadding,
+ mClockBottom + y,
+ mDarkAmount);
}
private float getClockScale(int notificationPadding, int clockY, int startPadding) {
@@ -149,7 +152,11 @@
}
private int getClockY() {
- return (int) (getClockYFraction() * mHeight);
+ // Dark: Align the bottom edge of the clock at one third:
+ // clockBottomEdge = result - mKeyguardStatusHeight / 2 + mClockBottom
+ float clockYDark = (0.33f * mHeight + (float) mKeyguardStatusHeight / 2 - mClockBottom);
+ float clockYRegular = getClockYFraction() * mHeight;
+ return (int) NotificationUtils.interpolate(clockYRegular, clockYDark, mDarkAmount);
}
private float getClockYExpansionAdjustment() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index f7480bc..c5853ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -31,6 +31,7 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -91,6 +92,19 @@
public static final long DOZE_ANIMATION_DURATION = 700;
+ private static final FloatProperty<NotificationPanelView> SET_DARK_AMOUNT_PROPERTY =
+ new FloatProperty<NotificationPanelView>("mDarkAmount") {
+ @Override
+ public void setValue(NotificationPanelView object, float value) {
+ object.setDarkAmount(value);
+ }
+
+ @Override
+ public Float get(NotificationPanelView object) {
+ return object.mDarkAmount;
+ }
+ };
+
private KeyguardAffordanceHelper mAffordanceHelper;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private KeyguardStatusBarView mKeyguardStatusBar;
@@ -211,9 +225,10 @@
private boolean mShowIconsWhenExpanded;
private int mIndicationBottomPadding;
private boolean mIsFullWidth;
- private boolean mDark;
+ private float mDarkAmount;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mNoVisibleNotifications = true;
+ private ValueAnimator mDarkAnimator;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -397,7 +412,7 @@
mKeyguardStatusView.getHeight(),
mEmptyDragAmount,
mKeyguardStatusView.getClockBottom(),
- mDark);
+ mDarkAmount);
mClockPositionAlgorithm.run(mClockPositionResult);
if (animate || mClockAnimator != null) {
startClockAnimation(mClockPositionResult.clockY);
@@ -2473,9 +2488,27 @@
}
}
- public void setDark(boolean dark) {
- mDark = dark;
- mKeyguardStatusView.setDark(dark);
+ public void setDark(boolean dark, boolean animate) {
+ float darkAmount = dark ? 1 : 0;
+ if (mDarkAmount == darkAmount) {
+ return;
+ }
+ if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
+ mDarkAnimator.cancel();
+ }
+ if (animate) {
+ mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, darkAmount);
+ mDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mDarkAnimator.start();
+ } else {
+ setDarkAmount(darkAmount);
+ }
+ }
+
+ private void setDarkAmount(float amount) {
+ mDarkAmount = amount;
+ mKeyguardStatusView.setDark(amount == 1);
positionClockAndNotifications();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a597e5b..fc73c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4360,7 +4360,7 @@
mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
mScrimController.setDozing(mDozing);
mKeyguardIndicationController.setDozing(mDozing);
- mNotificationPanel.setDark(mDozing);
+ mNotificationPanel.setDark(mDozing, animate);
updateQsExpansionEnabled();
// Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserTrackerTest.java
new file mode 100644
index 0000000..9e15a05
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserTrackerTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.settings;
+
+import android.content.Intent;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Testing functionality of the current user tracker
+ */
+public class CurrentUserTrackerTest extends SysuiTestCase {
+
+ private CurrentUserTracker mTracker;
+ private CurrentUserTracker.UserReceiver mReceiver;
+
+ @Before
+ public void setUp() {
+ mReceiver = new CurrentUserTracker.UserReceiver(getContext());
+ mTracker = new CurrentUserTracker(mReceiver) {
+ @Override
+ public void onUserSwitched(int newUserId) {
+ stopTracking();
+ }
+ };
+ }
+
+ @Test
+ public void testBroadCastDoesntCrashOnConcurrentModification() {
+ mTracker.startTracking();
+ CurrentUserTracker secondTracker = new CurrentUserTracker(mReceiver) {
+ @Override
+ public void onUserSwitched(int newUserId) {
+ stopTracking();
+ }
+ };
+ secondTracker.startTracking();
+ triggerUserSwitch();
+ }
+ /**
+ * Simulates a user switch event.
+ */
+ private void triggerUserSwitch() {
+ Intent intent = new Intent(Intent.ACTION_USER_SWITCHED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, 1);
+ mReceiver.onReceive(getContext(), intent);
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 4dd0b35..0999580 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -144,6 +144,9 @@
final int userId = users.get(i).id;
final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL);
if (disabled) {
+ if (disabled) {
+ Slog.i(TAG, "Disabling Autofill for user " + userId);
+ }
mDisabledUsers.put(userId, disabled);
}
}
@@ -155,11 +158,12 @@
if (disabledBefore == disabledNow) {
// Nothing changed, do nothing.
if (sDebug) {
- Slog.d(TAG, "Restriction not changed for user " + userId + ": "
+ Slog.d(TAG, "Autofill restriction did not change for user " + userId + ": "
+ bundleToString(newRestrictions));
return;
}
}
+ Slog.i(TAG, "Updating Autofill for user " + userId + ": disabled=" + disabledNow);
mDisabledUsers.put(userId, disabledNow);
updateCachedServiceLocked(userId, disabledNow);
}
@@ -606,7 +610,7 @@
pw.println("Usage: dumpsys autofill [--ui-only|--no-history]");
return;
default:
- throw new IllegalArgumentException("Invalid dump arg: " + arg);
+ Slog.w(TAG, "Ignoring invalid dump arg: " + arg);
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 70771e8..c455eda 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -384,6 +384,7 @@
}
}
if (response == null) {
+ if (sVerbose) Slog.v(TAG, "canceling session " + id + " when server returned null");
if ((requestFlags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
}
@@ -906,7 +907,10 @@
// If it's not, then check if it it should start a partition.
if (shouldStartNewPartitionLocked(id)) {
- if (sDebug) Slog.d(TAG, "Starting partition for view id " + id);
+ if (sDebug) {
+ Slog.d(TAG, "Starting partition for view id " + id + ": "
+ + viewState.getStateAsString());
+ }
viewState.setState(ViewState.STATE_STARTED_PARTITION);
requestNewFillResponseLocked(flags);
}
@@ -1344,15 +1348,19 @@
}
void dumpLocked(String prefix, PrintWriter pw) {
+ final String prefix2 = prefix + " ";
pw.print(prefix); pw.print("id: "); pw.println(id);
pw.print(prefix); pw.print("uid: "); pw.println(uid);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
- pw.print(prefix); pw.print("mResponses: "); pw.println(mResponses);
+ pw.print(prefix); pw.print("mResponses: "); pw.println(mResponses.size());
+ for (int i = 0; i < mResponses.size(); i++) {
+ pw.print(prefix2); pw.print('#'); pw.print(i); pw.print(' ');
+ pw.println(mResponses.valueAt(i));
+ }
pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
pw.print(prefix); pw.print("mIsSaving: "); pw.println(mIsSaving);
- final String prefix2 = prefix + " ";
for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey());
entry.getValue().dump(prefix2, pw);
diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java
index c34c097..52eaca1 100644
--- a/services/core/java/com/android/server/am/PersistentConnection.java
+++ b/services/core/java/com/android/server/am/PersistentConnection.java
@@ -22,32 +22,77 @@
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
/**
* Connects to a given service component on a given user.
*
- * - Call {@link #connect()} to create a connection.
- * - Call {@link #disconnect()} to disconnect. Make sure to disconnect when the user stops.
+ * - Call {@link #bind()} to create a connection.
+ * - Call {@link #unbind()} to disconnect. Make sure to disconnect when the user stops.
*
* Add onConnected/onDisconnected callbacks as needed.
+ *
+ * When the target process gets killed (by OOM-killer, etc), then the activity manager will
+ * re-connect the connection automatically, in which case onServiceDisconnected() gets called
+ * and then onServiceConnected().
+ *
+ * However sometimes the activity manager just "kills" the connection -- like when the target
+ * package gets updated or the target process crashes multiple times in a row, in which case
+ * onBindingDied() gets called. This class handles this case by re-connecting in the time
+ * {@link #mRebindBackoffMs}. If this happens again, this class increases the back-off time
+ * by {@link #mRebindBackoffIncrease} and retry. The back-off time is capped at
+ * {@link #mRebindMaxBackoffMs}.
+ *
+ * The back-off time will never be reset until {@link #unbind()} and {@link #bind()} are called
+ * explicitly.
+ *
+ * NOTE: This class does *not* handle package-updates -- i.e. even if the binding dies due to
+ * the target package being updated, this class won't reconnect. This is because this class doesn't
+ * know what to do when the service component has gone missing, for example. If the user of this
+ * class wants to restore the connection, then it should call {@link #unbind()} and {@link #bind}
+ * explicitly.
*/
public abstract class PersistentConnection<T> {
private final Object mLock = new Object();
+ private final static boolean DEBUG = false;
+
private final String mTag;
private final Context mContext;
private final Handler mHandler;
private final int mUserId;
private final ComponentName mComponentName;
+ private long mNextBackoffMs;
+
+ private final long mRebindBackoffMs;
+ private final double mRebindBackoffIncrease;
+ private final long mRebindMaxBackoffMs;
+
+ private long mReconnectTime;
+
+ // TODO too many booleans... Should clean up.
+
@GuardedBy("mLock")
- private boolean mStarted;
+ private boolean mBound;
+
+ /**
+ * Whether {@link #bind()} has been called and {@link #unbind()} hasn't been yet; meaning this
+ * is the expected bind state from the caller's point of view.
+ */
+ @GuardedBy("mLock")
+ private boolean mShouldBeBound;
+
+ @GuardedBy("mLock")
+ private boolean mRebindScheduled;
@GuardedBy("mLock")
private boolean mIsConnected;
@@ -59,6 +104,14 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
+ if (!mBound) {
+ // Callback came in after PersistentConnection.unbind() was called.
+ // We just ignore this.
+ // (We've already called unbindService() already in unbind)
+ Slog.w(mTag, "Connected: " + mComponentName.flattenToShortString()
+ + " u" + mUserId + " but not bound, ignore.");
+ return;
+ }
Slog.i(mTag, "Connected: " + mComponentName.flattenToShortString()
+ " u" + mUserId);
@@ -76,15 +129,41 @@
cleanUpConnectionLocked();
}
}
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ // Activity manager gave up; we'll schedule a re-connect by ourselves.
+ synchronized (mLock) {
+ if (!mBound) {
+ // Callback came in late?
+ Slog.w(mTag, "Binding died: " + mComponentName.flattenToShortString()
+ + " u" + mUserId + " but not bound, ignore.");
+ return;
+ }
+
+ Slog.w(mTag, "Binding died: " + mComponentName.flattenToShortString()
+ + " u" + mUserId);
+ scheduleRebindLocked();
+ }
+ }
};
+ private final Runnable mBindForBackoffRunnable = () -> bindForBackoff();
+
public PersistentConnection(@NonNull String tag, @NonNull Context context,
- @NonNull Handler handler, int userId, @NonNull ComponentName componentName) {
+ @NonNull Handler handler, int userId, @NonNull ComponentName componentName,
+ long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
mTag = tag;
mContext = context;
mHandler = handler;
mUserId = userId;
mComponentName = componentName;
+
+ mRebindBackoffMs = rebindBackoffSeconds * 1000;
+ mRebindBackoffIncrease = rebindBackoffIncrease;
+ mRebindMaxBackoffMs = rebindMaxBackoffSeconds * 1000;
+
+ mNextBackoffMs = mRebindBackoffMs;
}
public final ComponentName getComponentName() {
@@ -92,6 +171,27 @@
}
/**
+ * @return whether {@link #bind()} has been called and {@link #unbind()} hasn't.
+ *
+ * Note when the AM gives up on connection, this class detects it and un-bind automatically,
+ * and schedule rebind, and {@link #isBound} returns false when it's waiting for a retry.
+ */
+ public final boolean isBound() {
+ synchronized (mLock) {
+ return mBound;
+ }
+ }
+
+ /**
+ * @return whether re-bind is scheduled after the AM gives up on a connection.
+ */
+ public final boolean isRebindScheduled() {
+ synchronized (mLock) {
+ return mRebindScheduled;
+ }
+ }
+
+ /**
* @return whether connected.
*/
public final boolean isConnected() {
@@ -112,23 +212,51 @@
/**
* Connects to the service.
*/
- public final void connect() {
+ public final void bind() {
synchronized (mLock) {
- if (mStarted) {
+ mShouldBeBound = true;
+
+ bindInnerLocked(/* resetBackoff= */ true);
+ }
+ }
+
+ public final void bindInnerLocked(boolean resetBackoff) {
+ unscheduleRebindLocked();
+
+ if (mBound) {
+ return;
+ }
+ mBound = true;
+
+ if (resetBackoff) {
+ // Note this is the only place we reset the backoff time.
+ mNextBackoffMs = mRebindBackoffMs;
+ }
+
+ final Intent service = new Intent().setComponent(mComponentName);
+
+ if (DEBUG) {
+ Slog.d(mTag, "Attempting to connect to " + mComponentName);
+ }
+
+ final boolean success = mContext.bindServiceAsUser(service, mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ mHandler, UserHandle.of(mUserId));
+
+ if (!success) {
+ Slog.e(mTag, "Binding: " + service.getComponent() + " u" + mUserId
+ + " failed.");
+ }
+ }
+
+ final void bindForBackoff() {
+ synchronized (mLock) {
+ if (!mShouldBeBound) {
+ // Race condition -- by the time we got here, unbind() has already been called.
return;
}
- mStarted = true;
- final Intent service = new Intent().setComponent(mComponentName);
-
- final boolean success = mContext.bindServiceAsUser(service, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- mHandler, UserHandle.of(mUserId));
-
- if (!success) {
- Slog.e(mTag, "Binding: " + service.getComponent() + " u" + mUserId
- + " failed.");
- }
+ bindInnerLocked(/* resetBackoff= */ false);
}
}
@@ -140,16 +268,46 @@
/**
* Disconnect from the service.
*/
- public final void disconnect() {
+ public final void unbind() {
synchronized (mLock) {
- if (!mStarted) {
- return;
- }
- Slog.i(mTag, "Stopping: " + mComponentName.flattenToShortString() + " u" + mUserId);
- mStarted = false;
- mContext.unbindService(mServiceConnection);
+ mShouldBeBound = false;
- cleanUpConnectionLocked();
+ unbindLocked();
+ }
+ }
+
+ private final void unbindLocked() {
+ unscheduleRebindLocked();
+
+ if (!mBound) {
+ return;
+ }
+ Slog.i(mTag, "Stopping: " + mComponentName.flattenToShortString() + " u" + mUserId);
+ mBound = false;
+ mContext.unbindService(mServiceConnection);
+
+ cleanUpConnectionLocked();
+ }
+
+ void unscheduleRebindLocked() {
+ injectRemoveCallbacks(mBindForBackoffRunnable);
+ mRebindScheduled = false;
+ }
+
+ void scheduleRebindLocked() {
+ unbindLocked();
+
+ if (!mRebindScheduled) {
+ Slog.i(mTag, "Scheduling to reconnect in " + mNextBackoffMs + " ms (uptime)");
+
+ mReconnectTime = injectUptimeMillis() + mNextBackoffMs;
+
+ injectPostAtTime(mBindForBackoffRunnable, mReconnectTime);
+
+ mNextBackoffMs = Math.min(mRebindMaxBackoffMs,
+ (long) (mNextBackoffMs * mRebindBackoffIncrease));
+
+ mRebindScheduled = true;
}
}
@@ -160,9 +318,57 @@
synchronized (mLock) {
pw.print(prefix);
pw.print(mComponentName.flattenToShortString());
- pw.print(mStarted ? " [started]" : " [not started]");
+ pw.print(mBound ? " [bound]" : " [not bound]");
pw.print(mIsConnected ? " [connected]" : " [not connected]");
+ if (mRebindScheduled) {
+ pw.print(" reconnect in ");
+ TimeUtils.formatDuration((mReconnectTime - injectUptimeMillis()), pw);
+ }
pw.println();
+
+ pw.print(prefix);
+ pw.print(" Next backoff(sec): ");
+ pw.print(mNextBackoffMs / 1000);
}
}
+
+ @VisibleForTesting
+ void injectRemoveCallbacks(Runnable r) {
+ mHandler.removeCallbacks(r);
+ }
+
+ @VisibleForTesting
+ void injectPostAtTime(Runnable r, long uptimeMillis) {
+ mHandler.postAtTime(r, uptimeMillis);
+ }
+
+ @VisibleForTesting
+ long injectUptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
+ @VisibleForTesting
+ long getNextBackoffMsForTest() {
+ return mNextBackoffMs;
+ }
+
+ @VisibleForTesting
+ long getReconnectTimeForTest() {
+ return mReconnectTime;
+ }
+
+ @VisibleForTesting
+ ServiceConnection getServiceConnectionForTest() {
+ return mServiceConnection;
+ }
+
+ @VisibleForTesting
+ Runnable getBindForBackoffRunnableForTest() {
+ return mBindForBackoffRunnable;
+ }
+
+ @VisibleForTesting
+ boolean shouldBeBoundForTest() {
+ return mShouldBeBound;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index c7b8f02..60f204d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -53,6 +53,7 @@
private final DevicePolicyManagerService mService;
private final DevicePolicyManagerService.Injector mInjector;
+ private final DevicePolicyConstants mConstants;
private final Handler mHandler; // needed?
@@ -66,7 +67,10 @@
private class DevicePolicyServiceConnection
extends PersistentConnection<IDeviceAdminService> {
public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) {
- super(TAG, mContext, mHandler, userId, componentName);
+ super(TAG, mContext, mHandler, userId, componentName,
+ mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC,
+ mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE,
+ mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
}
@Override
@@ -81,11 +85,13 @@
@GuardedBy("mLock")
private final SparseArray<DevicePolicyServiceConnection> mConnections = new SparseArray<>();
- public DeviceAdminServiceController(DevicePolicyManagerService service) {
+ public DeviceAdminServiceController(DevicePolicyManagerService service,
+ DevicePolicyConstants constants) {
mService = service;
mInjector = service.mInjector;
mContext = mInjector.mContext;
mHandler = new Handler(BackgroundThread.get().getLooper());
+ mConstants = constants;
}
/**
@@ -150,9 +156,11 @@
final PersistentConnection<IDeviceAdminService> existing =
mConnections.get(userId);
if (existing != null) {
- if (existing.getComponentName().equals(service.getComponentName())) {
- return;
- }
+ // Note even when we're already connected to the same service, the binding
+ // would have died at this point due to a package update. So we disconnect
+ // anyway and re-connect.
+ debug("Disconnecting from existing service connection.",
+ packageName, userId);
disconnectServiceOnUserLocked(userId, actionForLog);
}
@@ -164,7 +172,7 @@
new DevicePolicyServiceConnection(
userId, service.getComponentName());
mConnections.put(userId, conn);
- conn.connect();
+ conn.bind();
}
} finally {
mInjector.binderRestoreCallingIdentity(token);
@@ -190,7 +198,7 @@
if (conn != null) {
debug("Stopping service for u%d if already running for %s.",
userId, actionForLog);
- conn.disconnect();
+ conn.unbind();
mConnections.remove(userId);
}
}
@@ -209,6 +217,7 @@
final DevicePolicyServiceConnection con = mConnections.valueAt(i);
con.dump(prefix + " ", pw);
}
+ pw.println();
}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
new file mode 100644
index 0000000..616c669
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Constants that are configurable via the global settings for {@link DevicePolicyManagerService}.
+ *
+ * Example of setting the values for testing.
+ * adb shell settings put global device_policy_constants das_died_service_reconnect_backoff_sec=10,das_died_service_reconnect_backoff_increase=1.5,das_died_service_reconnect_max_backoff_sec=30
+ */
+public class DevicePolicyConstants {
+ private static final String TAG = DevicePolicyManagerService.LOG_TAG;
+
+ private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY
+ = "das_died_service_reconnect_backoff_sec";
+
+ private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY
+ = "das_died_service_reconnect_backoff_increase";
+
+ private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY
+ = "das_died_service_reconnect_max_backoff_sec";
+
+ /**
+ * The back-off before re-connecting, when a service binding died, due to the owner
+ * crashing repeatedly.
+ */
+ public final long DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC;
+
+ /**
+ * The exponential back-off increase factor when a binding dies multiple times.
+ */
+ public final double DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE;
+
+ /**
+ * The max back-off
+ */
+ public final long DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC;
+
+ private DevicePolicyConstants(String settings) {
+
+ final KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(settings);
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on
+ // with defaults.
+ Slog.e(TAG, "Bad device policy settings: " + settings);
+ }
+
+ long dasDiedServiceReconnectBackoffSec = parser.getLong(
+ DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));
+
+ double dasDiedServiceReconnectBackoffIncrease = parser.getFloat(
+ DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY, 2f);
+
+ long dasDiedServiceReconnectMaxBackoffSec = parser.getLong(
+ DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1));
+
+ // Set minimum: 5 seconds.
+ dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec);
+
+ // Set minimum: 1.0.
+ dasDiedServiceReconnectBackoffIncrease =
+ Math.max(1, dasDiedServiceReconnectBackoffIncrease);
+
+ // Make sure max >= default back off.
+ dasDiedServiceReconnectMaxBackoffSec = Math.max(dasDiedServiceReconnectBackoffSec,
+ dasDiedServiceReconnectMaxBackoffSec);
+
+ DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC = dasDiedServiceReconnectBackoffSec;
+ DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE = dasDiedServiceReconnectBackoffIncrease;
+ DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec;
+
+ }
+
+ public static DevicePolicyConstants loadFromString(String settings) {
+ return new DevicePolicyConstants(settings);
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.println("Constants:");
+
+ pw.print(prefix);
+ pw.print(" DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC: ");
+ pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
+
+ pw.print(prefix);
+ pw.print(" DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE: ");
+ pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
+
+ pw.print(prefix);
+ pw.print(" DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
+ pw.println( DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 87fb8c8..911bb2a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -141,6 +141,7 @@
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsInternal;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.security.IKeyChainAliasCallback;
import android.security.IKeyChainService;
import android.security.KeyChain;
@@ -364,6 +365,7 @@
final UserManagerInternal mUserManagerInternal;
final TelephonyManager mTelephonyManager;
private final LockPatternUtils mLockPatternUtils;
+ private final DevicePolicyConstants mConstants;
private final DeviceAdminServiceController mDeviceAdminServiceController;
/**
@@ -1447,7 +1449,10 @@
private void handlePackagesChanged(@Nullable String packageName, int userHandle) {
boolean removedAdmin = false;
- if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
+ if (VERBOSE_LOG) {
+ Slog.d(LOG_TAG, "Handling package changes package " + packageName
+ + " for user " + userHandle);
+ }
DevicePolicyData policy = getUserData(userHandle);
synchronized (this) {
for (int i = policy.mAdminList.size() - 1; i >= 0; i--) {
@@ -1760,6 +1765,10 @@
return Settings.Global.getInt(mContext.getContentResolver(), name, def);
}
+ String settingsGlobalGetString(String name) {
+ return Settings.Global.getString(mContext.getContentResolver(), name);
+ }
+
void settingsGlobalPutInt(String name, int value) {
Settings.Global.putInt(mContext.getContentResolver(), name, value);
}
@@ -1801,6 +1810,9 @@
mInjector = injector;
mContext = Preconditions.checkNotNull(injector.mContext);
mHandler = new Handler(Preconditions.checkNotNull(injector.getMyLooper()));
+ mConstants = DevicePolicyConstants.loadFromString(
+ mInjector.settingsGlobalGetString(Global.DEVICE_POLICY_CONSTANTS));
+
mOwners = Preconditions.checkNotNull(injector.newOwners());
mUserManager = Preconditions.checkNotNull(injector.getUserManager());
@@ -1823,7 +1835,7 @@
// Needed when mHasFeature == false, because it controls the certificate warning text.
mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
- mDeviceAdminServiceController = new DeviceAdminServiceController(this);
+ mDeviceAdminServiceController = new DeviceAdminServiceController(this, mConstants);
if (!mHasFeature) {
// Skip the rest of the initialization
@@ -7354,6 +7366,7 @@
synchronized (this) {
pw.println("Current Device Policy Manager state:");
+
mOwners.dump(" ", pw);
mDeviceAdminServiceController.dump(" ", pw);
int userCount = mUserData.size();
@@ -7380,7 +7393,9 @@
pw.print(" mPasswordOwner="); pw.println(policy.mPasswordOwner);
}
pw.println();
- pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
+ mConstants.dump(" ", pw);
+ pw.println();
+ pw.println(" Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
new file mode 100644
index 0000000..f287386
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.am;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.IDeviceAdminService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.test.AndroidTestCase;
+import android.util.Pair;
+
+import org.mockito.ArgumentMatchers;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+public class PersistentConnectionTest extends AndroidTestCase {
+ private static class MyConnection extends PersistentConnection<IDeviceAdminService> {
+ public long uptimeMillis = 12345;
+
+ public ArrayList<Pair<Runnable, Long>> scheduledRunnables = new ArrayList<>();
+
+ public MyConnection(String tag, Context context, Handler handler, int userId,
+ ComponentName componentName, long rebindBackoffSeconds,
+ double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
+ super(tag, context, handler, userId, componentName,
+ rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds);
+ }
+
+ @Override
+ protected IDeviceAdminService asInterface(IBinder binder) {
+ return (IDeviceAdminService) binder;
+ }
+
+ @Override
+ long injectUptimeMillis() {
+ return uptimeMillis;
+ }
+
+ @Override
+ void injectPostAtTime(Runnable r, long uptimeMillis) {
+ scheduledRunnables.add(Pair.create(r, uptimeMillis));
+ }
+
+ @Override
+ void injectRemoveCallbacks(Runnable r) {
+ for (int i = scheduledRunnables.size() - 1; i >= 0; i--) {
+ if (scheduledRunnables.get(i).first.equals(r)) {
+ scheduledRunnables.remove(i);
+ }
+ }
+ }
+
+ void elapse(long milliSeconds) {
+ uptimeMillis += milliSeconds;
+
+ // Fire the scheduled runnables.
+
+ // Note we collect first and then run all, because sometimes a scheduled runnable
+ // calls removeCallbacks.
+ final ArrayList<Runnable> list = new ArrayList<>();
+
+ for (int i = scheduledRunnables.size() - 1; i >= 0; i--) {
+ if (scheduledRunnables.get(i).second <= uptimeMillis) {
+ list.add(scheduledRunnables.get(i).first);
+ scheduledRunnables.remove(i);
+ }
+ }
+
+ Collections.reverse(list);
+ for (Runnable r : list) {
+ r.run();
+ }
+ }
+ }
+
+ public void testAll() {
+ final Context context = mock(Context.class);
+ final int userId = 11;
+ final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
+ final Handler handler = new Handler(Looper.getMainLooper());
+
+ final MyConnection conn = new MyConnection("tag", context, handler, userId, cn,
+ /* rebindBackoffSeconds= */ 5,
+ /* rebindBackoffIncrease= */ 1.5,
+ /* rebindMaxBackoffSeconds= */ 11);
+
+ assertFalse(conn.isBound());
+ assertFalse(conn.isConnected());
+ assertFalse(conn.isRebindScheduled());
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+ assertNull(conn.getServiceBinder());
+
+ when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
+ any(Handler.class), any(UserHandle.class)))
+ .thenReturn(true);
+
+ // Call bind.
+ conn.bind();
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertFalse(conn.isRebindScheduled());
+ assertNull(conn.getServiceBinder());
+
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+
+ verify(context).bindServiceAsUser(
+ ArgumentMatchers.argThat(intent -> cn.equals(intent.getComponent())),
+ eq(conn.getServiceConnectionForTest()),
+ eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+ eq(handler), eq(UserHandle.of(userId)));
+
+ // AM responds...
+ conn.getServiceConnectionForTest().onServiceConnected(cn,
+ new IDeviceAdminService.Stub() {});
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertTrue(conn.isConnected());
+ assertNotNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+
+
+ // Now connected.
+
+ // Call unbind...
+ conn.unbind();
+ assertFalse(conn.isBound());
+ assertFalse(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ // Caller bind again...
+ conn.bind();
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertFalse(conn.isRebindScheduled());
+ assertNull(conn.getServiceBinder());
+
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+
+
+ // Now connected again.
+
+ // The service got killed...
+ conn.getServiceConnectionForTest().onServiceDisconnected(cn);
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+
+ // Connected again...
+ conn.getServiceConnectionForTest().onServiceConnected(cn,
+ new IDeviceAdminService.Stub() {});
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertTrue(conn.isConnected());
+ assertNotNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+
+
+ // Then the binding is "died"...
+ conn.getServiceConnectionForTest().onBindingDied(cn);
+
+ assertFalse(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertTrue(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ assertEquals(
+ Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(),
+ conn.uptimeMillis + 5000)),
+ conn.scheduledRunnables);
+
+ // 5000 ms later...
+ conn.elapse(5000);
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ // Connected.
+ conn.getServiceConnectionForTest().onServiceConnected(cn,
+ new IDeviceAdminService.Stub() {});
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertTrue(conn.isConnected());
+ assertNotNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ // Then the binding is "died"...
+ conn.getServiceConnectionForTest().onBindingDied(cn);
+
+ assertFalse(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertTrue(conn.isRebindScheduled());
+
+ assertEquals(11000, conn.getNextBackoffMsForTest());
+
+ assertEquals(
+ Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(),
+ conn.uptimeMillis + 7500)),
+ conn.scheduledRunnables);
+
+ // Later...
+ conn.elapse(7500);
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ assertEquals(11000, conn.getNextBackoffMsForTest());
+
+
+ // Then the binding is "died"...
+ conn.getServiceConnectionForTest().onBindingDied(cn);
+
+ assertFalse(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertTrue(conn.isRebindScheduled());
+
+ assertEquals(11000, conn.getNextBackoffMsForTest());
+
+ assertEquals(
+ Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(),
+ conn.uptimeMillis + 11000)),
+ conn.scheduledRunnables);
+
+ // Call unbind...
+ conn.unbind();
+ assertFalse(conn.isBound());
+ assertFalse(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertNull(conn.getServiceBinder());
+ assertFalse(conn.isRebindScheduled());
+
+ // Call bind again... And now the backoff is reset to 5000.
+ conn.bind();
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isConnected());
+ assertFalse(conn.isRebindScheduled());
+ assertNull(conn.getServiceBinder());
+
+ assertEquals(5000, conn.getNextBackoffMsForTest());
+ }
+
+ public void testReconnectFiresAfterUnbind() {
+ final Context context = mock(Context.class);
+ final int userId = 11;
+ final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
+ final Handler handler = new Handler(Looper.getMainLooper());
+
+ final MyConnection conn = new MyConnection("tag", context, handler, userId, cn,
+ /* rebindBackoffSeconds= */ 5,
+ /* rebindBackoffIncrease= */ 1.5,
+ /* rebindMaxBackoffSeconds= */ 11);
+
+ when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
+ any(Handler.class), any(UserHandle.class)))
+ .thenReturn(true);
+
+ // Bind.
+ conn.bind();
+
+ assertTrue(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertFalse(conn.isRebindScheduled());
+
+ conn.elapse(1000);
+
+ // Service crashes.
+ conn.getServiceConnectionForTest().onBindingDied(cn);
+
+ assertFalse(conn.isBound());
+ assertTrue(conn.shouldBeBoundForTest());
+ assertTrue(conn.isRebindScheduled());
+
+ assertEquals(7500, conn.getNextBackoffMsForTest());
+
+ // Call unbind.
+ conn.unbind();
+ assertFalse(conn.isBound());
+ assertFalse(conn.shouldBeBoundForTest());
+
+ // Now, at this point, it's possible that the scheduled runnable had already been fired
+ // before during the unbind() call, and waiting on mLock.
+ // To simulate it, we just call the runnable here.
+ conn.getBindForBackoffRunnableForTest().run();
+
+ // Should still not be bound.
+ assertFalse(conn.isBound());
+ assertFalse(conn.shouldBeBoundForTest());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java
new file mode 100644
index 0000000..3819914
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import android.test.AndroidTestCase;
+
+/**
+ * Test for {@link DevicePolicyConstants}.
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyConstantsTest \
+ -w com.android.frameworks.servicestests
+
+
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+public class DevicePolicyConstantsTest extends AndroidTestCase {
+ private static final String TAG = "DevicePolicyConstantsTest";
+
+ public void testDefaultValues() throws Exception {
+ final DevicePolicyConstants constants = DevicePolicyConstants.loadFromString("");
+
+ assertEquals(1 * 60 * 60, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
+ assertEquals(24 * 60 * 60, constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ assertEquals(2.0, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
+ }
+
+ public void testCustomValues() throws Exception {
+ final DevicePolicyConstants constants = DevicePolicyConstants.loadFromString(
+ "das_died_service_reconnect_backoff_sec=10,"
+ + "das_died_service_reconnect_backoff_increase=1.25,"
+ + "das_died_service_reconnect_max_backoff_sec=15"
+ );
+
+ assertEquals(10, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
+ assertEquals(15, constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ assertEquals(1.25, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
+ }
+
+ public void testMinMax() throws Exception {
+ final DevicePolicyConstants constants = DevicePolicyConstants.loadFromString(
+ "das_died_service_reconnect_backoff_sec=3,"
+ + "das_died_service_reconnect_backoff_increase=.25,"
+ + "das_died_service_reconnect_max_backoff_sec=1"
+ );
+
+ assertEquals(5, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
+ assertEquals(5, constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+ assertEquals(1.0, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 46da3de..b870d94 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -369,6 +369,11 @@
}
@Override
+ String settingsGlobalGetString(String name) {
+ return context.settings.settingsGlobalGetString(name);
+ }
+
+ @Override
void securityLogSetLoggingEnabledProperty(boolean enabled) {
context.settings.securityLogSetLoggingEnabledProperty(enabled);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 23fada4..87106ec 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -233,6 +233,10 @@
return 0;
}
+ public String settingsGlobalGetString(String name) {
+ return "";
+ }
+
public void securityLogSetLoggingEnabledProperty(boolean enabled) {
}