Merge "Fix negative inactiveTime on creating a new user" into mnc-dev
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index cc95c0e..2fb3203 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -85,7 +85,7 @@
private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200;
private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
- public static final int MAX_DIMEN_FOR_ROUNDING = 1080; // maximum allowed width for rounding
+ public static final int MAX_DIMEN_FOR_ROUNDING = 1920; // maximum allowed width for rounding
// Keep up to date with values in system/core/include/system/window.h
public static final int NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1;
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 348b14a..4866598 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -187,8 +187,18 @@
private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
@Override
public void onError(int i, Camera camera) {
- Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
- mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
+ switch(i) {
+ case Camera.CAMERA_ERROR_EVICTED: {
+ flush();
+ mDeviceState.setError(
+ CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED);
+ } break;
+ default: {
+ Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
+ mDeviceState.setError(
+ CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
+ } break;
+ }
}
};
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f76192e..d165240 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3541,8 +3541,15 @@
}
printmAh(pw, bs.totalPowerMah);
- if (bs.drainType == BatterySipper.DrainType.APP) {
+ if (bs.usagePowerMah != bs.totalPowerMah) {
+ // If the usage (generic power) isn't the whole amount, we list out
+ // what components are involved in the calculation.
+
pw.print(" (");
+ if (bs.usagePowerMah != 0) {
+ pw.print(" usage=");
+ printmAh(pw, bs.usagePowerMah);
+ }
if (bs.cpuPowerMah != 0) {
pw.print(" cpu=");
printmAh(pw, bs.cpuPowerMah);
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 5bc45d5..397d87e 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -40,6 +40,8 @@
"android.os.storage.action.DISK_SCANNED";
public static final String EXTRA_DISK_ID =
"android.os.storage.extra.DISK_ID";
+ public static final String EXTRA_VOLUME_COUNT =
+ "android.os.storage.extra.VOLUME_COUNT";
public static final int FLAG_ADOPTABLE = 1 << 0;
public static final int FLAG_DEFAULT_PRIMARY = 1 << 1;
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 372725f..8d11527 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -55,6 +55,8 @@
"android.os.storage.action.VOLUME_STATE_CHANGED";
public static final String EXTRA_VOLUME_ID =
"android.os.storage.extra.VOLUME_ID";
+ public static final String EXTRA_VOLUME_STATE =
+ "android.os.storage.extra.VOLUME_STATE";
/** Stub volume representing internal private storage */
public static final String ID_PRIVATE_INTERNAL = "private";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f2d3e71..e335f6d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5551,13 +5551,6 @@
public static final String SLEEP_TIMEOUT = "sleep_timeout";
/**
- * Duration in milliseconds that an app should be inactive before it is considered idle.
- * <p/>Type: Long
- * @hide
- */
- public static final String APP_IDLE_DURATION = "app_idle_duration";
-
- /**
* Controls whether double tap to wake is enabled.
* @hide
*/
@@ -7117,6 +7110,28 @@
public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
/**
+ * App standby (app idle) specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "idle_duration=5000,parole_interval=4500"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * idle_duration (long)
+ * wallclock_threshold (long)
+ * parole_interval (long)
+ * parole_duration (long)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.usage.UsageStatsService.SettingsObserver
+ */
+ public static final String APP_IDLE_CONSTANTS = "app_idle_constants";
+
+ /**
* Get the key that retrieves a bluetooth headset's priority.
* @hide
*/
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index 7ae7d0f..05cfd81 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -47,7 +47,6 @@
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.ActionMode;
-import android.view.ActionMode.Callback;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -106,6 +105,10 @@
private static final int INVALID_POSITION = -1;
+ // The fade duration for toolbar and action bar when entering/exiting action mode.
+ private static final long FADE_OUT_DURATION_MS = 100;
+ private static final long FADE_IN_DURATION_MS = 200;
+
private int mContextDisplayMode;
private boolean mHasEmbeddedTabs;
@@ -866,8 +869,21 @@
hideForActionMode();
}
- mDecorToolbar.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
- mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
+ Animator fadeIn, fadeOut;
+ if (toActionMode) {
+ fadeOut = mDecorToolbar.setupAnimatorToVisibility(View.GONE,
+ FADE_OUT_DURATION_MS);
+ fadeIn = mContextView.setupAnimatorToVisibility(View.VISIBLE,
+ FADE_IN_DURATION_MS);
+ } else {
+ fadeIn = mDecorToolbar.setupAnimatorToVisibility(View.VISIBLE,
+ FADE_IN_DURATION_MS);
+ fadeOut = mContextView.setupAnimatorToVisibility(View.GONE,
+ FADE_OUT_DURATION_MS);
+ }
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(fadeOut, fadeIn);
+ set.start();
// mTabScrollView's visibility is not affected by action mode.
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 4290e22..6a85afb 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -587,6 +587,7 @@
bs.add(wbs);
}
bs.computeMobilemspp();
+ bs.sumPower();
}
private void addIdleUsage() {
@@ -612,9 +613,8 @@
private void addWiFiUsage() {
BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
- bs.sumPower();
- if (bs.totalPowerMah > 0 || !mWifiSippers.isEmpty()) {
- aggregateSippers(bs, mWifiSippers, "WIFI");
+ aggregateSippers(bs, mWifiSippers, "WIFI");
+ if (bs.totalPowerMah > 0) {
mUsageList.add(bs);
}
}
@@ -627,8 +627,8 @@
BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime,
mStatsType);
- if (bs.sumPower() > 0) {
- aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
+ aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
+ if (bs.totalPowerMah > 0) {
mUsageList.add(bs);
}
}
@@ -639,7 +639,6 @@
BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
bs.userId = userId;
aggregateSippers(bs, mUserSippers.valueAt(i), "User");
- bs.sumPower();
mUsageList.add(bs);
}
}
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
index 850ea23..35eeca7 100644
--- a/core/java/com/android/internal/widget/AbsActionBarView.java
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -136,10 +136,11 @@
return getVisibility();
}
- public void animateToVisibility(int visibility) {
+ public Animator setupAnimatorToVisibility(int visibility, long duration) {
if (mVisibilityAnim != null) {
mVisibilityAnim.cancel();
}
+
if (visibility == VISIBLE) {
if (getVisibility() != VISIBLE) {
setAlpha(0);
@@ -147,38 +148,43 @@
mMenuView.setAlpha(0);
}
}
- ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
- anim.setDuration(FADE_DURATION);
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 1);
+ anim.setDuration(duration);
anim.setInterpolator(sAlphaInterpolator);
if (mSplitView != null && mMenuView != null) {
AnimatorSet set = new AnimatorSet();
- ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 1);
- splitAnim.setDuration(FADE_DURATION);
+ ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 1);
+ splitAnim.setDuration(duration);
set.addListener(mVisAnimListener.withFinalVisibility(visibility));
set.play(anim).with(splitAnim);
- set.start();
+ return set;
} else {
anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
- anim.start();
+ return anim;
}
} else {
- ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
- anim.setDuration(FADE_DURATION);
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 0);
+ anim.setDuration(duration);
anim.setInterpolator(sAlphaInterpolator);
if (mSplitView != null && mMenuView != null) {
AnimatorSet set = new AnimatorSet();
- ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 0);
- splitAnim.setDuration(FADE_DURATION);
+ ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 0);
+ splitAnim.setDuration(duration);
set.addListener(mVisAnimListener.withFinalVisibility(visibility));
set.play(anim).with(splitAnim);
- set.start();
+ return set;
} else {
anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
- anim.start();
+ return anim;
}
}
}
+ public void animateToVisibility(int visibility) {
+ Animator anim = setupAnimatorToVisibility(visibility, FADE_DURATION);
+ anim.start();
+ }
+
@Override
public void setVisibility(int visibility) {
if (visibility != getVisibility()) {
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index c5d3290..693b194 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -21,10 +21,6 @@
import android.widget.ActionMenuView;
import com.android.internal.view.menu.MenuBuilder;
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -35,14 +31,13 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* @hide
*/
-public class ActionBarContextView extends AbsActionBarView implements AnimatorListener {
+public class ActionBarContextView extends AbsActionBarView {
private static final String TAG = "ActionBarContextView";
private CharSequence mTitle;
@@ -59,14 +54,6 @@
private boolean mTitleOptional;
private int mCloseItemLayout;
- private Animator mCurrentAnimation;
- private boolean mAnimateInOnLayout;
- private int mAnimationMode;
-
- private static final int ANIMATE_IDLE = 0;
- private static final int ANIMATE_IN = 1;
- private static final int ANIMATE_OUT = 2;
-
public ActionBarContextView(Context context) {
this(context, null);
}
@@ -255,43 +242,23 @@
mMenuView.setBackgroundDrawable(mSplitBackground);
mSplitView.addView(mMenuView, layoutParams);
}
-
- mAnimateInOnLayout = true;
}
public void closeMode() {
- if (mAnimationMode == ANIMATE_OUT) {
- // Called again during close; just finish what we were doing.
- return;
- }
if (mClose == null) {
killMode();
return;
}
- finishAnimation();
- mAnimationMode = ANIMATE_OUT;
- mCurrentAnimation = makeOutAnimation();
- mCurrentAnimation.start();
- }
-
- private void finishAnimation() {
- final Animator a = mCurrentAnimation;
- if (a != null) {
- mCurrentAnimation = null;
- a.end();
- }
}
public void killMode() {
- finishAnimation();
removeAllViews();
if (mSplitView != null) {
mSplitView.removeView(mMenuView);
}
mCustomView = null;
mMenuView = null;
- mAnimateInOnLayout = false;
}
@Override
@@ -343,7 +310,7 @@
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
"with android:layout_height=\"wrap_content\"");
}
-
+
final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = mContentHeight > 0 ?
@@ -353,7 +320,7 @@
int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
final int height = maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
-
+
if (mClose != null) {
availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
@@ -411,66 +378,13 @@
}
}
- private Animator makeInAnimation() {
- mClose.setTranslationX(-mClose.getWidth() -
- ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
- ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 0);
- buttonAnimator.setDuration(200);
- buttonAnimator.addListener(this);
- buttonAnimator.setInterpolator(new DecelerateInterpolator());
-
- AnimatorSet set = new AnimatorSet();
- AnimatorSet.Builder b = set.play(buttonAnimator);
-
- if (mMenuView != null) {
- final int count = mMenuView.getChildCount();
- if (count > 0) {
- for (int i = count - 1, j = 0; i >= 0; i--, j++) {
- View child = mMenuView.getChildAt(i);
- child.setScaleY(0);
- ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1);
- a.setDuration(300);
- b.with(a);
- }
- }
- }
-
- return set;
- }
-
- private Animator makeOutAnimation() {
- ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX",
- -mClose.getWidth() - ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
- buttonAnimator.setDuration(200);
- buttonAnimator.addListener(this);
- buttonAnimator.setInterpolator(new DecelerateInterpolator());
-
- AnimatorSet set = new AnimatorSet();
- AnimatorSet.Builder b = set.play(buttonAnimator);
-
- if (mMenuView != null) {
- final int count = mMenuView.getChildCount();
- if (count > 0) {
- for (int i = 0; i < 0; i++) {
- View child = mMenuView.getChildAt(i);
- child.setScaleY(0);
- ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0);
- a.setDuration(300);
- b.with(a);
- }
- }
- }
-
- return set;
- }
-
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final boolean isLayoutRtl = isLayoutRtl();
int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft();
final int y = getPaddingTop();
final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
-
+
if (mClose != null && mClose.getVisibility() != GONE) {
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
final int startMargin = (isLayoutRtl ? lp.rightMargin : lp.leftMargin);
@@ -479,12 +393,6 @@
x += positionChild(mClose, x, y, contentHeight, isLayoutRtl);
x = next(x, endMargin, isLayoutRtl);
- if (mAnimateInOnLayout) {
- mAnimationMode = ANIMATE_IN;
- mCurrentAnimation = makeInAnimation();
- mCurrentAnimation.start();
- mAnimateInOnLayout = false;
- }
}
if (mTitleLayout != null && mCustomView == null && mTitleLayout.getVisibility() != GONE) {
@@ -503,26 +411,6 @@
}
@Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mAnimationMode == ANIMATE_OUT) {
- killMode();
- }
- mAnimationMode = ANIMATE_IDLE;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
-
- @Override
public boolean shouldDelayChildPressedState() {
return false;
}
diff --git a/core/java/com/android/internal/widget/DecorToolbar.java b/core/java/com/android/internal/widget/DecorToolbar.java
index fb413b5..fe70d7b 100644
--- a/core/java/com/android/internal/widget/DecorToolbar.java
+++ b/core/java/com/android/internal/widget/DecorToolbar.java
@@ -17,6 +17,7 @@
package com.android.internal.widget;
+import android.animation.Animator;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
@@ -87,6 +88,7 @@
void setCustomView(View view);
View getCustomView();
void animateToVisibility(int visibility);
+ Animator setupAnimatorToVisibility(int visibility, long duration);
void setNavigationIcon(Drawable icon);
void setNavigationIcon(int resId);
void setNavigationContentDescription(CharSequence description);
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
index 54df87b..32aae72 100644
--- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.app.ActionBar;
import android.content.Context;
import android.content.res.TypedArray;
@@ -59,6 +60,8 @@
private static final int AFFECTS_LOGO_MASK =
ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO;
+ // Default fade duration for fading in/out tool bar.
+ private static final long DEFAULT_FADE_DURATION_MS = 200;
private Toolbar mToolbar;
@@ -571,9 +574,19 @@
@Override
public void animateToVisibility(int visibility) {
+ Animator anim = setupAnimatorToVisibility(visibility, DEFAULT_FADE_DURATION_MS);
+ if (anim != null) {
+ anim.start();
+ }
+ }
+
+ @Override
+ public Animator setupAnimatorToVisibility(int visibility, long duration) {
+
if (visibility == View.GONE) {
- mToolbar.animate().alpha(0)
- .setListener(new AnimatorListenerAdapter() {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mToolbar, View.ALPHA, 1, 0);
+ anim.setDuration(duration);
+ anim.addListener(new AnimatorListenerAdapter() {
private boolean mCanceled = false;
@Override
public void onAnimationEnd(Animator animation) {
@@ -587,15 +600,19 @@
mCanceled = true;
}
});
+ return anim;
} else if (visibility == View.VISIBLE) {
- mToolbar.animate().alpha(1)
- .setListener(new AnimatorListenerAdapter() {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mToolbar, View.ALPHA, 0, 1);
+ anim.setDuration(duration);
+ anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mToolbar.setVisibility(View.VISIBLE);
}
});
+ return anim;
}
+ return null;
}
@Override
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index a13dc85..97df978e 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -58,6 +58,7 @@
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
/>
</LinearLayout>
<include
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index b677a98..7ae29fb 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -60,6 +60,7 @@
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
/>
</LinearLayout>
<ImageView
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index c109225..950ae40 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -62,6 +62,7 @@
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
/>
</LinearLayout>
<TextView android:id="@+id/inbox_text1"
diff --git a/core/res/res/layout/notification_template_part_line2.xml b/core/res/res/layout/notification_template_part_line2.xml
index aeef3ab..db43271 100644
--- a/core/res/res/layout/notification_template_part_line2.xml
+++ b/core/res/res/layout/notification_template_part_line2.xml
@@ -43,6 +43,7 @@
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
/>
</LinearLayout>
<ViewStub
diff --git a/core/res/res/layout/notification_template_part_line3.xml b/core/res/res/layout/notification_template_part_line3.xml
index 6c043a0..da3c5c5 100644
--- a/core/res/res/layout/notification_template_part_line3.xml
+++ b/core/res/res/layout/notification_template_part_line3.xml
@@ -51,5 +51,6 @@
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
/>
</LinearLayout>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a6b7e35..915a445 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4168,6 +4168,9 @@
<string name="stk_cc_ss_to_ussd">SS request is modified to USSD request.</string>
<string name="stk_cc_ss_to_ss">SS request is modified to new SS request.</string>
+ <!-- Content description of the work profile icon in the notification. -->
+ <string name="notification_work_profile_content_description">Work profile</string>
+
<!-- User visible name for USB MIDI Peripheral port -->
<string name="usb_midi_peripheral_name">Android USB Peripheral Port</string>
<!-- Manufacturer name for USB MIDI Peripheral port -->
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index 9354e94..b35db28 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -155,8 +155,9 @@
inline bool currentlyIgnored() const { return currentSnapshot()->isIgnored(); }
int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); }
int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); }
- int getWidth() { return mWidth; }
- int getHeight() { return mHeight; }
+ int getWidth() const { return mWidth; }
+ int getHeight() const { return mHeight; }
+ bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
inline const Snapshot* currentSnapshot() const {
return mSnapshot != nullptr ? mSnapshot.get() : mFirstSnapshot.get();
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index b077a85..f05857c 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -494,7 +494,9 @@
// complex clip has a complex set of expectations on the renderer state - for now, avoid taking
// the merge path in those cases
deferInfo.mergeable &= !recordingComplexClip();
- deferInfo.opaqueOverBounds &= !recordingComplexClip() && mSaveStack.isEmpty();
+ deferInfo.opaqueOverBounds &= !recordingComplexClip()
+ && mSaveStack.isEmpty()
+ && !state->mRoundRectClipState;
if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
state->mClipSideFlags != kClipSide_ConservativeFull &&
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 900a621..843c412 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -203,10 +203,10 @@
void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) {
LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode");
-
- // dirty is an out parameter and should not be recorded,
- // it matters only when replaying the display list
- DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, *mState.currentTransform());
+ DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(
+ renderNode,
+ *mState.currentTransform(),
+ mState.clipIsSimple());
addRenderNodeOp(op);
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index e9d6ebc..fb2852a 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1398,9 +1398,10 @@
friend class RenderNode; // grant RenderNode access to info of child
friend class DisplayListData; // grant DisplayListData access to info of child
public:
- DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent)
+ DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple)
: DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
, mRenderNode(renderNode)
+ , mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple())
, mTransformFromParent(transformFromParent)
, mSkipInOrderDraw(false) {}
@@ -1436,6 +1437,20 @@
private:
RenderNode* mRenderNode;
+ /**
+ * This RenderNode was drawn into a DisplayList with the canvas in a state that will likely
+ * require rendering with stencil clipping. Either:
+ *
+ * 1) A path clip or rotated rect clip was in effect on the canvas at record time
+ * 2) The RenderNode was recorded with a non-simple canvas transform (e.g. rotation)
+ *
+ * Note: even if this is false, non-rect clipping may still be applied applied either due to
+ * property-driven rotation (either in this RenderNode, or any ancestor), or record time
+ * clipping in an ancestor. These are handled in RenderNode::prepareTreeImpl since they are
+ * dynamic (relative to a static DisplayList of a parent), and don't affect this flag.
+ */
+ bool mRecordedWithPotentialStencilClip;
+
///////////////////////////
// Properties below are used by RenderNode::computeOrderingImpl() and issueOperations()
///////////////////////////
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index b7cdaa2..288fed3 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -29,8 +29,9 @@
#include <GLES2/gl2.h>
#include <SkPaint.h>
-namespace android {
-namespace uirenderer {
+#define DEBUG_GLOP_BUILDER 0
+
+#if DEBUG_GLOP_BUILDER
#define TRIGGER_STAGE(stageFlag) \
LOG_ALWAYS_FATAL_IF((stageFlag) & mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \
@@ -40,6 +41,16 @@
LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \
"not prepared for current stage")
+#else
+
+#define TRIGGER_STAGE(stageFlag) ((void)0)
+#define REQUIRE_STAGES(requiredFlags) ((void)0)
+
+#endif
+
+namespace android {
+namespace uirenderer {
+
static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) {
quadVertex[0] = {0, 0, uvs.left, uvs.top};
quadVertex[1] = {1, 0, uvs.right, uvs.top};
@@ -301,7 +312,7 @@
GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture,
const int textureFillFlags, const SkPaint* paint, float alphaScale) {
TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage);
+ REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
GLenum filter = (textureFillFlags & TextureFillFlags::ForceFilter)
? GL_LINEAR : PaintUtils::getFilter(paint);
@@ -345,7 +356,7 @@
GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale) {
TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage);
+ REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
@@ -359,7 +370,7 @@
GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture,
const SkPaint& paint, float alphaScale) {
TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage);
+ REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
//specify invalid filter/clamp, since these are always static for PathTextures
mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
@@ -376,7 +387,7 @@
GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor,
const SkPaint& paint, float alphaScale) {
TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage);
+ REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
//specify invalid filter/clamp, since these are always static for ShadowTextures
mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
@@ -399,7 +410,7 @@
GlopBuilder& GlopBuilder::setFillBlack() {
TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage);
+ REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
@@ -409,7 +420,7 @@
GlopBuilder& GlopBuilder::setFillClear() {
TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage);
+ REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, Blend::ModeOrderSwap::NoSwap,
@@ -420,7 +431,7 @@
GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) {
TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage);
+ REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { &texture,
GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
@@ -434,7 +445,7 @@
GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage);
+ REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { &(layer.getTexture()),
layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5769376..433e178 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -847,11 +847,11 @@
&& layer->getHeight() == (uint32_t) rect.getHeight();
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
.setFillTextureLayer(*layer, getLayerAlpha(layer))
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -859,12 +859,12 @@
void OpenGLRenderer::composeLayerRectSwapped(Layer* layer, const Rect& rect) {
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedUvQuad(nullptr, layer->texCoords)
.setFillLayer(layer->getTexture(), layer->getColorFilter(),
getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::Swap)
.setTransform(*currentSnapshot(), TransformFlags::MeshIgnoresCanvasTransform)
.setModelViewMapUnitToRect(rect)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -880,11 +880,11 @@
&& layer->getHeight() == static_cast<uint32_t>(rect.getHeight());
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedUvQuad(nullptr, layer->texCoords)
.setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -1021,11 +1021,11 @@
Rect modelRect = Rect(rect.getWidth(), rect.getHeight());
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
.setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewOffsetRectSnap(rect.left, rect.top, modelRect)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
@@ -1140,11 +1140,11 @@
const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(nullptr) // clear ignores clip state
.setMeshIndexedQuads(&mesh[0], quadCount)
.setFillClear()
.setTransform(*currentSnapshot(), transformFlags)
.setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop, false);
@@ -1330,11 +1330,11 @@
Glop glop;
Vertex* vertices = &rectangleVertices[0];
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshIndexedQuads(vertices, rectangleVertices.size() / 4)
.setFillBlack()
.setTransform(*currentSnapshot(), transformFlags)
.setModelViewOffsetRect(0, 0, scissorBox)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -1534,11 +1534,11 @@
const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedMesh(vertices, bitmapCount * 6)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), transformFlags)
.setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(0, 0, bounds.getWidth(), bounds.getHeight()))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -1557,11 +1557,11 @@
? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedUnitQuad(texture->uvMapper)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -1647,11 +1647,11 @@
const int textureFillFlags = TextureFillFlags::None;
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshColoredTexturedMesh(mesh.get(), elementCount)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -1676,11 +1676,11 @@
&& MathUtils::areEqual(src.getHeight(), dst.getHeight());
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedUvQuad(texture->uvMapper, uv)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewMapUnitToRectOptionalSnap(tryToSnap, dst)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -1702,11 +1702,11 @@
}
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshPatchQuads(*mesh)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewOffsetRectSnap(left, top, Rect(0, 0, right - left, bottom - top)) // TODO: get minimal bounds from patch
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -1732,11 +1732,11 @@
}
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedIndexedQuads(vertices, elementCount)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), transformFlags)
.setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -1753,11 +1753,11 @@
const int transformFlags = TransformFlags::OffsetByFudgeFactor;
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshVertexBuffer(vertexBuffer, shadowInterp)
.setFillPaint(*paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), transformFlags)
.setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -2039,11 +2039,11 @@
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedUnitQuad(nullptr)
.setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -2364,11 +2364,11 @@
} else if (layer->mesh) {
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
.setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
#if DEBUG_LAYERS_AS_REGIONS
@@ -2422,11 +2422,11 @@
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshTexturedUnitQuad(nullptr)
.setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
.setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -2560,11 +2560,11 @@
? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshIndexedQuads(&mesh[0], count / 4)
.setFillPaint(*paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), transformFlags)
.setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
@@ -2575,11 +2575,11 @@
? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
Glop glop;
GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.setMeshUnitQuad()
.setFillPaint(*paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), transformFlags)
.setModelViewMapUnitToRect(Rect(left, top, right, bottom))
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
.build();
renderGlop(glop);
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 75e700a..b4cbc36 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -120,7 +120,10 @@
ATRACE_CALL();
LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
- prepareTreeImpl(info);
+ // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer.
+ bool functorsNeedLayer = Properties::debugOverdraw;
+
+ prepareTreeImpl(info, functorsNeedLayer);
}
void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
@@ -219,7 +222,15 @@
}
}
-void RenderNode::prepareTreeImpl(TreeInfo& info) {
+/**
+ * Traverse down the the draw tree to prepare for a frame.
+ *
+ * MODE_FULL = UI Thread-driven (thus properties must be synced), otherwise RT driven
+ *
+ * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the
+ * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
+ */
+void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
info.damageAccumulator->pushTransform(this);
if (info.mode == TreeInfo::MODE_FULL) {
@@ -229,11 +240,17 @@
if (CC_LIKELY(info.runAnimations)) {
animatorDirtyMask = mAnimatorManager.animate(info);
}
+
+ bool willHaveFunctor = info.mode == TreeInfo::MODE_FULL && mStagingDisplayListData
+ ? !mStagingDisplayListData->functors.isEmpty() : !mDisplayListData->functors.isEmpty();
+ bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
+ willHaveFunctor, functorsNeedLayer);
+
prepareLayer(info, animatorDirtyMask);
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingDisplayListChanges(info);
}
- prepareSubTree(info, mDisplayListData);
+ prepareSubTree(info, childFunctorsNeedLayer, mDisplayListData);
pushLayerUpdate(info);
info.damageAccumulator->popTransform();
@@ -313,7 +330,7 @@
mDisplayListData = nullptr;
}
-void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
+void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree) {
if (subtree) {
TextureCache& cache = Caches::getInstance().textureCache;
info.out.hasFunctors |= subtree->functors.size();
@@ -324,7 +341,10 @@
DrawRenderNodeOp* op = subtree->children()[i];
RenderNode* childNode = op->mRenderNode;
info.damageAccumulator->pushTransform(&op->mTransformFromParent);
- childNode->prepareTreeImpl(info);
+ bool childFunctorsNeedLayer = functorsNeedLayer
+ // Recorded with non-rect clip, or canvas-rotated by parent
+ || op->mRecordedWithPotentialStencilClip;
+ childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
info.damageAccumulator->popTransform();
}
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index d0d81d9..025a4a4 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -235,10 +235,10 @@
const char* mText;
};
- void prepareTreeImpl(TreeInfo& info);
+ void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
void pushStagingDisplayListChanges(TreeInfo& info);
- void prepareSubTree(TreeInfo& info, DisplayListData* subtree);
+ void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree);
void applyLayerPropertiesToLayer(TreeInfo& info);
void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
void pushLayerUpdate(TreeInfo& info);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 65c1c4a..81cf2df 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -158,6 +158,32 @@
}
}
+ /**
+ * Set internal layer state based on whether this layer
+ *
+ * Additionally, returns true if child RenderNodes with functors will need to use a layer
+ * to support clipping.
+ */
+ bool prepareForFunctorPresence(bool willHaveFunctor, bool ancestorDictatesFunctorsNeedLayer) {
+ // parent may have already dictated that a descendant layer is needed
+ bool functorsNeedLayer = ancestorDictatesFunctorsNeedLayer
+
+ // Round rect clipping forces layer for functors
+ || CC_UNLIKELY(getOutline().willClip())
+ || CC_UNLIKELY(getRevealClip().willClip())
+
+ // Complex matrices forces layer, due to stencil clipping
+ || CC_UNLIKELY(getTransformMatrix() && !getTransformMatrix()->isScaleTranslate())
+ || CC_UNLIKELY(getAnimationMatrix() && !getAnimationMatrix()->isScaleTranslate())
+ || CC_UNLIKELY(getStaticMatrix() && !getStaticMatrix()->isScaleTranslate());
+
+ mComputedFields.mNeedLayerForFunctors = (willHaveFunctor && functorsNeedLayer);
+
+ // If on a layer, will have consumed the need for isolating functors from stencil.
+ // Thus, it's safe to reset the flag until some descendent sets it.
+ return CC_LIKELY(effectiveLayerType() == LayerType::None) && functorsNeedLayer;
+ }
+
RenderProperties& operator=(const RenderProperties& other);
bool setClipToBounds(bool clipToBounds) {
@@ -580,15 +606,16 @@
bool promotedToLayer() const {
const int maxTextureSize = Caches::getInstance().maxTextureSize;
return mLayerProperties.mType == LayerType::None
- && !MathUtils::isZero(mPrimitiveFields.mAlpha)
- && mPrimitiveFields.mAlpha < 1
- && mPrimitiveFields.mHasOverlappingRendering
&& mPrimitiveFields.mWidth <= maxTextureSize
- && mPrimitiveFields.mHeight <= maxTextureSize;
+ && mPrimitiveFields.mHeight <= maxTextureSize
+ && (mComputedFields.mNeedLayerForFunctors
+ || (!MathUtils::isZero(mPrimitiveFields.mAlpha)
+ && mPrimitiveFields.mAlpha < 1
+ && mPrimitiveFields.mHasOverlappingRendering));
}
LayerType effectiveLayerType() const {
- return promotedToLayer() ? LayerType::RenderLayer : mLayerProperties.mType;
+ return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
}
private:
@@ -636,6 +663,9 @@
SkMatrix* mTransformMatrix;
Sk3DView mTransformCamera;
+
+ // Force layer on for functors to enable render features they don't yet support (clipping)
+ bool mNeedLayerForFunctors = false;
} mComputedFields;
};
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 33db9cf..a806440 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3701,8 +3701,9 @@
* The message sent to apps when the contents of the device list changes if they provide
* a {#link Handler} object to addOnAudioDeviceConnectionListener().
*/
- private final static int MSG_DEVICES_DEVICES_ADDED = 0;
- private final static int MSG_DEVICES_DEVICES_REMOVED = 1;
+ private final static int MSG_DEVICES_CALLBACK_REGISTERED = 0;
+ private final static int MSG_DEVICES_DEVICES_ADDED = 1;
+ private final static int MSG_DEVICES_DEVICES_REMOVED = 2;
/**
* The list of {@link AudioDeviceCallback} objects to receive add/remove notifications.
@@ -3848,8 +3849,10 @@
android.os.Handler handler) {
if (callback != null && !mDeviceCallbacks.containsKey(callback)) {
synchronized (mDeviceCallbacks) {
- mDeviceCallbacks.put(
- callback, new NativeEventHandlerDelegate(callback, handler));
+ NativeEventHandlerDelegate delegate =
+ new NativeEventHandlerDelegate(callback, handler);
+ mDeviceCallbacks.put(callback, delegate);
+ broadcastDeviceListChange(delegate.getHandler());
}
}
}
@@ -3868,49 +3871,60 @@
}
}
+ // Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
+ // (unpredictable) last time updateAudioPortCache() was called by someone, keep a list
+ // of the ports that exist at the time of the last notification.
+ private ArrayList<AudioDevicePort> mPreviousPorts = new ArrayList<AudioDevicePort>();
+
/**
* Internal method to compute and generate add/remove messages and then send to any
* registered callbacks.
*/
- private void broadcastDeviceListChange() {
+ private void broadcastDeviceListChange(Handler handler) {
int status;
- ArrayList<AudioDevicePort> previous_ports = new ArrayList<AudioDevicePort>();
- status = AudioManager.listPreviousAudioDevicePorts(previous_ports);
- if (status != AudioManager.SUCCESS) {
- return;
- }
-
+ // Get the new current set of ports
ArrayList<AudioDevicePort> current_ports = new ArrayList<AudioDevicePort>();
status = AudioManager.listAudioDevicePorts(current_ports);
if (status != AudioManager.SUCCESS) {
return;
}
- AudioDeviceInfo[] added_devices =
- calcListDeltas(previous_ports, current_ports, GET_DEVICES_ALL);
- AudioDeviceInfo[] removed_devices =
- calcListDeltas(current_ports, previous_ports, GET_DEVICES_ALL);
+ if (handler != null) {
+ // This is the callback for the registration, so send the current list
+ AudioDeviceInfo[] deviceList =
+ infoListFromPortList(current_ports, GET_DEVICES_ALL);
+ handler.sendMessage(
+ Message.obtain(handler, MSG_DEVICES_CALLBACK_REGISTERED, deviceList));
+ } else {
+ AudioDeviceInfo[] added_devices =
+ calcListDeltas(mPreviousPorts, current_ports, GET_DEVICES_ALL);
+ AudioDeviceInfo[] removed_devices =
+ calcListDeltas(current_ports, mPreviousPorts, GET_DEVICES_ALL);
- if (added_devices.length != 0 || removed_devices.length != 0) {
- Collection<NativeEventHandlerDelegate> values;
- synchronized (mDeviceCallbacks) {
- values = mDeviceCallbacks.values();
- }
- for (NativeEventHandlerDelegate delegate : values) {
- Handler handler = delegate.getHandler();
- if (handler != null) {
- if (added_devices.length != 0) {
- handler.sendMessage(
- Message.obtain(handler,MSG_DEVICES_DEVICES_ADDED, added_devices));
- }
- if (removed_devices.length != 0) {
- handler.sendMessage(
- Message.obtain(handler,MSG_DEVICES_DEVICES_REMOVED, removed_devices));
+ if (added_devices.length != 0 || removed_devices.length != 0) {
+ Collection<NativeEventHandlerDelegate> values;
+ synchronized (mDeviceCallbacks) {
+ values = mDeviceCallbacks.values();
+ }
+ for (NativeEventHandlerDelegate delegate : values) {
+ handler = delegate.getHandler();
+ if (handler != null) {
+ if (added_devices.length != 0) {
+ handler.sendMessage(
+ Message.obtain(handler,MSG_DEVICES_DEVICES_ADDED, added_devices));
+ }
+ if (removed_devices.length != 0) {
+ handler.sendMessage(
+ Message.obtain(handler,MSG_DEVICES_DEVICES_REMOVED,
+ removed_devices));
+ }
}
}
}
}
+
+ mPreviousPorts = current_ports;
}
/**
@@ -3919,7 +3933,7 @@
private class OnAmPortUpdateListener implements AudioManager.OnAudioPortUpdateListener {
static final String TAG = "OnAmPortUpdateListener";
public void onAudioPortListUpdate(AudioPort[] portList) {
- broadcastDeviceListChange();
+ broadcastDeviceListChange(null);
}
/**
@@ -3933,7 +3947,7 @@
* Callback method called when the mediaserver dies
*/
public void onServiceDied() {
- broadcastDeviceListChange();
+ broadcastDeviceListChange(null);
}
}
@@ -3965,8 +3979,8 @@
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
+ case MSG_DEVICES_CALLBACK_REGISTERED:
case MSG_DEVICES_DEVICES_ADDED:
- // call the OnAudioDeviceConnectionListener
if (callback != null) {
callback.onAudioDevicesAdded((AudioDeviceInfo[])msg.obj);
}
diff --git a/media/java/android/media/midi/IMidiDeviceServer.aidl b/media/java/android/media/midi/IMidiDeviceServer.aidl
index e30796d..c2cc2b9 100644
--- a/media/java/android/media/midi/IMidiDeviceServer.aidl
+++ b/media/java/android/media/midi/IMidiDeviceServer.aidl
@@ -31,4 +31,5 @@
void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
MidiDeviceInfo getDeviceInfo();
+ void setDeviceInfo(in MidiDeviceInfo deviceInfo);
}
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index 1b56e1c..1212b64 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -269,8 +269,20 @@
public MidiDeviceInfo getDeviceInfo() {
return mDeviceInfo;
}
+
+ @Override
+ public void setDeviceInfo(MidiDeviceInfo deviceInfo) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("setDeviceInfo should only be called by MidiService");
+ }
+ if (mDeviceInfo != null) {
+ throw new IllegalStateException("setDeviceInfo should only be called once");
+ }
+ mDeviceInfo = deviceInfo;
+ }
};
+ // Constructor for MidiManager.createDeviceServer()
/* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers,
int numOutputPorts, Callback callback) {
mMidiManager = midiManager;
@@ -292,6 +304,13 @@
mGuard.open("close");
}
+ // Constructor for MidiDeviceService.onCreate()
+ /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers,
+ MidiDeviceInfo deviceInfo, Callback callback) {
+ this(midiManager, inputPortReceivers, deviceInfo.getOutputPortCount(), callback);
+ mDeviceInfo = deviceInfo;
+ }
+
/* package */ IMidiDeviceServer getBinderInterface() {
return mServer;
}
@@ -300,13 +319,6 @@
return mServer.asBinder();
}
- /* package */ void setDeviceInfo(MidiDeviceInfo deviceInfo) {
- if (mDeviceInfo != null) {
- throw new IllegalStateException("setDeviceInfo should only be called once");
- }
- mDeviceInfo = deviceInfo;
- }
-
private void updateDeviceStatus() {
// clear calling identity, since we may be in a Binder call from one of our clients
long identityToken = Binder.clearCallingIdentity();
diff --git a/media/java/android/media/midi/MidiDeviceService.java b/media/java/android/media/midi/MidiDeviceService.java
index d897ad2..388d95b 100644
--- a/media/java/android/media/midi/MidiDeviceService.java
+++ b/media/java/android/media/midi/MidiDeviceService.java
@@ -83,9 +83,7 @@
if (inputPortReceivers == null) {
inputPortReceivers = new MidiReceiver[0];
}
- server = new MidiDeviceServer(mMidiManager, inputPortReceivers,
- deviceInfo.getOutputPortCount(), mCallback);
- server.setDeviceInfo(deviceInfo);
+ server = new MidiDeviceServer(mMidiManager, inputPortReceivers, deviceInfo, mCallback);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in IMidiManager.getServiceDeviceInfo");
server = null;
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index 0beb9a4..89230fe 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -318,7 +318,6 @@
Log.e(TAG, "registerVirtualDevice failed");
return null;
}
- server.setDeviceInfo(deviceInfo);
return server;
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in createVirtualDevice");
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 50a215c..f52ccc9 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -434,9 +434,12 @@
}
/**
- * Informs the application that the video is now available for watching. This is primarily
- * used to signal the application to unblock the screen. The TV input service must call this
- * method as soon as the content rendered onto its surface gets ready for viewing.
+ * Informs the application that the video is now available for watching. Video is blocked
+ * until this method is called.
+ *
+ * <p>The TV input service must call this method as soon as the content rendered onto its
+ * surface is ready for viewing. This method must be called each time {@link #onTune(Uri)}
+ * is called.
*
* @see #notifyVideoUnavailable
*/
@@ -761,9 +764,11 @@
public abstract void onSetStreamVolume(float volume);
/**
- * Tunes to a given channel. When the video is available, {@link #notifyVideoAvailable()}
- * should be called. Also, {@link #notifyVideoUnavailable(int)} should be called when the TV
- * input cannot continue playing the given channel.
+ * Tunes to a given channel.
+ *
+ * <p>No video will be displayed until {@link #notifyVideoAvailable()} is called.
+ * Also, {@link #notifyVideoUnavailable(int)} should be called when the TV input cannot
+ * continue playing the given channel.
*
* @param channelUri The URI of the channel.
* @return {@code true} if the tuning was successful, {@code false} otherwise.
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index aa7d1f8..2427363 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -991,16 +991,6 @@
}
private void onDiskScannedLocked(DiskInfo disk) {
- final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
- android.Manifest.permission.WRITE_MEDIA_STORAGE);
-
- final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
- if (latch != null) {
- latch.countDown();
- }
-
int volumeCount = 0;
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
@@ -1009,6 +999,18 @@
}
}
+ final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);
+ intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.WRITE_MEDIA_STORAGE);
+
+ final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
+ if (latch != null) {
+ latch.countDown();
+ }
+
disk.volumeCount = volumeCount;
mCallbacks.notifyDiskScanned(disk, volumeCount);
}
@@ -1060,6 +1062,7 @@
private boolean isBroadcastWorthy(VolumeInfo vol) {
switch (vol.getType()) {
+ case VolumeInfo.TYPE_PRIVATE:
case VolumeInfo.TYPE_PUBLIC:
case VolumeInfo.TYPE_EMULATED:
break;
@@ -1072,6 +1075,7 @@
case VolumeInfo.STATE_MOUNTED_READ_ONLY:
case VolumeInfo.STATE_EJECTING:
case VolumeInfo.STATE_UNMOUNTED:
+ case VolumeInfo.STATE_UNMOUNTABLE:
break;
default:
return false;
@@ -1098,6 +1102,8 @@
if (isBroadcastWorthy(vol)) {
final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
+ intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
+ intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.WRITE_MEDIA_STORAGE);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index b97fa69..b56e326 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1113,23 +1113,32 @@
// correct delta from when we should start reading (aka when we are on battery).
WifiActivityEnergyInfo info = mWifiManager.reportActivityInfo();
if (info != null && info.isValid()) {
+ if (info.mControllerEnergyUsed < 0 || info.mControllerIdleTimeMs < 0 ||
+ info.mControllerRxTimeMs < 0 || info.mControllerTxTimeMs < 0) {
+ Slog.wtf(TAG, "Reported WiFi energy data is invalid: " + info);
+ return null;
+ }
+
// We will modify the last info object to be the delta, and store the new
// WifiActivityEnergyInfo object as our last one.
final WifiActivityEnergyInfo result = mLastInfo;
result.mTimestamp = info.getTimeStamp();
result.mStackState = info.getStackState();
+
+ // These times seem to be the most reliable.
result.mControllerTxTimeMs =
info.mControllerTxTimeMs - mLastInfo.mControllerTxTimeMs;
result.mControllerRxTimeMs =
info.mControllerRxTimeMs - mLastInfo.mControllerRxTimeMs;
- result.mControllerEnergyUsed =
- info.mControllerEnergyUsed - mLastInfo.mControllerEnergyUsed;
// WiFi calculates the idle time as a difference from the on time and the various
// Rx + Tx times. There seems to be some missing time there because this sometimes
// becomes negative. Just cap it at 0 and move on.
+ // b/21613534
result.mControllerIdleTimeMs =
Math.max(0, info.mControllerIdleTimeMs - mLastInfo.mControllerIdleTimeMs);
+ result.mControllerEnergyUsed =
+ Math.max(0, info.mControllerEnergyUsed - mLastInfo.mControllerEnergyUsed);
if (result.mControllerTxTimeMs < 0 ||
result.mControllerRxTimeMs < 0) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cd467bd..d39b25f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2852,6 +2852,49 @@
}
}
+ void setBtScoDeviceConnectionState(BluetoothDevice btDevice, int state) {
+ if (btDevice == null) {
+ return;
+ }
+
+ String address = btDevice.getAddress();
+ BluetoothClass btClass = btDevice.getBluetoothClass();
+ int outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
+ int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ if (btClass != null) {
+ switch (btClass.getDeviceClass()) {
+ case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+ case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ break;
+ case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ break;
+ }
+ }
+
+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+ address = "";
+ }
+
+ boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
+
+ String btDeviceName = btDevice.getName();
+ boolean success =
+ handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
+ handleDeviceConnection(connected, inDevice, address, btDeviceName);
+ if (success) {
+ synchronized (mScoClients) {
+ if (connected) {
+ mBluetoothHeadsetDevice = btDevice;
+ } else {
+ mBluetoothHeadsetDevice = null;
+ resetBluetoothSco();
+ }
+ }
+ }
+ }
+
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
@@ -3002,6 +3045,10 @@
case BluetoothProfile.HEADSET:
synchronized (mScoClients) {
+ if (mBluetoothHeadsetDevice != null) {
+ setBtScoDeviceConnectionState(mBluetoothHeadsetDevice,
+ BluetoothProfile.STATE_DISCONNECTED);
+ }
mBluetoothHeadset = null;
}
break;
@@ -4894,49 +4941,9 @@
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
- inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- String address = null;
-
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- if (btDevice == null) {
- return;
- }
- address = btDevice.getAddress();
- BluetoothClass btClass = btDevice.getBluetoothClass();
- if (btClass != null) {
- switch (btClass.getDeviceClass()) {
- case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
- case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
- break;
- case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
- break;
- }
- }
-
- if (!BluetoothAdapter.checkBluetoothAddress(address)) {
- address = "";
- }
-
- boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
-
- String btDeviceName = btDevice.getName();
- boolean success =
- handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
- handleDeviceConnection(connected, inDevice, address, btDeviceName);
- if (success) {
- synchronized (mScoClients) {
- if (connected) {
- mBluetoothHeadsetDevice = btDevice;
- } else {
- mBluetoothHeadsetDevice = null;
- resetBluetoothSco();
- }
- }
- }
+ setBtScoDeviceConnectionState(btDevice, state);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 7531403..1ee07a5 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -277,6 +277,11 @@
return mKeySets.get(keySetId);
}
+ /* Checks if an identifier refers to a known keyset */
+ public boolean isIdValidKeySetId(long id) {
+ return mKeySets.get(id) != null;
+ }
+
/**
* Fetches the {@link PublicKey public keys} which belong to the specified
* KeySet id.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6c9fd3f..99b24ed 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6157,7 +6157,24 @@
pkg.applicationInfo.uid = pkgSetting.appId;
pkg.mExtras = pkgSetting;
- if (!pkgSetting.keySetData.isUsingUpgradeKeySets() || pkgSetting.sharedUser != null) {
+ if (shouldCheckUpgradeKeySetLP(pkgSetting, scanFlags)) {
+ if (checkUpgradeKeySetLP(pkgSetting, pkg)) {
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ } else {
+ if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Package " + pkg.packageName + " upgrade keys do not match the "
+ + "previously installed version");
+ } else {
+ pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ String msg = "System package " + pkg.packageName
+ + " signature changed; retaining data.";
+ reportSettingsProblem(Log.WARN, msg);
+ }
+ }
+ } else {
try {
verifySignaturesLP(pkgSetting, pkg);
// We just determined the app is signed correctly, so bring
@@ -6189,23 +6206,6 @@
+ " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
}
- } else {
- if (!checkUpgradeKeySetLP(pkgSetting, pkg)) {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Package " + pkg.packageName + " upgrade keys do not match the "
- + "previously installed version");
- } else {
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
- String msg = "System package " + pkg.packageName
- + " signature changed; retaining data.";
- reportSettingsProblem(Log.WARN, msg);
- }
- } else {
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
- }
}
// Verify that this new package doesn't have any content providers
// that conflict with existing packages. Only do this if the
@@ -11161,6 +11161,28 @@
}
}
+ private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) {
+ // Can't rotate keys during boot or if sharedUser.
+ if (oldPs == null || (scanFlags&SCAN_BOOTING) != 0 || oldPs.sharedUser != null
+ || !oldPs.keySetData.isUsingUpgradeKeySets()) {
+ return false;
+ }
+ // app is using upgradeKeySets; make sure all are valid
+ KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets();
+ for (int i = 0; i < upgradeKeySets.length; i++) {
+ if (!ksms.isIdValidKeySetId(upgradeKeySets[i])) {
+ Slog.wtf(TAG, "Package "
+ + (oldPs.name != null ? oldPs.name : "<null>")
+ + " contains upgrade-key-set reference to unknown key-set: "
+ + upgradeKeySets[i]
+ + " reverting to signatures check.");
+ return false;
+ }
+ }
+ return true;
+ }
+
private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {
// Upgrade keysets are being used. Determine if new package has a superset of the
// required keys.
@@ -11189,7 +11211,14 @@
oldPackage = mPackages.get(pkgName);
if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
final PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (ps == null || !ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {
+ if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
+ if(!checkUpgradeKeySetLP(ps, pkg)) {
+ res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package not signed by keys specified by upgrade-keysets: "
+ + pkgName);
+ return;
+ }
+ } else {
// default to original signature matching
if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
@@ -11197,13 +11226,6 @@
"New package has a different signature: " + pkgName);
return;
}
- } else {
- if(!checkUpgradeKeySetLP(ps, pkg)) {
- res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package not signed by keys specified by upgrade-keysets: "
- + pkgName);
- return;
- }
}
// In case of rollback, remember per-user/profile install state
@@ -11633,20 +11655,20 @@
// Quick sanity check that we're signed correctly if updating;
// we'll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
- if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {
- try {
- verifySignaturesLP(ps, pkg);
- } catch (PackageManagerException e) {
- res.setError(e.error, e.getMessage());
- return;
- }
- } else {
+ if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
+ } else {
+ try {
+ verifySignaturesLP(ps, pkg);
+ } catch (PackageManagerException e) {
+ res.setError(e.error, e.getMessage());
+ return;
+ }
}
oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
@@ -11667,14 +11689,14 @@
// also includes the "updating the same package" case, of course.
// "updating same package" could also involve key-rotation.
final boolean sigsOk;
- if (!bp.sourcePackage.equals(pkg.packageName)
- || !(bp.packageSetting instanceof PackageSetting)
- || !bp.packageSetting.keySetData.isUsingUpgradeKeySets()
- || ((PackageSetting) bp.packageSetting).sharedUser != null) {
+ if (bp.sourcePackage.equals(pkg.packageName)
+ && (bp.packageSetting instanceof PackageSetting)
+ && (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting,
+ scanFlags))) {
+ sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
+ } else {
sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
- } else {
- sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
}
if (!sigsOk) {
// If the owning package is the system itself, we log but allow
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 25ca167..dbcfa19 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -243,7 +243,7 @@
}
/** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
- static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;
+ static final int WAITING_FOR_DRAWN_TIMEOUT = 500;
/**
* Lock protecting internal state. Must not call out into window
@@ -836,13 +836,18 @@
// If sensor is turned off or nonexistent for some reason
return;
}
- //Could have been invoked due to screen turning on or off or
- //change of the currently visible window's orientation
+ // Could have been invoked due to screen turning on or off or
+ // change of the currently visible window's orientation.
if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly
+ ", mAwake=" + mAwake + ", mCurrentAppOrientation=" + mCurrentAppOrientation
- + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled);
+ + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled
+ + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
+ + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
boolean disable = true;
- if (mScreenOnEarly && mAwake) {
+ // Note: We postpone the rotating of the screen until the keyguard as well as the
+ // window manager have reported a draw complete.
+ if (mScreenOnEarly && mAwake &&
+ mKeyguardDrawComplete && mWindowManagerDrawComplete) {
if (needSensorRunningLp()) {
disable = false;
//enable listener if not already enabled
@@ -5369,7 +5374,7 @@
private void finishKeyguardDrawn() {
synchronized (mLock) {
if (!mAwake || mKeyguardDrawComplete) {
- return; // spurious
+ return; // We are not awake yet or we have already informed of this event.
}
mKeyguardDrawComplete = true;
@@ -5407,18 +5412,18 @@
mScreenOnFully = false;
mWindowManagerDrawComplete = false;
mScreenOnListener = screenOnListener;
- updateOrientationListenerLp();
}
mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback,
WAITING_FOR_DRAWN_TIMEOUT);
- // ... eventually calls finishWindowsDrawn
+ // ... eventually calls finishWindowsDrawn which will finalize our screen turn on
+ // as well as enabling the orientation change logic/sensor.
}
private void finishWindowsDrawn() {
synchronized (mLock) {
if (!mScreenOnEarly || mWindowManagerDrawComplete) {
- return; // spurious
+ return; // Screen is not turned on or we did already handle this case earlier.
}
mWindowManagerDrawComplete = true;
@@ -5428,6 +5433,11 @@
}
private void finishScreenTurningOn() {
+ synchronized (mLock) {
+ // We have just finished drawing screen content. Since the orientation listener
+ // gets only installed when all windows are drawn, we try to install it again.
+ updateOrientationListenerLp();
+ }
final ScreenOnListener listener;
final boolean enableScreen;
synchronized (mLock) {
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index d1bbbfc..3ecfd55 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -741,6 +741,15 @@
MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts,
inputPortNames, outputPortNames, properties, isPrivate);
+ if (server != null) {
+ try {
+ server.setDeviceInfo(deviceInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in setDeviceInfo()");
+ return null;
+ }
+ }
+
Device device = null;
BluetoothDevice bluetoothDevice = null;
if (type == MidiDeviceInfo.TYPE_BLUETOOTH) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 8e1895e..837570b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -41,7 +41,6 @@
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
-import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
@@ -61,8 +60,10 @@
import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
@@ -101,16 +102,11 @@
private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
- static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = COMPRESS_TIME ? ONE_MINUTE * 4
- : 12 * 60 * ONE_MINUTE; // 12 hours of screen-on time sans dream-time
- static final long DEFAULT_WALLCLOCK_APP_IDLE_THRESHOLD_MILLIS = COMPRESS_TIME ? ONE_MINUTE * 8
- : 2L * 24 * 60 * ONE_MINUTE; // 2 days
- static final long DEFAULT_CHECK_IDLE_INTERVAL = COMPRESS_TIME ? ONE_MINUTE
- : 8 * 60 * ONE_MINUTE; // 8 hours
- static final long DEFAULT_PAROLE_INTERVAL = COMPRESS_TIME ? ONE_MINUTE * 10
- : 24 * 60 * ONE_MINUTE; // 24 hours between paroles
- static final long DEFAULT_PAROLE_DURATION = COMPRESS_TIME ? ONE_MINUTE
- : 10 * ONE_MINUTE; // 10 minutes
+ long mAppIdleDurationMillis;
+ long mCheckIdleIntervalMillis;
+ long mAppIdleWallclockThresholdMillis;
+ long mAppIdleParoleIntervalMillis;
+ long mAppIdleParoleDurationMillis;
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
@@ -140,8 +136,7 @@
boolean mAppIdleParoled;
private boolean mScreenOn;
private long mLastAppIdleParoledTime;
- long mAppIdleDurationMillis;
- long mCheckIdleIntervalMillis = DEFAULT_CHECK_IDLE_INTERVAL;
+
long mScreenOnTime;
long mScreenOnSystemTimeSnapshot;
@@ -185,11 +180,7 @@
mRealTimeSnapshot = SystemClock.elapsedRealtime();
mSystemTimeSnapshot = System.currentTimeMillis();
- // Look at primary user's secure setting for this. TODO: Maybe apply different
- // thresholds for different users.
- mAppIdleDurationMillis = Settings.Secure.getLongForUser(getContext().getContentResolver(),
- Settings.Secure.APP_IDLE_DURATION, DEFAULT_APP_IDLE_THRESHOLD_MILLIS,
- UserHandle.USER_OWNER);
+
publishLocalService(UsageStatsManagerInternal.class, new LocalService());
publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
@@ -199,7 +190,10 @@
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// Observe changes to the threshold
- new SettingsObserver(mHandler).registerObserver();
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.registerObserver();
+ settingsObserver.updateSettings();
+
mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(DeviceIdleController.SERVICE_NAME));
@@ -323,7 +317,7 @@
// Compute when the next parole needs to happen. We check more frequently than necessary
// since the message handler delays are based on elapsedRealTime and not wallclock time.
// The comparison is done in wallclock time.
- long timeLeft = (mLastAppIdleParoledTime + DEFAULT_PAROLE_INTERVAL)
+ long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis)
- checkAndGetTimeLocked();
if (timeLeft < 0) {
timeLeft = 0;
@@ -334,7 +328,7 @@
private void postParoleEndTimeout() {
if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
- mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, DEFAULT_PAROLE_DURATION);
+ mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
}
void postCheckIdleStates(int userId) {
@@ -386,7 +380,7 @@
synchronized (mLock) {
if (!mAppIdleParoled) {
final long timeSinceLastParole = checkAndGetTimeLocked() - mLastAppIdleParoledTime;
- if (timeSinceLastParole > DEFAULT_PAROLE_INTERVAL) {
+ if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
setAppIdleParoled(true);
// Make sure it ends at some point
@@ -475,7 +469,7 @@
synchronized (mLock) {
final long timeSinceLastParole = checkAndGetTimeLocked() - mLastAppIdleParoledTime;
if (!deviceIdle
- && timeSinceLastParole >= DEFAULT_PAROLE_INTERVAL) {
+ && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
if (DEBUG) Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
postNextParoleTimeout();
setAppIdleParoled(true);
@@ -608,7 +602,7 @@
lastUsedTime, screenOnTime, timeNow);
service.setBeginIdleTime(packageName, deviceUsageTime);
service.setSystemLastUsedTime(packageName,
- timeNow - (idle ? DEFAULT_WALLCLOCK_APP_IDLE_THRESHOLD_MILLIS : 0) - 5000);
+ timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000);
// Inform listeners if necessary
if (previouslyIdle != idle) {
// Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
@@ -711,7 +705,7 @@
boolean hasPassedIdleTimeoutLocked(long beginIdleTime, long lastUsedTime,
long screenOnTime, long currentTime) {
return (beginIdleTime <= screenOnTime - mAppIdleDurationMillis)
- && (lastUsedTime <= currentTime - DEFAULT_WALLCLOCK_APP_IDLE_THRESHOLD_MILLIS);
+ && (lastUsedTime <= currentTime - mAppIdleWallclockThresholdMillis);
}
void addListener(AppIdleStateChangeListener listener) {
@@ -854,7 +848,30 @@
}
idpw.decreaseIndent();
}
- pw.write("Screen On Timebase:" + mScreenOnTime + "\n");
+ pw.println("Screen On Timebase:" + mScreenOnTime);
+
+ pw.println();
+ pw.println("Settings:");
+
+ pw.print(" mAppIdleDurationMillis=");
+ TimeUtils.formatDuration(mAppIdleDurationMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleWallclockThresholdMillis=");
+ TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
+ pw.println();
+
+ pw.print(" mCheckIdleIntervalMillis=");
+ TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleParoleIntervalMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleParoleDurationMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
+ pw.println();
}
}
@@ -907,28 +924,61 @@
}
/**
- * Observe settings changes for Settings.Secure.APP_IDLE_DURATION.
+ * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
*/
private class SettingsObserver extends ContentObserver {
+ private static final String KEY_IDLE_DURATION = "idle_duration";
+ private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
+ private static final String KEY_PAROLE_INTERVAL = "parole_interval";
+ private static final String KEY_PAROLE_DURATION = "parole_duration";
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
SettingsObserver(Handler handler) {
super(handler);
}
void registerObserver() {
- getContext().getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.APP_IDLE_DURATION), false, this, UserHandle.USER_OWNER);
+ getContext().getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.APP_IDLE_CONSTANTS), false, this);
}
@Override
- public void onChange(boolean selfChange, Uri uri, int userId) {
- mAppIdleDurationMillis = Settings.Secure.getLongForUser(getContext().getContentResolver(),
- Settings.Secure.APP_IDLE_DURATION, DEFAULT_APP_IDLE_THRESHOLD_MILLIS,
- UserHandle.USER_OWNER);
- mCheckIdleIntervalMillis = Math.min(DEFAULT_CHECK_IDLE_INTERVAL,
- mAppIdleDurationMillis / 4);
+ public void onChange(boolean selfChange) {
+ updateSettings();
postCheckIdleStates(UserHandle.USER_ALL);
}
+
+ void updateSettings() {
+ synchronized (mLock) {
+ // Look at global settings for this.
+ // TODO: Maybe apply different thresholds for different users.
+ try {
+ mParser.setString(Settings.Global.getString(getContext().getContentResolver(),
+ Settings.Global.APP_IDLE_CONSTANTS));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
+ // fallthrough, mParser is empty and all defaults will be returned.
+ }
+
+ // Default: 12 hours of screen-on time sans dream-time
+ mAppIdleDurationMillis = mParser.getLong(KEY_IDLE_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
+
+ mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
+ COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
+
+ mCheckIdleIntervalMillis = Math.min(mAppIdleDurationMillis / 4,
+ COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
+
+ // Default: 24 hours between paroles
+ mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
+ COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
+
+ mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+ }
+ }
}
private class BinderService extends IUsageStatsManager.Stub {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9a63aa3..91566f8 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -448,8 +448,13 @@
/**
* Stores a list of the video callbacks, keyed by IBinder.
+ *
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+ * load factor before resizing, 1 means we only expect a single thread to
+ * access the map so make only a single shard
*/
- private HashMap<IBinder, IVideoCallback> mVideoCallbacks = new HashMap<>();
+ private ConcurrentHashMap<IBinder, IVideoCallback> mVideoCallbacks =
+ new ConcurrentHashMap<IBinder, IVideoCallback>(8, 0.9f, 1);
/**
* Default handler used to consolidate binder method calls onto a single thread.
@@ -470,12 +475,16 @@
IBinder binder = (IBinder) msg.obj;
IVideoCallback callback = IVideoCallback.Stub
.asInterface((IBinder) msg.obj);
+ if (callback == null) {
+ Log.w(this, "addVideoProvider - skipped; callback is null.");
+ break;
+ }
+
if (mVideoCallbacks.containsKey(binder)) {
Log.i(this, "addVideoProvider - skipped; already present.");
break;
}
mVideoCallbacks.put(binder, callback);
- Log.i(this, "addVideoProvider "+ mVideoCallbacks.size());
break;
}
case MSG_REMOVE_VIDEO_CALLBACK: {
@@ -594,7 +603,7 @@
public VideoProvider() {
mBinder = new VideoProvider.VideoProviderBinder();
- mMessageHandler = new VideoProvider.VideoProviderHandler();
+ mMessageHandler = new VideoProvider.VideoProviderHandler(Looper.getMainLooper());
}
/**
@@ -763,11 +772,12 @@
*/
public void receiveSessionModifyRequest(VideoProfile videoProfile) {
if (mVideoCallbacks != null) {
- try {
- for (IVideoCallback callback : mVideoCallbacks.values()) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
callback.receiveSessionModifyRequest(videoProfile);
+ } catch (RemoteException ignored) {
+ Log.w(this, "receiveSessionModifyRequest callback failed", ignored);
}
- } catch (RemoteException ignored) {
}
}
}
@@ -793,12 +803,13 @@
public void receiveSessionModifyResponse(int status,
VideoProfile requestedProfile, VideoProfile responseProfile) {
if (mVideoCallbacks != null) {
- try {
- for (IVideoCallback callback : mVideoCallbacks.values()) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
callback.receiveSessionModifyResponse(status, requestedProfile,
responseProfile);
+ } catch (RemoteException ignored) {
+ Log.w(this, "receiveSessionModifyResponse callback failed", ignored);
}
- } catch (RemoteException ignored) {
}
}
}
@@ -819,11 +830,12 @@
*/
public void handleCallSessionEvent(int event) {
if (mVideoCallbacks != null) {
- try {
- for (IVideoCallback callback : mVideoCallbacks.values()) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
callback.handleCallSessionEvent(event);
+ } catch (RemoteException ignored) {
+ Log.w(this, "handleCallSessionEvent callback failed", ignored);
}
- } catch (RemoteException ignored) {
}
}
}
@@ -843,11 +855,12 @@
*/
public void changePeerDimensions(int width, int height) {
if (mVideoCallbacks != null) {
- try {
- for (IVideoCallback callback : mVideoCallbacks.values()) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
callback.changePeerDimensions(width, height);
+ } catch (RemoteException ignored) {
+ Log.w(this, "changePeerDimensions callback failed", ignored);
}
- } catch (RemoteException ignored) {
}
}
}
@@ -869,11 +882,12 @@
*/
public void setCallDataUsage(long dataUsage) {
if (mVideoCallbacks != null) {
- try {
- for (IVideoCallback callback : mVideoCallbacks.values()) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
callback.changeCallDataUsage(dataUsage);
+ } catch (RemoteException ignored) {
+ Log.w(this, "setCallDataUsage callback failed", ignored);
}
- } catch (RemoteException ignored) {
}
}
}
@@ -905,11 +919,12 @@
*/
public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
if (mVideoCallbacks != null) {
- try {
- for (IVideoCallback callback : mVideoCallbacks.values()) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
callback.changeCameraCapabilities(cameraCapabilities);
+ } catch (RemoteException ignored) {
+ Log.w(this, "changeCameraCapabilities callback failed", ignored);
}
- } catch (RemoteException ignored) {
}
}
}
@@ -929,11 +944,12 @@
*/
public void changeVideoQuality(int videoQuality) {
if (mVideoCallbacks != null) {
- try {
- for (IVideoCallback callback : mVideoCallbacks.values()) {
+ for (IVideoCallback callback : mVideoCallbacks.values()) {
+ try {
callback.changeVideoQuality(videoQuality);
+ } catch (RemoteException ignored) {
+ Log.w(this, "changeVideoQuality callback failed", ignored);
}
- } catch (RemoteException ignored) {
}
}
}