diff options
32 files changed, 648 insertions, 127 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f01feea14e73..45f7eba2af02 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5766,7 +5766,7 @@ public final class ActivityThread { final int preloadedFontsResource = info.metaData.getInt( ApplicationInfo.METADATA_PRELOADED_FONTS, 0); if (preloadedFontsResource != 0) { - data.info.mResources.preloadFonts(preloadedFontsResource); + data.info.getResources().preloadFonts(preloadedFontsResource); } } } catch (RemoteException e) { diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java index 0d14a8d1e51f..80fb80588e55 100644 --- a/core/java/android/app/SharedElementCallback.java +++ b/core/java/android/app/SharedElementCallback.java @@ -27,6 +27,7 @@ import android.os.Bundle; import android.os.Parcelable; import android.transition.TransitionUtils; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ImageView.ScaleType; @@ -202,7 +203,8 @@ public abstract class SharedElementCallback { } else { mTempMatrix.set(viewToGlobalMatrix); } - return TransitionUtils.createViewBitmap(sharedElement, mTempMatrix, screenBounds); + ViewGroup parent = (ViewGroup) sharedElement.getParent(); + return TransitionUtils.createViewBitmap(sharedElement, mTempMatrix, screenBounds, parent); } /** diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index c208f1dbba8a..9383626360dc 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -1265,7 +1265,7 @@ public class AssistStructure implements Parcelable { * {@link android.service.autofill.AutofillService} for more details. * * @return domain-only part of the document. For example, if the full URL is - * {@code https://my.site/login?user=my_user}, it returns {@code my.site}. + * {@code https://example.com/login?user=my_user}, it returns {@code example.com}. */ @Nullable public String getWebDomain() { return mWebDomain; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 54f587ef28b9..b1a2133168b5 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10839,6 +10839,26 @@ public final class Settings { */ public static final String ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE = "enable_deletion_helper_no_threshold_toggle"; + + /** + * The list of snooze options for notifications + * This is encoded as a key=value list, separated by commas. Ex: + * + * "default=60,options_array=15:30:60:120" + * + * The following keys are supported: + * + * <pre> + * default (int) + * options_array (string) + * </pre> + * + * All delays in integer minutes. Array order is respected. + * Options will be used in order up to the maximum allowed by the UI. + * @hide + */ + public static final String NOTIFICATION_SNOOZE_OPTIONS = + "notification_snooze_options"; } /** diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java index 3306a5055297..084b79d58301 100644 --- a/core/java/android/transition/TransitionUtils.java +++ b/core/java/android/transition/TransitionUtils.java @@ -101,7 +101,7 @@ public class TransitionUtils { ImageView copy = new ImageView(view.getContext()); copy.setScaleType(ImageView.ScaleType.CENTER_CROP); - Bitmap bitmap = createViewBitmap(view, matrix, bounds); + Bitmap bitmap = createViewBitmap(view, matrix, bounds, sceneRoot); if (bitmap != null) { copy.setImageBitmap(bitmap); } @@ -156,11 +156,18 @@ public class TransitionUtils { * returning. * @param bounds The bounds of the bitmap in the destination coordinate system (where the * view should be presented. Typically, this is matrix.mapRect(viewBounds); + * @param sceneRoot A ViewGroup that is attached to the window to temporarily contain the view + * if it isn't attached to the window. * @return A bitmap of the given view or null if bounds has no width or height. */ - public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds) { - if (!view.isAttachedToWindow()) { - return null; + public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds, + ViewGroup sceneRoot) { + final boolean addToOverlay = !view.isAttachedToWindow(); + if (addToOverlay) { + if (sceneRoot == null || !sceneRoot.isAttachedToWindow()) { + return null; + } + sceneRoot.getOverlay().add(view); } Bitmap bitmap = null; int bitmapWidth = Math.round(bounds.width()); @@ -181,6 +188,9 @@ public class TransitionUtils { node.end(canvas); bitmap = ThreadedRenderer.createHardwareBitmap(node, bitmapWidth, bitmapHeight); } + if (addToOverlay) { + sceneRoot.getOverlay().remove(view); + } return bitmap; } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index e3a55cb617db..6e0ba3413e8c 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -100,7 +100,7 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; private static final int QUERY_TARGET_SERVICE_LIMIT = 5; - private static final int WATCHDOG_TIMEOUT_MILLIS = 5000; + private static final int WATCHDOG_TIMEOUT_MILLIS = 2000; private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 43e762d34967..7d9c50dc432e 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -277,6 +277,7 @@ public class SettingsBackupTest { Settings.Global.NEW_CONTACT_AGGREGATOR, Settings.Global.NITZ_UPDATE_DIFF, Settings.Global.NITZ_UPDATE_SPACING, + Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, Settings.Global.NSD_ON, Settings.Global.NTP_SERVER, Settings.Global.NTP_TIMEOUT, diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index e3527e359d62..43fd2708ee3e 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -658,6 +658,14 @@ public class ImageFormat { * float confidence = floatDepthBuffer.get(); * </pre> * + * For camera devices that support the + * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT} + * capability, DEPTH_POINT_CLOUD coordinates have units of meters, and the coordinate system is + * defined by the camera's pose transforms: + * {@link android.hardware.camera2.CameraCharacteristics#LENS_POSE_TRANSLATION} and + * {@link android.hardware.camera2.CameraCharacteristics#LENS_POSE_ROTATION}. That means the origin is + * the optical center of the camera device, and the positive Z axis points along the camera's optical axis, + * toward the scene. */ public static final int DEPTH_POINT_CLOUD = 0x101; diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index 4808d7a5aa6a..09449a183d96 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -127,8 +127,9 @@ public abstract class PlayerBase { Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e); } synchronized (mLock) { + boolean attributesChanged = (mAttributes != attr); mAttributes = attr; - updateAppOpsPlayAudio_sync(); + updateAppOpsPlayAudio_sync(attributesChanged); } } @@ -200,16 +201,13 @@ public abstract class PlayerBase { } void baseSetVolume(float leftVolume, float rightVolume) { - final boolean hasAppOpsPlayAudio; + final boolean isRestricted; synchronized (mLock) { mLeftVolume = leftVolume; mRightVolume = rightVolume; - hasAppOpsPlayAudio = mHasAppOpsPlayAudio; - if (isRestricted_sync()) { - return; - } + isRestricted = isRestricted_sync(); } - playerSetVolume(!hasAppOpsPlayAudio/*muting*/, + playerSetVolume(isRestricted/*muting*/, leftVolume * mPanMultiplierL, rightVolume * mPanMultiplierR); } @@ -250,7 +248,7 @@ public abstract class PlayerBase { private void updateAppOpsPlayAudio() { synchronized (mLock) { - updateAppOpsPlayAudio_sync(); + updateAppOpsPlayAudio_sync(false); } } @@ -258,7 +256,7 @@ public abstract class PlayerBase { * To be called whenever a condition that might affect audibility of this player is updated. * Must be called synchronized on mLock. */ - void updateAppOpsPlayAudio_sync() { + void updateAppOpsPlayAudio_sync(boolean attributesChanged) { boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio; try { int mode = AppOpsManager.MODE_IGNORED; @@ -275,9 +273,10 @@ public abstract class PlayerBase { // AppsOps alters a player's volume; when the restriction changes, reflect it on the actual // volume used by the player try { - if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) { + if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio || + attributesChanged) { getService().playerHasOpPlayAudio(mPlayerIId, mHasAppOpsPlayAudio); - if (mHasAppOpsPlayAudio) { + if (!isRestricted_sync()) { if (DEBUG_APP_OPS) { Log.v(TAG, "updateAppOpsPlayAudio: unmuting player, vol=" + mLeftVolume + "/" + mRightVolume); diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java index 8fc9fa6a4f26..9fbadee3075a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java @@ -131,7 +131,7 @@ public class StorageStatsSource { } public long getTotalBytes() { - return mStats.getCacheBytes() + mStats.getCodeBytes() + mStats.getDataBytes(); + return mStats.getAppBytes() + mStats.getDataBytes(); } } }
\ No newline at end of file diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java new file mode 100644 index 000000000000..3dabe99f3776 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/StorageStatsSourceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.applications; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.usage.StorageStats; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class StorageStatsSourceTest { + @Test + public void AppStorageStatsImpl_totalCorrectly() { + StorageStats storageStats = new StorageStats(); + storageStats.cacheBytes = 1; + storageStats.codeBytes = 10; + storageStats.dataBytes = 100; + StorageStatsSource.AppStorageStatsImpl stats = new StorageStatsSource.AppStorageStatsImpl( + storageStats); + + // Note that this does not double add the cache (111). + assertThat(stats.getTotalBytes()).isEqualTo(110); + } +} diff --git a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml b/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml index db2eb3a27455..bff97f6f8fe4 100644 --- a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml +++ b/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml @@ -17,6 +17,6 @@ <corners android:radius="@dimen/borderless_button_radius" /> - <solid android:color="#CC000000" /> + <solid android:color="?attr/clearAllBackgroundColor" /> </shape> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 87f6306ffb9a..8a1e0b92e4d7 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -422,4 +422,14 @@ increase the rate of unintentional unlocks. --> <bool name="config_lockscreenAntiFalsingClassifierEnabled">true</bool> + <!-- Snooze: default notificaiton snooze time. --> + <integer name="config_notification_snooze_time_default">60</integer> + + <!-- Snooze: List of snooze values in integer minutes. --> + <integer-array name="config_notification_snooze_times"> + <item>15</item> + <item>30</item> + <item>60</item> + <item>120</item> + </integer-array> </resources> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 2148c8047476..e5f802944ff3 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -82,10 +82,10 @@ <!-- Accessibility actions for the notification menu --> <item type="id" name="action_snooze_undo"/> - <item type="id" name="action_snooze_15_min"/> - <item type="id" name="action_snooze_30_min"/> - <item type="id" name="action_snooze_1_hour"/> - <item type="id" name="action_snooze_2_hours"/> + <item type="id" name="action_snooze_shorter"/> + <item type="id" name="action_snooze_short"/> + <item type="id" name="action_snooze_long"/> + <item type="id" name="action_snooze_longer"/> <item type="id" name="action_snooze_assistant_suggestion_1"/> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 4a11fd05664d..f38420e686ab 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -203,10 +203,6 @@ public class RecentsView extends FrameLayout { mStackButtonShadowDistance.x, mStackButtonShadowDistance.y, mStackButtonShadowColor); } - if (Recents.getConfiguration().isLowRamDevice) { - int bgColor = Utils.getColorAttr(mContext, R.attr.clearAllBackgroundColor); - mStackActionButton.setBackgroundColor(bgColor); - } } // Let's also require dark status and nav bars if the text is dark diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java index c45ca54024db..492ab44d499b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java @@ -16,8 +16,13 @@ package com.android.systemui.statusbar; */ import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; @@ -28,12 +33,15 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Typeface; +import android.metrics.LogMaker; import android.os.Bundle; +import android.provider.Settings; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.text.SpannableString; import android.text.style.StyleSpan; import android.util.AttributeSet; +import android.util.KeyValueListParser; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -51,11 +59,23 @@ import com.android.systemui.R; public class NotificationSnooze extends LinearLayout implements NotificationGuts.GutsContent, View.OnClickListener { + private static final String TAG = "NotificationSnooze"; /** * If this changes more number increases, more assistant action resId's should be defined for * accessibility purposes, see {@link #setSnoozeOptions(List)} */ private static final int MAX_ASSISTANT_SUGGESTIONS = 1; + private static final String KEY_DEFAULT_SNOOZE = "default"; + private static final String KEY_OPTIONS = "options_array"; + private static final LogMaker OPTIONS_OPEN_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS) + .setType(MetricsEvent.TYPE_OPEN); + private static final LogMaker OPTIONS_CLOSE_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS) + .setType(MetricsEvent.TYPE_CLOSE); + private static final LogMaker UNDO_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE) + .setType(MetricsEvent.TYPE_ACTION); private NotificationGuts mGutsContainer; private NotificationSwipeActionHelper mSnoozeListener; private StatusBarNotification mSbn; @@ -72,9 +92,31 @@ public class NotificationSnooze extends LinearLayout private boolean mSnoozing; private boolean mExpanded; private AnimatorSet mExpandAnimation; + private KeyValueListParser mParser; + + private final static int[] sAccessibilityActions = { + R.id.action_snooze_shorter, + R.id.action_snooze_short, + R.id.action_snooze_long, + R.id.action_snooze_longer, + }; + + private MetricsLogger mMetricsLogger = new MetricsLogger(); public NotificationSnooze(Context context, AttributeSet attrs) { super(context, attrs); + mParser = new KeyValueListParser(','); + } + + @VisibleForTesting + SnoozeOption getDefaultOption() + { + return mDefaultOption; + } + + @VisibleForTesting + void setKeyValueListParser(KeyValueListParser parser) { + mParser = parser; } @Override @@ -96,7 +138,13 @@ public class NotificationSnooze extends LinearLayout mSnoozeOptions = getDefaultSnoozeOptions(); createOptionViews(); - setSelected(mDefaultOption); + setSelected(mDefaultOption, false); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption); } @Override @@ -136,7 +184,7 @@ public class NotificationSnooze extends LinearLayout SnoozeOption so = mSnoozeOptions.get(i); if (so.getAccessibilityAction() != null && so.getAccessibilityAction().getId() == action) { - setSelected(so); + setSelected(so, true); return true; } } @@ -172,17 +220,49 @@ public class NotificationSnooze extends LinearLayout mSbn = sbn; } - private ArrayList<SnoozeOption> getDefaultSnoozeOptions() { + @VisibleForTesting + ArrayList<SnoozeOption> getDefaultSnoozeOptions() { + final Resources resources = getContext().getResources(); ArrayList<SnoozeOption> options = new ArrayList<>(); + try { + final String config = Settings.Global.getString(getContext().getContentResolver(), + Settings.Global.NOTIFICATION_SNOOZE_OPTIONS); + mParser.setString(config); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Bad snooze constants"); + } + + final int defaultSnooze = mParser.getInt(KEY_DEFAULT_SNOOZE, + resources.getInteger(R.integer.config_notification_snooze_time_default)); + final int[] snoozeTimes = parseIntArray(KEY_OPTIONS, + resources.getIntArray(R.array.config_notification_snooze_times)); - options.add(createOption(15 /* minutes */, R.id.action_snooze_15_min)); - options.add(createOption(30 /* minutes */, R.id.action_snooze_30_min)); - mDefaultOption = createOption(60 /* minutes */, R.id.action_snooze_1_hour); - options.add(mDefaultOption); - options.add(createOption(60 * 2 /* minutes */, R.id.action_snooze_2_hours)); + for (int i = 0; i < snoozeTimes.length && i < sAccessibilityActions.length; i++) { + int snoozeTime = snoozeTimes[i]; + SnoozeOption option = createOption(snoozeTime, sAccessibilityActions[i]); + if (i == 0 || snoozeTime == defaultSnooze) { + mDefaultOption = option; + } + options.add(option); + } return options; } + @VisibleForTesting + int[] parseIntArray(final String key, final int[] defaultArray) { + final String value = mParser.getString(key, null); + if (value != null) { + try { + return Arrays.stream(value.split(":")).map(String::trim).mapToInt( + Integer::parseInt).toArray(); + } catch (NumberFormatException e) { + return defaultArray; + } + } else { + return defaultArray; + } + } + private SnoozeOption createOption(int minutes, int accessibilityActionId) { Resources res = getResources(); boolean showInHours = minutes >= 60; @@ -268,12 +348,24 @@ public class NotificationSnooze extends LinearLayout mExpandAnimation.start(); } - private void setSelected(SnoozeOption option) { + private void setSelected(SnoozeOption option, boolean userAction) { mSelectedOption = option; mSelectedOptionText.setText(option.getConfirmation()); showSnoozeOptions(false); hideSelectedOption(); sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (userAction) { + logOptionSelection(MetricsEvent.NOTIFICATION_SELECT_SNOOZE, option); + } + } + + private void logOptionSelection(int category, SnoozeOption option) { + int index = mSnoozeOptions.indexOf(option); + long duration = TimeUnit.MINUTES.toMillis(option.getMinutesToSnoozeFor()); + mMetricsLogger.write(new LogMaker(category) + .setType(MetricsEvent.TYPE_ACTION) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_INDEX, index) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, duration)); } @Override @@ -284,13 +376,15 @@ public class NotificationSnooze extends LinearLayout final int id = v.getId(); final SnoozeOption tag = (SnoozeOption) v.getTag(); if (tag != null) { - setSelected(tag); + setSelected(tag, true); } else if (id == R.id.notification_snooze) { // Toggle snooze options showSnoozeOptions(!mExpanded); + mMetricsLogger.write(!mExpanded ? OPTIONS_OPEN_LOG : OPTIONS_CLOSE_LOG); } else { // Undo snooze was selected undoSnooze(v); + mMetricsLogger.write(UNDO_LOG); } } @@ -321,7 +415,7 @@ public class NotificationSnooze extends LinearLayout @Override public View getContentView() { // Reset the view before use - setSelected(mDefaultOption); + setSelected(mDefaultOption, false); return this; } @@ -343,7 +437,7 @@ public class NotificationSnooze extends LinearLayout return true; } else { // The view should actually be closed - setSelected(mSnoozeOptions.get(0)); + setSelected(mSnoozeOptions.get(0), false); return false; // Return false here so that guts handles closing the view } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java new file mode 100644 index 000000000000..6b31c967a247 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.provider.Settings; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableResources; +import android.testing.UiThreadTest; +import android.util.KeyValueListParser; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.anyString; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@UiThreadTest +public class NotificationSnoozeTest extends SysuiTestCase { + private static final int RES_DEFAULT = 2; + private static final int[] RES_OPTIONS = {1, 2, 3}; + private NotificationSnooze mNotificationSnooze; + private KeyValueListParser mMockParser; + + @Before + public void setUp() throws Exception { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, null); + TestableResources resources = mContext.getOrCreateTestableResources(); + resources.addOverride(R.integer.config_notification_snooze_time_default, RES_DEFAULT); + resources.addOverride(R.array.config_notification_snooze_times, RES_OPTIONS); + mNotificationSnooze = new NotificationSnooze(mContext, null); + mMockParser = mock(KeyValueListParser.class); + } + + @Test + public void testParseIntArrayNull() throws Exception { + when(mMockParser.getString(anyString(), isNull())).thenReturn(null); + mNotificationSnooze.setKeyValueListParser(mMockParser); + + int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS); + assertEquals(RES_OPTIONS, result); + } + + @Test + public void testParseIntArrayLeadingSep() throws Exception { + when(mMockParser.getString(anyString(), isNull())).thenReturn(":4:5:6"); + mNotificationSnooze.setKeyValueListParser(mMockParser); + + int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS); + assertEquals(RES_OPTIONS, result); + } + + @Test + public void testParseIntArrayEmptyItem() throws Exception { + when(mMockParser.getString(anyString(), isNull())).thenReturn("4::6"); + mNotificationSnooze.setKeyValueListParser(mMockParser); + + int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS); + assertEquals(RES_OPTIONS, result); + } + + @Test + public void testParseIntArrayTrailingSep() throws Exception { + when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6:"); + mNotificationSnooze.setKeyValueListParser(mMockParser); + + int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS); + assertEquals(3, result.length); + assertEquals(4, result[0]); // respect order + assertEquals(5, result[1]); + assertEquals(6, result[2]); + } + + @Test + public void testParseIntArrayGoodData() throws Exception { + when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6"); + mNotificationSnooze.setKeyValueListParser(mMockParser); + + int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS); + assertEquals(3, result.length); + assertEquals(4, result[0]); // respect order + assertEquals(5, result[1]); + assertEquals(6, result[2]); + } + + @Test + public void testGetOptionsWithNoConfig() throws Exception { + ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions(); + assertEquals(3, result.size()); + assertEquals(1, result.get(0).getMinutesToSnoozeFor()); // respect order + assertEquals(2, result.get(1).getMinutesToSnoozeFor()); + assertEquals(3, result.get(2).getMinutesToSnoozeFor()); + assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor()); + } + + @Test + public void testGetOptionsWithInvalidConfig() throws Exception { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, + "this is garbage"); + ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions(); + assertEquals(3, result.size()); + assertEquals(1, result.get(0).getMinutesToSnoozeFor()); // respect order + assertEquals(2, result.get(1).getMinutesToSnoozeFor()); + assertEquals(3, result.get(2).getMinutesToSnoozeFor()); + assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor()); + } + + @Test + public void testGetOptionsWithValidDefault() throws Exception { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, + "default=10,options_array=4:5:6:7"); + ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions(); + assertNotNull(mNotificationSnooze.getDefaultOption()); // pick one + } + + @Test + public void testGetOptionsWithValidConfig() throws Exception { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, + "default=6,options_array=4:5:6:7"); + ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions(); + assertEquals(4, result.size()); + assertEquals(4, result.get(0).getMinutesToSnoozeFor()); // respect order + assertEquals(5, result.get(1).getMinutesToSnoozeFor()); + assertEquals(6, result.get(2).getMinutesToSnoozeFor()); + assertEquals(7, result.get(3).getMinutesToSnoozeFor()); + assertEquals(6, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor()); + } + + @Test + public void testGetOptionsWithLongConfig() throws Exception { + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, + "default=6,options_array=4:5:6:7:8:9:10:11:12:13:14:15:16:17"); + ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions(); + assertTrue(result.size() > 3); + assertEquals(4, result.get(0).getMinutesToSnoozeFor()); // respect order + assertEquals(5, result.get(1).getMinutesToSnoozeFor()); + assertEquals(6, result.get(2).getMinutesToSnoozeFor()); + } +} diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 38272a02ef19..48f3b9ce9fb0 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4538,6 +4538,30 @@ message MetricsEvent { // OS: O MR AUTOFILL_UI_LATENCY = 1136; + // Action: the snooze leave-behind was shown after the user clicked the snooze icon + // OS: O MR + NOTIFICATION_SNOOZE_CLICKED = 1137; + + // Action: user selected a notification snooze duration from the drop down + // OS: O MR + NOTIFICATION_SELECT_SNOOZE = 1138; + + // attached to NOTIFICATION_SNOOZED and NOTIFICATION_SELECT_SNOOZE events + // OS: O MR + FIELD_NOTIFICATION_SNOOZE_DURATION_MS = 1139; + + // attached to NOTIFICATION_SELECT_SNOOZE events to indicate the option selected + // OS: O MR + FIELD_NOTIFICATION_SNOOZE_INDEX = 1140; + + // Action: user tapped undo on the notification snooze leave-behind + // OS: O MR + NOTIFICATION_UNDO_SNOOZE = 1141; + + // Action: user togged the visibility of the notification snooze options drop down + // OS: O MR + NOTIFICATION_SNOOZE_OPTIONS = 1142; + // ---- End O-MR1 Constants, all O-MR1 constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index edcd4b7ba99b..0dd518141cf6 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1983,7 +1983,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { if (uri == null) { return; } - String pkgName = uri.getSchemeSpecificPart(); + final String pkgName = uri.getSchemeSpecificPart(); if (pkgName != null) { pkgList = new String[] { pkgName }; } @@ -1991,7 +1991,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { // At package-changed we only care about looking at new transport states if (changed) { - String[] components = + final String[] components = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); if (MORE_DEBUG) { @@ -2001,7 +2001,8 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - mTransportManager.onPackageChanged(pkgName, components); + mBackupHandler.post( + () -> mTransportManager.onPackageChanged(pkgName, components)); return; // nothing more to do in the PACKAGE_CHANGED case } @@ -2033,7 +2034,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // If they're full-backup candidates, add them there instead final long now = System.currentTimeMillis(); - for (String packageName : pkgList) { + for (final String packageName : pkgList) { try { PackageInfo app = mPackageManager.getPackageInfo(packageName, 0); if (appGetsFullBackup(app) @@ -2050,7 +2051,8 @@ public class BackupManagerService implements BackupManagerServiceInterface { writeFullBackupScheduleAsync(); } - mTransportManager.onPackageAdded(packageName); + mBackupHandler.post( + () -> mTransportManager.onPackageAdded(packageName)); } catch (NameNotFoundException e) { // doesn't really exist; ignore it @@ -2074,8 +2076,9 @@ public class BackupManagerService implements BackupManagerServiceInterface { removePackageParticipantsLocked(pkgList, uid); } } - for (String pkgName : pkgList) { - mTransportManager.onPackageRemoved(pkgName); + for (final String pkgName : pkgList) { + mBackupHandler.post( + () -> mTransportManager.onPackageRemoved(pkgName)); } } } diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 74859777ae1e..23abd934334b 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -1197,7 +1197,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter if (uri == null) { return; } - String pkgName = uri.getSchemeSpecificPart(); + final String pkgName = uri.getSchemeSpecificPart(); if (pkgName != null) { pkgList = new String[]{pkgName}; } @@ -1205,7 +1205,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // At package-changed we only care about looking at new transport states if (changed) { - String[] components = + final String[] components = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); if (MORE_DEBUG) { @@ -1215,7 +1215,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } - mTransportManager.onPackageChanged(pkgName, components); + mBackupHandler.post( + () -> mTransportManager.onPackageChanged(pkgName, components)); return; // nothing more to do in the PACKAGE_CHANGED case } @@ -1247,7 +1248,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } // If they're full-backup candidates, add them there instead final long now = System.currentTimeMillis(); - for (String packageName : pkgList) { + for (final String packageName : pkgList) { try { PackageInfo app = mPackageManager.getPackageInfo(packageName, 0); if (AppBackupUtils.appGetsFullBackup(app) @@ -1265,7 +1266,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter writeFullBackupScheduleAsync(); } - mTransportManager.onPackageAdded(packageName); + mBackupHandler.post( + () -> mTransportManager.onPackageAdded(packageName)); } catch (NameNotFoundException e) { // doesn't really exist; ignore it @@ -1289,8 +1291,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter removePackageParticipantsLocked(pkgList, uid); } } - for (String pkgName : pkgList) { - mTransportManager.onPackageRemoved(pkgName); + for (final String pkgName : pkgList) { + mBackupHandler.post( + () -> mTransportManager.onPackageRemoved(pkgName)); } } } diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java index fb2982eb0baa..dab218db51b2 100644 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ b/services/backup/java/com/android/server/backup/TransportManager.java @@ -316,9 +316,9 @@ public class TransportManager { private class TransportConnection implements ServiceConnection { // Hold mTransportsLock to access these fields so as to provide a consistent view of them. - private IBackupTransport mBinder; + private volatile IBackupTransport mBinder; private final List<SelectBackupTransportCallback> mListeners = new ArrayList<>(); - private String mTransportName; + private volatile String mTransportName; private final ComponentName mTransportComponent; @@ -401,25 +401,24 @@ public class TransportManager { + rebindTimeout + "ms"); } + // Intentionally not synchronized -- the variable is volatile and changes to its value + // are inside synchronized blocks, providing a memory sync barrier; and this method + // does not touch any other state protected by that lock. private IBackupTransport getBinder() { - synchronized (mTransportLock) { - return mBinder; - } + return mBinder; } + // Intentionally not synchronized; same as getBinder() private String getName() { - synchronized (mTransportLock) { - return mTransportName; - } + return mTransportName; } + // Intentionally not synchronized; same as getBinder() private void bindIfUnbound() { - synchronized (mTransportLock) { - if (mBinder == null) { - Slog.d(TAG, - "Rebinding to transport " + mTransportComponent.flattenToShortString()); - bindToTransport(mTransportComponent, this); - } + if (mBinder == null) { + Slog.d(TAG, + "Rebinding to transport " + mTransportComponent.flattenToShortString()); + bindToTransport(mTransportComponent, this); } } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 027dc0862e9d..ac85e6b132bf 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -517,11 +517,14 @@ final class ServiceRecord extends Binder { } catch (PackageManager.NameNotFoundException e) { } } - if (localForegroundNoti.getSmallIcon() == null) { + if (localForegroundNoti.getSmallIcon() == null + || nm.getNotificationChannel(localPackageName, appUid, + localForegroundNoti.getChannelId()) == null) { // Notifications whose icon is 0 are defined to not show // a notification, silently ignoring it. We don't want to // just ignore it, we want to prevent the service from // being foreground. + // Also every notification needs a channel. throw new RuntimeException("invalid service notification: " + foregroundNoti); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0bc20a2e6578..5eb2a8d2066f 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -751,6 +751,9 @@ public class AudioService extends IAudioService.Stub // relies on audio policy having correct ranges for volume indexes. mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); + mPlaybackMonitor = + new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]); + mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor); mRecordMonitor = new RecordingActivityMonitor(mContext); @@ -4158,7 +4161,8 @@ public class AudioService extends IAudioService.Stub newDevice, AudioSystem.getOutputDeviceName(newDevice))); } synchronized (mConnectedDevices) { - if ((newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0 + if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS + && (newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0 && mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted && mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0 && (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) @@ -6976,7 +6980,7 @@ public class AudioService extends IAudioService.Stub //====================== // Audio playback notification //====================== - private final PlaybackActivityMonitor mPlaybackMonitor = new PlaybackActivityMonitor(); + private final PlaybackActivityMonitor mPlaybackMonitor; public void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb) { final boolean isPrivileged = diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 9195a9ba33b4..6506cf7fa189 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -17,6 +17,8 @@ package com.android.server.audio; import android.annotation.NonNull; +import android.content.Context; +import android.content.pm.PackageManager; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; @@ -90,7 +92,14 @@ public final class PlaybackActivityMonitor private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers = new HashMap<Integer, AudioPlaybackConfiguration>(); - PlaybackActivityMonitor() { + private final Context mContext; + private int mSavedAlarmVolume = -1; + private final int mMaxAlarmVolume; + private int mPrivilegedAlarmActiveCount = 0; + + PlaybackActivityMonitor(Context context, int maxAlarmVolume) { + mContext = context; + mMaxAlarmVolume = maxAlarmVolume; PlayMonitorClient.sListenerDeathMonitor = this; AudioPlaybackConfiguration.sPlayerDeathMonitor = this; } @@ -175,6 +184,38 @@ public final class PlaybackActivityMonitor } } + private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) { + if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED || + apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if ((apc.getAudioAttributes().getAllFlags() & + AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0 && + apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM && + mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, + apc.getClientPid(), apc.getClientUid()) == + PackageManager.PERMISSION_GRANTED) { + if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED && + apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if (mPrivilegedAlarmActiveCount++ == 0) { + mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex( + AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER); + AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM, + mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); + } + } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED && + apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if (--mPrivilegedAlarmActiveCount == 0) { + if (AudioSystem.getStreamVolumeIndex( + AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) == + mMaxAlarmVolume) { + AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM, + mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); + } + } + } + } + } + } + public void playerEvent(int piid, int event, int binderUid) { if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); } final boolean change; @@ -200,6 +241,7 @@ public final class PlaybackActivityMonitor } if (checkConfigurationCaller(piid, apc, binderUid)) { //TODO add generation counter to only update to the latest state + checkVolumeForPrivilegedAlarm(apc, event); change = apc.handleStateEvent(event); } else { Log.e(TAG, "Error handling event " + event); @@ -228,6 +270,7 @@ public final class PlaybackActivityMonitor "releasing player piid:" + piid)); mPlayers.remove(new Integer(piid)); mDuckingManager.removeReleased(apc); + checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 9fd54ec4f789..5159c70e991c 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -332,7 +332,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_UPDATE_INTERFACE_QUOTA = 10; private static final int MSG_REMOVE_INTERFACE_QUOTA = 11; private static final int MSG_POLICIES_CHANGED = 13; - private static final int MSG_SET_FIREWALL_RULES = 14; private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15; private static final int UID_MSG_STATE_CHANGED = 100; @@ -3184,9 +3183,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW); } } - setUidFirewallRulesAsync(chain, uidRules, CHAIN_TOGGLE_ENABLE); + setUidFirewallRulesUL(chain, uidRules, CHAIN_TOGGLE_ENABLE); } else { - setUidFirewallRulesAsync(chain, null, CHAIN_TOGGLE_DISABLE); + setUidFirewallRulesUL(chain, null, CHAIN_TOGGLE_DISABLE); } } @@ -3253,7 +3252,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - setUidFirewallRulesAsync(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE); + setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } @@ -3954,18 +3953,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { removeInterfaceQuota((String) msg.obj); return true; } - case MSG_SET_FIREWALL_RULES: { - final int chain = msg.arg1; - final int toggle = msg.arg2; - final SparseIntArray uidRules = (SparseIntArray) msg.obj; - if (uidRules != null) { - setUidFirewallRules(chain, uidRules); - } - if (toggle != CHAIN_TOGGLE_NONE) { - enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE); - } - return true; - } case MSG_RESET_FIREWALL_RULES_BY_UID: { resetUidFirewallRules(msg.arg1); return true; @@ -4111,15 +4098,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * Calls {@link #setUidFirewallRules(int, SparseIntArray)} and - * {@link #enableFirewallChainUL(int, boolean)} asynchronously. + * {@link #enableFirewallChainUL(int, boolean)} synchronously. * * @param chain firewall chain. * @param uidRules new UID rules; if {@code null}, only toggles chain state. * @param toggle whether the chain should be enabled, disabled, or not changed. */ - private void setUidFirewallRulesAsync(int chain, @Nullable SparseIntArray uidRules, + private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules, @ChainToggleType int toggle) { - mHandler.obtainMessage(MSG_SET_FIREWALL_RULES, chain, toggle, uidRules).sendToTarget(); + if (uidRules != null) { + setUidFirewallRulesUL(chain, uidRules); + } + if (toggle != CHAIN_TOGGLE_NONE) { + enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE); + } } /** @@ -4127,7 +4119,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * here to netd. It will clean up dead rules and make sure the target chain only contains rules * specified here. */ - private void setUidFirewallRules(int chain, SparseIntArray uidRules) { + private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) { try { int size = uidRules.size(); int[] uids = new int[size]; diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index 4923b06e9595..f1476b34388b 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -17,8 +17,10 @@ package com.android.server.notification; import android.app.Notification; +import android.app.NotificationChannel; public interface NotificationManagerInternal { + NotificationChannel getNotificationChannel(String pkg, int uid, String channelId); void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid, String tag, int id, Notification notification, int userId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index c6f2e8a4f512..4e465357c8e6 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3373,6 +3373,12 @@ public class NotificationManagerService extends SystemService { */ private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { @Override + public NotificationChannel getNotificationChannel(String pkg, int uid, String + channelId) { + return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false); + } + + @Override public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, String tag, int id, Notification notification, int userId) { enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, @@ -3741,6 +3747,8 @@ public class NotificationManagerService extends SystemService { MetricsLogger.action(r.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsEvent.TYPE_CLOSE) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, + mDuration) .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, mSnoozeCriterionId == null ? 0 : 1)); boolean wasPosted = removeFromNotificationListsLocked(r); diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp index d7508d257db4..c139b73db296 100644 --- a/tools/aapt2/java/ClassDefinition.cpp +++ b/tools/aapt2/java/ClassDefinition.cpp @@ -41,18 +41,21 @@ void MethodDefinition::WriteToStream(const StringPiece& prefix, bool final, ClassDefinition::Result ClassDefinition::AddMember(std::unique_ptr<ClassMember> member) { Result result = Result::kAdded; - auto iter = members_.find(member); - if (iter != members_.end()) { - members_.erase(iter); + auto iter = indexed_members_.find(member->GetName()); + if (iter != indexed_members_.end()) { + // Overwrite the entry. + ordered_members_[iter->second].reset(); result = Result::kOverridden; } - members_.insert(std::move(member)); + + indexed_members_[member->GetName()] = ordered_members_.size(); + ordered_members_.push_back(std::move(member)); return result; } bool ClassDefinition::empty() const { - for (const std::unique_ptr<ClassMember>& member : members_) { - if (!member->empty()) { + for (const std::unique_ptr<ClassMember>& member : ordered_members_) { + if (member != nullptr && !member->empty()) { return false; } } @@ -61,7 +64,7 @@ bool ClassDefinition::empty() const { void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final, std::ostream* out) const { - if (members_.empty() && !create_if_empty_) { + if (empty() && !create_if_empty_) { return; } @@ -76,9 +79,14 @@ void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final, std::string new_prefix = prefix.to_string(); new_prefix.append(kIndent); - for (const std::unique_ptr<ClassMember>& member : members_) { - member->WriteToStream(new_prefix, final, out); - *out << "\n"; + for (const std::unique_ptr<ClassMember>& member : ordered_members_) { + // There can be nullptr members when a member is added to the ClassDefinition + // and takes precedence over a previous member with the same name. The overridden member is + // set to nullptr. + if (member != nullptr) { + member->WriteToStream(new_prefix, final, out); + *out << "\n"; + } } *out << prefix << "}"; diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h index 6c4bcad83d2a..28a3489e71a4 100644 --- a/tools/aapt2/java/ClassDefinition.h +++ b/tools/aapt2/java/ClassDefinition.h @@ -18,8 +18,9 @@ #define AAPT_JAVA_CLASSDEFINITION_H #include <ostream> -#include <set> #include <string> +#include <unordered_map> +#include <vector> #include "android-base/macros.h" #include "androidfw/StringPiece.h" @@ -73,21 +74,18 @@ class PrimitiveMember : public ClassMember { void WriteToStream(const android::StringPiece& prefix, bool final, std::ostream* out) const override { ClassMember::WriteToStream(prefix, final, out); - - *out << prefix << "public static " << (final ? "final " : "") << "int " - << name_ << "=" << val_ << ";"; + *out << prefix << "public static " << (final ? "final " : "") << "int " << name_ << "=" << val_ + << ";"; } private: + DISALLOW_COPY_AND_ASSIGN(PrimitiveMember); + std::string name_; T val_; - - DISALLOW_COPY_AND_ASSIGN(PrimitiveMember); }; -/** - * Specialization for strings so they get the right type and are quoted with "". - */ +// Specialization for strings so they get the right type and are quoted with "". template <> class PrimitiveMember<std::string> : public ClassMember { public: @@ -111,10 +109,10 @@ class PrimitiveMember<std::string> : public ClassMember { } private: + DISALLOW_COPY_AND_ASSIGN(PrimitiveMember); + std::string name_; std::string val_; - - DISALLOW_COPY_AND_ASSIGN(PrimitiveMember); }; using IntMember = PrimitiveMember<uint32_t>; @@ -160,10 +158,10 @@ class PrimitiveArrayMember : public ClassMember { } private: + DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember); + std::string name_; std::vector<T> elements_; - - DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember); }; using ResourceArrayMember = PrimitiveArrayMember<ResourceId>; @@ -185,12 +183,16 @@ class MethodDefinition : public ClassMember { } // Even if the method is empty, we always want to write the method signature. - bool empty() const override { return false; } + bool empty() const override { + return false; + } void WriteToStream(const android::StringPiece& prefix, bool final, std::ostream* out) const override; private: + DISALLOW_COPY_AND_ASSIGN(MethodDefinition); + std::string signature_; std::vector<std::string> statements_; }; @@ -222,19 +224,13 @@ class ClassDefinition : public ClassMember { std::ostream* out) const override; private: - struct ClassMemberCompare { - using T = std::unique_ptr<ClassMember>; - bool operator()(const T& a, const T& b) const { - return a->GetName() < b->GetName(); - } - }; + DISALLOW_COPY_AND_ASSIGN(ClassDefinition); std::string name_; ClassQualifier qualifier_; bool create_if_empty_; - std::set<std::unique_ptr<ClassMember>, ClassMemberCompare> members_; - - DISALLOW_COPY_AND_ASSIGN(ClassDefinition); + std::vector<std::unique_ptr<ClassMember>> ordered_members_; + std::unordered_map<android::StringPiece, size_t> indexed_members_; }; } // namespace aapt diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp index 271279ff5e92..84bf04134ad9 100644 --- a/tools/aapt2/java/JavaClassGenerator_test.cpp +++ b/tools/aapt2/java/JavaClassGenerator_test.cpp @@ -23,6 +23,8 @@ #include "util/Util.h" using android::StringPiece; +using ::testing::Lt; +using ::testing::Ne; namespace aapt { @@ -329,6 +331,53 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) EXPECT_NE(std::string::npos, actual.find(styleable.GetComment().data())); } +TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) { + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .SetPackageId("android", 0x01) + .AddValue("android:attr/layout_gravity", util::make_unique<Attribute>()) + .AddValue("android:attr/background", util::make_unique<Attribute>()) + .AddValue("android:styleable/ActionBar", + test::StyleableBuilder() + .AddItem("android:attr/background", ResourceId(0x01010000)) + .Build()) + .AddValue("android:styleable/ActionBar.LayoutParams", + test::StyleableBuilder() + .AddItem("android:attr/layout_gravity", ResourceId(0x01010001)) + .Build()) + .Build(); + + std::unique_ptr<IAaptContext> context = + test::ContextBuilder() + .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) + .SetNameManglerPolicy(NameManglerPolicy{"android"}) + .Build(); + + JavaClassGeneratorOptions options; + JavaClassGenerator generator(context.get(), table.get(), {}); + std::stringstream out; + ASSERT_TRUE(generator.Generate("android", &out)); + std::string output = out.str(); + + std::string::size_type actionbar_pos = output.find("int[] ActionBar"); + ASSERT_THAT(actionbar_pos, Ne(std::string::npos)); + + std::string::size_type actionbar_background_pos = output.find("int ActionBar_background"); + ASSERT_THAT(actionbar_background_pos, Ne(std::string::npos)); + + std::string::size_type actionbar_layout_params_pos = output.find("int[] ActionBar_LayoutParams"); + ASSERT_THAT(actionbar_layout_params_pos, Ne(std::string::npos)); + + std::string::size_type actionbar_layout_params_layout_gravity_pos = + output.find("int ActionBar_LayoutParams_layout_gravity"); + ASSERT_THAT(actionbar_layout_params_layout_gravity_pos, Ne(std::string::npos)); + + EXPECT_THAT(actionbar_pos, Lt(actionbar_background_pos)); + EXPECT_THAT(actionbar_pos, Lt(actionbar_layout_params_pos)); + EXPECT_THAT(actionbar_background_pos, Lt(actionbar_layout_params_pos)); + EXPECT_THAT(actionbar_layout_params_pos, Lt(actionbar_layout_params_layout_gravity_pos)); +} + TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) { Attribute attr(false); attr.SetComment(StringPiece("removed")); diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp index cbb652ed9a1a..1d122db9990f 100644 --- a/tools/aapt2/xml/XmlDom.cpp +++ b/tools/aapt2/xml/XmlDom.cpp @@ -274,6 +274,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos switch (code) { case ResXMLParser::START_NAMESPACE: { NamespaceDecl decl; + decl.line_number = tree.getLineNumber(); + size_t len; const char16_t* str16 = tree.getNamespacePrefix(&len); if (str16) { @@ -288,6 +290,7 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos if (pending_element == nullptr) { pending_element = util::make_unique<Element>(); } + pending_element->namespace_decls.push_back(std::move(decl)); break; } @@ -297,8 +300,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos el = std::move(pending_element); } else { el = util::make_unique<Element>(); - ; } + el->line_number = tree.getLineNumber(); size_t len; const char16_t* str16 = tree.getElementNamespace(&len); diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp index 6ed2d616f782..b501cfd4b8b9 100644 --- a/tools/aapt2/xml/XmlDom_test.cpp +++ b/tools/aapt2/xml/XmlDom_test.cpp @@ -18,6 +18,7 @@ #include <string> +#include "flatten/XmlFlattener.h" #include "io/StringInputStream.h" #include "test/Test.h" @@ -51,6 +52,36 @@ TEST(XmlDomTest, Inflate) { EXPECT_THAT(el->namespace_decls[0].prefix, StrEq("android")); } +TEST(XmlDomTest, BinaryInflate) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<XmlResource> doc = util::make_unique<XmlResource>(); + doc->root = util::make_unique<Element>(); + doc->root->name = "Layout"; + doc->root->line_number = 2u; + + NamespaceDecl decl; + decl.uri = kSchemaAndroid; + decl.prefix = "android"; + decl.line_number = 2u; + doc->root->namespace_decls.push_back(decl); + + BigBuffer buffer(4096); + XmlFlattener flattener(&buffer, {}); + ASSERT_TRUE(flattener.Consume(context.get(), doc.get())); + + auto block = util::Copy(buffer); + std::unique_ptr<XmlResource> new_doc = + Inflate(block.get(), buffer.size(), context->GetDiagnostics(), Source("test.xml")); + ASSERT_THAT(new_doc, NotNull()); + + EXPECT_THAT(new_doc->root->name, StrEq("Layout")); + EXPECT_THAT(new_doc->root->line_number, Eq(2u)); + ASSERT_THAT(new_doc->root->namespace_decls, SizeIs(1u)); + EXPECT_THAT(new_doc->root->namespace_decls[0].uri, StrEq(kSchemaAndroid)); + EXPECT_THAT(new_doc->root->namespace_decls[0].prefix, StrEq("android")); + EXPECT_THAT(new_doc->root->namespace_decls[0].line_number, Eq(2u)); +} + // Escaping is handled after parsing of the values for resource-specific values. TEST(XmlDomTest, ForwardEscapes) { std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"( |