diff options
42 files changed, 625 insertions, 411 deletions
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 @@ public class LegacyCameraDevice implements AutoCloseable { 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 @@ public class RequestThreadManager { 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 @@ public abstract class BatteryStats implements Parcelable { } 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 @@ public class DiskInfo implements Parcelable { "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 @@ public class VolumeInfo implements Parcelable { "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 final class Settings { 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 final class Settings { 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.content.res.Resources; 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 @@ public class WindowDecorActionBar extends ActionBar implements 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 @@ public class WindowDecorActionBar extends ActionBar implements 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 @@ public final class BatteryStatsHelper { bs.add(wbs); } bs.computeMobilemspp(); + bs.sumPower(); } private void addIdleUsage() { @@ -612,9 +613,8 @@ public final class BatteryStatsHelper { 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 @@ public final class BatteryStatsHelper { 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 @@ public final class BatteryStatsHelper { 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 @@ public abstract class AbsActionBarView extends ViewGroup { 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 @@ public abstract class AbsActionBarView extends ViewGroup { 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.ActionMenuPresenter; 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.LayoutInflater; 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 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi 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 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi 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 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi 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 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi 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 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi } } - 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 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi 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 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi } @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 @@ public interface DecorToolbar { 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 @@ package com.android.internal.widget; 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 @@ public class ToolbarWidgetWrapper implements DecorToolbar { 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 @@ public class ToolbarWidgetWrapper implements DecorToolbar { @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 @@ public class ToolbarWidgetWrapper implements DecorToolbar { 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 @@ public: 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 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { // 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 @@ bool DisplayListCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { 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 @@ class DrawRenderNodeOp : public DrawBoundedOp { 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 @@ public: 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 @@ namespace uirenderer { 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 @@ void GlopBuilder::setFill(int color, float alphaScale, 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::setFillTexturePaint(Texture& texture, 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::setFillPaint(const SkPaint& paint, float alphaScale) { 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::setFillPathTexturePaint(PathTexture& texture, 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::setFillShadowTexturePaint(ShadowTexture& texture, int 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::setFillBlack() { 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::setFillClear() { 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::setFillLayer(Texture& texture, const SkColorFilter* co 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 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { && 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::drawTextureLayer(Layer* layer, const Rect& rect) { 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 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect) { && 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 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { 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 @@ void OpenGLRenderer::clearLayerRegions() { 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 @@ void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) { 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 @@ void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entr 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 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { ? 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 @@ void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int m 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 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, cons && 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 @@ void OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh, } 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 @@ void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entr } 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 @@ void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, 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 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, 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 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { } 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 @@ void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y, 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 @@ void OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint ? 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 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot ? 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 @@ void RenderNode::prepareTree(TreeInfo& info) { 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::pushLayerUpdate(TreeInfo& info) { } } -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 @@ void RenderNode::prepareTreeImpl(TreeInfo& info) { 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 @@ void RenderNode::deleteDisplayListData() { 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 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { 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 @@ private: 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 @@ public: } } + /** + * 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 @@ public: 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 @@ private: 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 @@ public class AudioManager { * 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 @@ public class AudioManager { 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 @@ public class AudioManager { } } + // 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 (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 (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 = 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 @@ public class AudioManager { private class OnAmPortUpdateListener implements AudioManager.OnAudioPortUpdateListener { static final String TAG = "OnAmPortUpdateListener"; public void onAudioPortListUpdate(AudioPort[] portList) { - broadcastDeviceListChange(); + broadcastDeviceListChange(null); } /** @@ -3933,7 +3947,7 @@ public class AudioManager { * Callback method called when the mediaserver dies */ public void onServiceDied() { - broadcastDeviceListChange(); + broadcastDeviceListChange(null); } } @@ -3965,8 +3979,8 @@ public class AudioManager { @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 @@ interface IMidiDeviceServer 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 final class MidiDeviceServer implements Closeable { 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 @@ public final class MidiDeviceServer implements Closeable { 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 @@ public final class MidiDeviceServer implements Closeable { 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 @@ abstract public class MidiDeviceService extends Service { 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 @@ public final class MidiManager { 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 @@ public abstract class TvInputService extends Service { } /** - * 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 class TvInputService extends Service { 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,8 +991,18 @@ class MountService extends IMountService.Stub } private void onDiskScannedLocked(DiskInfo disk) { + int volumeCount = 0; + for (int i = 0; i < mVolumes.size(); i++) { + final VolumeInfo vol = mVolumes.valueAt(i); + if (Objects.equals(disk.id, vol.getDiskId())) { + volumeCount++; + } + } + 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); @@ -1001,14 +1011,6 @@ class MountService extends IMountService.Stub latch.countDown(); } - int volumeCount = 0; - for (int i = 0; i < mVolumes.size(); i++) { - final VolumeInfo vol = mVolumes.valueAt(i); - if (Objects.equals(disk.id, vol.getDiskId())) { - volumeCount++; - } - } - disk.volumeCount = volumeCount; mCallbacks.notifyDiskScanned(disk, volumeCount); } @@ -1060,6 +1062,7 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub // 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 @@ public class AudioService extends IAudioService.Stub { } } + 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 @@ public class AudioService extends IAudioService.Stub { case BluetoothProfile.HEADSET: synchronized (mScoClients) { + if (mBluetoothHeadsetDevice != null) { + setBtScoDeviceConnectionState(mBluetoothHeadsetDevice, + BluetoothProfile.STATE_DISCONNECTED); + } mBluetoothHeadset = null; } break; @@ -4894,49 +4941,9 @@ public class AudioService extends IAudioService.Stub { } 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 @@ public class KeySetManagerService { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { + " 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 @@ public class PackageManagerService extends IPackageManager.Stub { } } + 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,19 +11211,19 @@ public class PackageManagerService extends IPackageManager.Stub { 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) { - // default to original signature matching - if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures) - != PackageManager.SIGNATURE_MATCH) { + if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) { + if(!checkUpgradeKeySetLP(ps, pkg)) { res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "New package has a different signature: " + pkgName); + "New package not signed by keys specified by upgrade-keysets: " + + pkgName); return; } } else { - if(!checkUpgradeKeySetLP(ps, pkg)) { + // default to original signature matching + if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures) + != PackageManager.SIGNATURE_MATCH) { res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "New package not signed by keys specified by upgrade-keysets: " - + pkgName); + "New package has a different signature: " + pkgName); return; } } @@ -11633,20 +11655,20 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { // 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } 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 @@ public class MidiService extends IMidiManager.Stub { 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.pm.UserInfo; 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.provider.Settings; 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements boolean mAppIdleParoled; private boolean mScreenOn; private long mLastAppIdleParoledTime; - long mAppIdleDurationMillis; - long mCheckIdleIntervalMillis = DEFAULT_CHECK_IDLE_INTERVAL; + long mScreenOnTime; long mScreenOnSystemTimeSnapshot; @@ -185,11 +180,7 @@ public class UsageStatsService extends SystemService implements 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 class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements // 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } 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 @@ public class UsageStatsService extends SystemService implements } /** - * 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 @@ public abstract class Connection extends Conferenceable { /** * 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 @@ public abstract class Connection extends Conferenceable { 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 abstract class Connection extends Conferenceable { public VideoProvider() { mBinder = new VideoProvider.VideoProviderBinder(); - mMessageHandler = new VideoProvider.VideoProviderHandler(); + mMessageHandler = new VideoProvider.VideoProviderHandler(Looper.getMainLooper()); } /** @@ -763,11 +772,12 @@ public abstract class Connection extends Conferenceable { */ 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 abstract class Connection extends Conferenceable { 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 abstract class Connection extends Conferenceable { */ 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 abstract class Connection extends Conferenceable { */ 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 abstract class Connection extends Conferenceable { */ 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 abstract class Connection extends Conferenceable { */ 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 abstract class Connection extends Conferenceable { */ 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) { } } } |
