From 1da4c747c646b40b49b6a3813cc049441e970679 Mon Sep 17 00:00:00 2001 From: Kevin Han Date: Fri, 17 Apr 2020 15:39:55 -0700 Subject: [PATCH 001/166] Don't allow HUNs during dozing fullscreen intents Move check preventing HUN from canAlertCommonWake to canAlertCommon so that we dont HUN while a fullscreen intent is active. Bug: 153705384 Test: repro in bug does not cause HUN Change-Id: I3e520621027d53da44556e925f21756bea8eac5b --- .../NotificationInterruptStateProviderImpl.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 324bc923fb35..fefad531377f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -336,6 +336,14 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } } + + if (entry.hasJustLaunchedFullScreenIntent()) { + if (DEBUG_HEADS_UP) { + Log.d(TAG, "No alerting: recent fullscreen: " + sbn.getKey()); + } + return false; + } + return true; } @@ -365,13 +373,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } - if (entry.hasJustLaunchedFullScreenIntent()) { - if (DEBUG_HEADS_UP) { - Log.d(TAG, "No alerting: recent fullscreen: " + sbn.getKey()); - } - return false; - } - return true; } -- GitLab From 03bf28bf5c62deb2e1d420a0a9028a09412af972 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 17 Apr 2020 15:53:20 -0700 Subject: [PATCH 002/166] Update ProcessRecord ApplicationInfo for RROs When overlays update, the changes in ApplicationInfo are propagated to affected processes. The ApplicationInfo in the ProcessRecords of the running processes are not updated in ActivityManagerService. Persistent processes reuse the existing ProcessRecord when they are restarted, so they are restarted with outdated overlay paths. This change updates the ApplicationInfo of ProcessRecords when overlay paths change. Bug: 154242963 Test: open settings > system > gestures switch to another navigation mode kill sysui (adb shell kill `pid com.android.systemui`) Confirm that navigation mode has not been reset Change-Id: I85f89df538079e1fb1d695a9b13b388dc338b47b --- .../com/android/server/am/ProcessList.java | 25 +++++++++++-------- .../com/android/server/am/ProcessRecord.java | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 595275d20154..caacc2d8667f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -3569,17 +3569,22 @@ public final class ProcessList { final int packageCount = app.pkgList.size(); for (int j = 0; j < packageCount; j++) { final String packageName = app.pkgList.keyAt(j); - if (updateFrameworkRes || packagesToUpdate.contains(packageName)) { - try { - final ApplicationInfo ai = AppGlobals.getPackageManager() - .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId); - if (ai != null) { - app.thread.scheduleApplicationInfoChanged(ai); - } - } catch (RemoteException e) { - Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s", - packageName, app)); + if (!updateFrameworkRes && !packagesToUpdate.contains(packageName)) { + continue; + } + try { + final ApplicationInfo ai = AppGlobals.getPackageManager() + .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId); + if (ai == null) { + continue; } + app.thread.scheduleApplicationInfoChanged(ai); + if (ai.packageName.equals(app.info.packageName)) { + app.info = ai; + } + } catch (RemoteException e) { + Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s", + packageName, app)); } } } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 61ebc361b6af..60c119aca9d0 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -86,7 +86,7 @@ class ProcessRecord implements WindowProcessListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM; private final ActivityManagerService mService; // where we came from - final ApplicationInfo info; // all about the first app in the process + volatile ApplicationInfo info; // all about the first app in the process final ProcessInfo processInfo; // if non-null, process-specific manifest info final boolean isolated; // true if this is a special isolated process final boolean appZygote; // true if this is forked from the app zygote -- GitLab From 2b4bdbb08151371f4fe88a2f524a988ffb2fe1b3 Mon Sep 17 00:00:00 2001 From: Kevin Han Date: Tue, 21 Apr 2020 17:26:51 -0700 Subject: [PATCH 003/166] Fix notif content when child updated to top level Since children can be updated to not be in groups and become a top level notification, we need to check and ensure that we inflate the content of any top level notifications that don't have content. Bug: 154227833 Test: repro in bug Test: atest DynamicChildBindControllerTest Change-Id: I83120691e20f63ee4f3d440d84c005cdfe84a719 --- .../NotificationViewHierarchyManager.java | 6 ++-- .../DynamicChildBindController.java | 30 ++++++++++++------- .../DynamicChildBindControllerTest.java | 4 +-- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index afb50027f03d..c6f5f6684237 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -60,7 +60,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private final Handler mHandler; - /** Re-usable map of notifications to their sorted children.*/ + /** Re-usable map of top-level notifications to their sorted children if any.*/ private final HashMap> mTmpChildOrderMap = new HashMap<>(); @@ -206,6 +206,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } orderedChildren.add(ent); } else { + // Top-level notif + mTmpChildOrderMap.put(ent, null); toShow.add(ent.getRow()); } } @@ -283,7 +285,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } - mDynamicChildBindController.updateChildContentViews(mTmpChildOrderMap); + mDynamicChildBindController.updateContentViews(mTmpChildOrderMap); mVisualStabilityManager.onReorderingFinished(); // clear the map again for the next usage mTmpChildOrderMap.clear(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java index 148cdea92052..57b41f36e51f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java @@ -63,44 +63,52 @@ public class DynamicChildBindController { } /** - * Update the child content views, unbinding content views on children that won't be visible - * and binding content views on children that will be visible eventually. + * Update the content views, unbinding content views on children that won't be visible + * and binding content views on children that will be visible eventually and previously unbound + * children that are no longer children. * - * @param groupNotifs map of notification summaries to their children + * @param groupNotifs map of top-level notifs to their children, if any */ - public void updateChildContentViews( + public void updateContentViews( Map> groupNotifs) { for (NotificationEntry entry : groupNotifs.keySet()) { List children = groupNotifs.get(entry); + if (children == null) { + if (!hasContent(entry)) { + // Case where child is updated to be top level + bindContent(entry); + } + continue; + } for (int j = 0; j < children.size(); j++) { NotificationEntry childEntry = children.get(j); if (j >= mChildBindCutoff) { - if (hasChildContent(childEntry)) { - freeChildContent(childEntry); + if (hasContent(childEntry)) { + freeContent(childEntry); } } else { - if (!hasChildContent(childEntry)) { - bindChildContent(childEntry); + if (!hasContent(childEntry)) { + bindContent(childEntry); } } } } } - private boolean hasChildContent(NotificationEntry entry) { + private boolean hasContent(NotificationEntry entry) { ExpandableNotificationRow row = entry.getRow(); return row.getPrivateLayout().getContractedChild() != null || row.getPrivateLayout().getExpandedChild() != null; } - private void freeChildContent(NotificationEntry entry) { + private void freeContent(NotificationEntry entry) { RowContentBindParams params = mStage.getStageParams(entry); params.markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED); params.markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED); mStage.requestRebind(entry, null); } - private void bindChildContent(NotificationEntry entry) { + private void bindContent(NotificationEntry entry) { RowContentBindParams params = mStage.getStageParams(entry); params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED); params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java index 29040147d1ca..d0dfb17111c6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java @@ -80,7 +80,7 @@ public class DynamicChildBindControllerTest extends SysuiTestCase { when(mBindStage.getStageParams(lastChild)).thenReturn(bindParams); // WHEN the controller gets the list - mDynamicChildBindController.updateChildContentViews(mGroupNotifs); + mDynamicChildBindController.updateContentViews(mGroupNotifs); // THEN we free content views verify(bindParams).markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED); @@ -101,7 +101,7 @@ public class DynamicChildBindControllerTest extends SysuiTestCase { when(mBindStage.getStageParams(lastChild)).thenReturn(bindParams); // WHEN the controller gets the list - mDynamicChildBindController.updateChildContentViews(mGroupNotifs); + mDynamicChildBindController.updateContentViews(mGroupNotifs); // THEN we bind content views verify(bindParams).requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED); -- GitLab From 08225a93fcb22c384e201d868ac5d1a55c0beca6 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 14 Apr 2020 18:18:51 -0700 Subject: [PATCH 004/166] Pass the right loading package when registering dex modules We computed the right value, but did not update the registration arguments. Test: manual, DexManagerTests Bug: 148774920 Change-Id: I98cef1d0a9b5431c181db3a9181a5a0958fe9b6a --- services/core/java/com/android/server/pm/dex/DexManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 95a81f099e2d..3074250950f7 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -711,7 +711,7 @@ public class DexManager { for (String isa : getAppDexInstructionSets(info.primaryCpuAbi, info.secondaryCpuAbi)) { boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, userId, isa, /*primaryOrSplit*/ false, - searchResult.mOwningPackageName, + loadingPackage, PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT); update |= newUpdate; } -- GitLab From 6f4e063edbaacfeb56b882644b7ca1a427f61c09 Mon Sep 17 00:00:00 2001 From: Tony Mak Date: Thu, 23 Apr 2020 22:27:37 +0100 Subject: [PATCH 005/166] Use TextClassicationSession to call smart selection APIs. We used to use TextClassicationSession to send us logging only (i.e. onSelectionEvent()). Now we use TCSession to call smart selection APIs (i.e. suggestSelection and classifyText). This allows the TCS to obtain the session ID in onSuggestSelection and onClassifyText. BUG: 149077320 Test: atest TextViewActivityTest Test: Try a few smart selections and log the sessionID of each TC APIs. Change-Id: I320249735aa08fb7e8612060955b2aa5496da94b --- .../widget/SelectionActionModeHelper.java | 4 +- core/java/android/widget/TextView.java | 3 +- .../android/widget/TextViewActivityTest.java | 59 ++++++++++++++++++- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 45943f512c22..e57b78917308 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -90,7 +90,7 @@ public final class SelectionActionModeHelper { mTextView = mEditor.getTextView(); mTextClassificationHelper = new TextClassificationHelper( mTextView.getContext(), - mTextView::getTextClassifier, + mTextView::getTextClassificationSession, getText(mTextView), 0, 1, mTextView.getTextLocales()); mSelectionTracker = new SelectionTracker(mTextView); @@ -465,7 +465,7 @@ public final class SelectionActionModeHelper { selectionEnd = mTextView.getSelectionEnd(); } mTextClassificationHelper.init( - mTextView::getTextClassifier, + mTextView::getTextClassificationSession, getText(mTextView), selectionStart, selectionEnd, mTextView.getTextLocales()); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 4be9e1a2051b..f08696916a57 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -12624,7 +12624,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return getTextClassifier() == TextClassifier.NO_OP; } - /** * Starts an ActionMode for the specified TextLinkSpan. * @@ -12668,7 +12667,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setDefaultLocales(getTextLocales()) .build(); final Supplier supplier = () -> - getTextClassifier().classifyText(request); + getTextClassificationSession().classifyText(request); final Consumer consumer = classification -> { if (classification != null) { if (!classification.getActions().isEmpty()) { diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 45d4b38d82aa..7cde19c30dd4 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -120,8 +120,10 @@ public class TextViewActivityTest { public void setUp() { mActivity = mActivityRule.getActivity(); mInstrumentation = InstrumentationRegistry.getInstrumentation(); - mActivity.getSystemService(TextClassificationManager.class) - .setTextClassifier(TextClassifier.NO_OP); + TextClassificationManager tcm = mActivity.getSystemService( + TextClassificationManager.class); + tcm.setTextClassifier(TextClassifier.NO_OP); + tcm.setTextClassificationSessionFactory(null); } @Test @@ -1173,6 +1175,53 @@ public class TextViewActivityTest { assertEquals(TextClassifier.TYPE_PHONE, lastEvent.getEntityType()); } + @Test + public void testTextClassifierSession() throws Throwable { + useSystemDefaultTextClassifier(); + TextClassificationManager tcm = + mActivity.getSystemService(TextClassificationManager.class); + List testableTextClassifiers = new ArrayList<>(); + tcm.setTextClassificationSessionFactory(classificationContext -> { + TestableTextClassifier textClassifier = new TestableTextClassifier(); + testableTextClassifiers.add(textClassifier); + return new TextClassifier() { + private boolean mIsDestroyed = false; + + @Override + public TextSelection suggestSelection(TextSelection.Request request) { + return textClassifier.suggestSelection(request); + } + + @Override + public void destroy() { + mIsDestroyed = true; + } + + @Override + public boolean isDestroyed() { + return mIsDestroyed; + } + }; + }); + + // Long press to trigger selection + onView(withId(R.id.textview)).perform(replaceText("android.com")); + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(0)); + sleepForFloatingToolbarPopup(); + // Click "Copy" to dismiss the selection. + clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy)); + + // Long press to trigger another selection + onView(withId(R.id.textview)).perform(replaceText("android@android.com")); + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(0)); + sleepForFloatingToolbarPopup(); + + // suggestSelection should be called in two different TextClassifier sessions. + assertEquals(2, testableTextClassifiers.size()); + assertEquals(1, testableTextClassifiers.get(0).getTextSelectionRequests().size()); + assertEquals(1, testableTextClassifiers.get(1).getTextSelectionRequests().size()); + } + @Test public void testPastePlainText_menuAction() { initializeClipboardWithText(TextStyle.STYLED); @@ -1227,6 +1276,7 @@ public class TextViewActivityTest { private final class TestableTextClassifier implements TextClassifier { final List mSelectionEvents = new ArrayList<>(); + final List mTextSelectionRequests = new ArrayList<>(); @Override public void onSelectionEvent(SelectionEvent event) { @@ -1235,6 +1285,7 @@ public class TextViewActivityTest { @Override public TextSelection suggestSelection(TextSelection.Request request) { + mTextSelectionRequests.add(request); return new TextSelection.Builder(request.getStartIndex(), request.getEndIndex()) .setEntityType(TextClassifier.TYPE_PHONE, 1) .build(); @@ -1243,5 +1294,9 @@ public class TextViewActivityTest { List getSelectionEvents() { return mSelectionEvents; } + + List getTextSelectionRequests() { + return mTextSelectionRequests; + } } } -- GitLab From 50038ee313b5f499b582ddf6261e2656cc773ce9 Mon Sep 17 00:00:00 2001 From: Ben Lin Date: Fri, 24 Apr 2020 17:09:12 -0700 Subject: [PATCH 006/166] Don't check parent's persistTaskBounds unless child is UNDEFINED. When Task gets an onConfigurationChanged call with a requested overriding configuration, it checks for itself and its parent's ability to see if persisting task bounds is possible or not. This was done for the case of freeform window support, where if the current window is undefined (persistBounds = false), it will then check for the parents (could be clamhshell, which persistBounds = true, or tablet, which persistBounds = false). However, there exists a case where this does not work well, which is when freeform window (inheriting from a freeform display) enters PIP, pinned windowing mode. We have already provided new PIP bounds, but since undefined window are not persistable and neither is PIP, but PIP's parent (the freeform display) is, it will end up setting the new bounds back to mLastNonFullscreenBounds, which is the freeform bounds. We then end up losing the new PIP bounds. Bug: 152911516 Test: On a freeform display, open an activity and enter PIP - PIP activity now has the correct bounds Change-Id: If73523ba1c54274eb5be4af5b967c6fef2993898 --- services/core/java/com/android/server/wm/Task.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 66e1b1758d85..160a649aa51a 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1930,9 +1930,11 @@ class Task extends WindowContainer { // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so // restore the last recorded non-fullscreen bounds. final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds(); - final boolean nextPersistTaskBounds = - getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds() - || newParentConfig.windowConfiguration.persistTaskBounds(); + boolean nextPersistTaskBounds = + getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds(); + if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED) { + nextPersistTaskBounds = newParentConfig.windowConfiguration.persistTaskBounds(); + } if (!prevPersistTaskBounds && nextPersistTaskBounds && mLastNonFullscreenBounds != null && !mLastNonFullscreenBounds.isEmpty()) { // Bypass onRequestedOverrideConfigurationChanged here to avoid infinite loop. -- GitLab From 2480d9cfa7579192c7af36a4b0ed615ccff260e0 Mon Sep 17 00:00:00 2001 From: Lyn Han Date: Sun, 26 Apr 2020 07:19:00 -0700 Subject: [PATCH 007/166] Show dots in overflow BadgedImageView#shouldDrawDot hides the icon if there are any suppression flags. Remove FLYOUT_VISIBLE flag for overflow icons, since we don't show flyouts for overflow bubbles. Fixes: 148878911 Test: manual: unopened bubbles show dots in overflow Test: manual: opened bubbles do not show dots in overflow Change-Id: I26147f75b479cb28eedd910135e8e5bc00ecef14 --- .../src/com/android/systemui/bubbles/BubbleOverflowActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index 37841f24a3cf..21fe282450ec 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -215,6 +215,7 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter { mBubbles.remove(b); notifyDataSetChanged(); -- GitLab From de2c5f135841e8f09e2a1056090801e10a58827d Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Fri, 10 Apr 2020 17:23:43 +0800 Subject: [PATCH 008/166] Compute visibilities when finishing activity with a translucent resume activity When there is a translucent activity in foreground and finishing a next intermediate activity. Since the visibility of next below activity was not updated. The black background shown below the translucent resume activity. The CL update the visibilities with above condition and add test case to verify. Bug: 153927250 Test: atest WmTests:ActivityRecordTests Change-Id: Ie5fc13872becb48b8bdcd572d710111534424a72 --- .../com/android/server/wm/ActivityRecord.java | 33 ++++++++++----- .../server/wm/ActivityRecordTests.java | 40 +++++++++++++++++++ 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 130da2dfe9c5..dcdbfdedb0c8 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2669,15 +2669,28 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return this; } - // Ensure activity visibilities and update lockscreen occluded/dismiss state when - // finishing the top activity that occluded keyguard. So that, the - // ActivityStack#mTopActivityOccludesKeyguard can be updated and the activity below won't - // be resumed. - if (isState(PAUSED) - && mStackSupervisor.getKeyguardController().isKeyguardLocked() - && getStack().topActivityOccludesKeyguard()) { - getDisplay().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */, false /* notifyClients */); + final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED); + if (isCurrentVisible) { + final ActivityStack stack = getStack(); + final ActivityRecord activity = stack.mResumedActivity; + boolean ensureVisibility = false; + if (activity != null && !activity.occludesParent()) { + // If the resume activity is not opaque, we need to make sure the visibilities of + // activities be updated, they may be seen by users. + ensureVisibility = true; + } else if (mStackSupervisor.getKeyguardController().isKeyguardLocked() + && stack.topActivityOccludesKeyguard()) { + // Ensure activity visibilities and update lockscreen occluded/dismiss state when + // finishing the top activity that occluded keyguard. So that, the + // ActivityStack#mTopActivityOccludesKeyguard can be updated and the activity below + // won't be resumed. + ensureVisibility = true; + } + + if (ensureVisibility) { + getDisplay().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + false /* preserveWindows */, true /* notifyClients */); + } } boolean activityRemoved = false; @@ -2698,7 +2711,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // than destroy immediately. final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.mVisibleRequested); - if ((mVisibleRequested || isState(PAUSED)) && isNextNotYetVisible) { + if (isCurrentVisible && isNextNotYetVisible) { // Add this activity to the list of stopping activities. It will be processed and // destroyed when the next activity reports idle. addToStopping(false /* scheduleIdle */, false /* idleDelayed */, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 6a28918eab00..364845c0ff4a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1082,6 +1082,46 @@ public class ActivityRecordTests extends ActivityTestsBase { assertFalse(mStack.topActivityOccludesKeyguard()); } + /** + * Verify that complete finish request for an activity which the resume activity is translucent + * must ensure the visibilities of activities being updated. + */ + @Test + public void testCompleteFinishing_ensureActivitiesVisible() { + final ActivityRecord firstActivity = new ActivityBuilder(mService).setTask(mTask).build(); + firstActivity.mVisibleRequested = false; + firstActivity.nowVisible = false; + firstActivity.setState(STOPPED, "true"); + + final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(mTask).build(); + secondActivity.mVisibleRequested = true; + secondActivity.nowVisible = true; + secondActivity.setState(PAUSED, "true"); + + final ActivityRecord translucentActivity = + new ActivityBuilder(mService).setTask(mTask).build(); + translucentActivity.mVisibleRequested = true; + translucentActivity.nowVisible = true; + translucentActivity.setState(RESUMED, "true"); + + doReturn(false).when(translucentActivity).occludesParent(); + + // Finish the second activity + secondActivity.finishing = true; + secondActivity.completeFinishing("test"); + verify(secondActivity.getDisplay()).ensureActivitiesVisible(null /* starting */, + 0 /* configChanges */ , false /* preserveWindows */, + true /* notifyClients */); + + // Finish the first activity + firstActivity.finishing = true; + firstActivity.mVisibleRequested = true; + firstActivity.completeFinishing("test"); + verify(firstActivity.getDisplay(), times(2)).ensureActivitiesVisible(null /* starting */, + 0 /* configChanges */ , false /* preserveWindows */, + true /* notifyClients */); + } + /** * Verify destroy activity request completes successfully. */ -- GitLab From 8e986e7c2cc89ec5393eee7bcf08f6a4fa53d561 Mon Sep 17 00:00:00 2001 From: Kelvin Kwan Date: Fri, 17 Apr 2020 14:54:32 +0100 Subject: [PATCH 009/166] Add metrics for DocsUI events Bug: 152609549 Test: manual Change-Id: If62eed6b9e3bbe9e40e6793c81041a30986bb8db --- .../android/stats/devicepolicy/device_policy_enums.proto | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 896ee4fa42a5..7c1a04944d68 100644 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto @@ -197,4 +197,8 @@ enum EventId { CROSS_PROFILE_SETTINGS_PAGE_USER_CONSENTED = 170; CROSS_PROFILE_SETTINGS_PAGE_USER_DECLINED_CONSENT = 171; CROSS_PROFILE_SETTINGS_PAGE_PERMISSION_REVOKED = 172; + DOCSUI_EMPTY_STATE_NO_PERMISSION = 173; + DOCSUI_EMPTY_STATE_QUIET_MODE = 174; + DOCSUI_LAUNCH_OTHER_APP = 175; + DOCSUI_PICK_RESULT = 176; } -- GitLab From f29759cd321bca4797a8f1352838fe4924d9f569 Mon Sep 17 00:00:00 2001 From: Marvin Ramin Date: Tue, 21 Apr 2020 15:30:15 +0200 Subject: [PATCH 010/166] Fix HdmiCecLocalDevicePlaybackTest Playback device implementation was unable to get an instance of PowerManager to aquire the WakeLock that is acquired when the device is active source. Bug: 151147315 Test: atest HdmiCecLocalDevicePlaybackTest Merged-In: I44a11d8d6365e32332818b08c84583c9afb5aa15 Change-Id: I44a11d8d6365e32332818b08c84583c9afb5aa15 --- .../hdmi/HdmiCecLocalDevicePlaybackTest.java | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index b8394e3c1643..b76211895ab0 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -20,7 +20,12 @@ import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC import static com.google.common.truth.Truth.assertThat; +import android.content.Context; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.IThermalService; import android.os.Looper; +import android.os.PowerManager; import android.os.test.TestLooper; import android.view.KeyEvent; @@ -32,6 +37,8 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.ArrayList; @@ -50,8 +57,17 @@ public class HdmiCecLocalDevicePlaybackTest { private int mPlaybackPhysicalAddress; private boolean mWokenUp; + @Mock private IPowerManager mIPowerManagerMock; + @Mock private IThermalService mIThermalServiceMock; + @Before public void setUp() { + MockitoAnnotations.initMocks(this); + + Context context = InstrumentationRegistry.getTargetContext(); + mMyLooper = mTestLooper.getLooper(); + PowerManager powerManager = new PowerManager(context, mIPowerManagerMock, + mIThermalServiceMock, new Handler(mMyLooper)); mHdmiControlService = new HdmiControlService(InstrumentationRegistry.getTargetContext()) { @Override @@ -68,9 +84,13 @@ public class HdmiCecLocalDevicePlaybackTest { void writeStringSystemProperty(String key, String value) { // do nothing } + + @Override + PowerManager getPowerManager() { + return powerManager; + } }; - mMyLooper = mTestLooper.getLooper(); mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService); mHdmiCecLocalDevicePlayback.init(); mHdmiControlService.setIoLooper(mMyLooper); @@ -140,7 +160,6 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - @Ignore("b/151147315") public void doNotWakeUpOnHotPlug_PlugIn() { mWokenUp = false; mHdmiCecLocalDevicePlayback.onHotplug(0, true); @@ -148,7 +167,6 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - @Ignore("b/151147315") public void doNotWakeUpOnHotPlug_PlugOut() { mWokenUp = false; mHdmiCecLocalDevicePlayback.onHotplug(0, false); @@ -156,7 +174,6 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - @Ignore("b/151147315") public void sendVolumeKeyEvent_up_volumeEnabled() { mHdmiControlService.setHdmiCecVolumeControlEnabled(true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); @@ -166,7 +183,6 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - @Ignore("b/151147315") public void sendVolumeKeyEvent_down_volumeEnabled() { mHdmiControlService.setHdmiCecVolumeControlEnabled(true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true); @@ -176,7 +192,6 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - @Ignore("b/151147315") public void sendVolumeKeyEvent_mute_volumeEnabled() { mHdmiControlService.setHdmiCecVolumeControlEnabled(true); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true); @@ -186,7 +201,6 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - @Ignore("b/151147315") public void sendVolumeKeyEvent_up_volumeDisabled() { mHdmiControlService.setHdmiCecVolumeControlEnabled(false); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); @@ -196,7 +210,6 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - @Ignore("b/151147315") public void sendVolumeKeyEvent_down_volumeDisabled() { mHdmiControlService.setHdmiCecVolumeControlEnabled(false); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true); @@ -206,7 +219,6 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - @Ignore("b/151147315") public void sendVolumeKeyEvent_mute_volumeDisabled() { mHdmiControlService.setHdmiCecVolumeControlEnabled(false); mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true); -- GitLab From 7d8b7a80080df1f2d021bd3246f12d9e8344dcb9 Mon Sep 17 00:00:00 2001 From: Fiona Campbell Date: Mon, 27 Apr 2020 12:51:31 +0100 Subject: [PATCH 011/166] Add lights HAL to watchdog Bug: b/154928509 Test: none Change-Id: I95be77955e6ebefa0e5e545e9b67007de216e28d --- services/core/java/com/android/server/Watchdog.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 58972a5346c7..425a0452b6b8 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -117,6 +117,7 @@ public class Watchdog extends Thread { "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", "android.hardware.health@2.0::IHealth", + "android.hardware.light@2.0::ILight", "android.hardware.media.c2@1.0::IComponentStore", "android.hardware.media.omx@1.0::IOmx", "android.hardware.media.omx@1.0::IOmxStore", -- GitLab From 806a185ba54de47908626ba04d86d3b370cec7af Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Mon, 27 Apr 2020 11:31:56 +0200 Subject: [PATCH 012/166] Add buildExternalStorageAndroidObbDirs(). For use by DownloadManager. Bug: 153504419 Test: atest CtsDownloadManagerInstaller Change-Id: Ifd45ad779087fb5ba3d885d8e648bc24bc8656eb --- core/java/android/os/Environment.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 5f8c4f5cdf27..eaadabddc8fa 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -970,6 +970,15 @@ public class Environment { return sCurrentUser.buildExternalStorageAndroidDataDirs(); } + /** + * Returns the path for android-specific OBB data on the SD card. + * @hide + */ + public static File[] buildExternalStorageAndroidObbDirs() { + throwIfUserRequired(); + return sCurrentUser.buildExternalStorageAndroidObbDirs(); + } + /** * Generates the raw path to an application's data * @hide -- GitLab From c47e9bfa281fc6c856c40f42ba8f99fa0c02733d Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Mon, 27 Apr 2020 15:13:38 +0800 Subject: [PATCH 013/166] Replace the switch item with the text for multiple shortcut menu. Bug: 155036209 Test: manual test Change-Id: I09bf88dc36244aeb2a37ab6390cb3e2b46358dfb --- .../accessibility/dialog/AccessibilityTarget.java | 2 +- .../accessibility/dialog/ShortcutTargetAdapter.java | 4 ++-- .../internal/accessibility/dialog/TargetAdapter.java | 3 +-- .../dialog/ToggleAccessibilityServiceTarget.java | 9 +++++++-- .../dialog/ToggleWhiteListingFeatureTarget.java | 9 +++++++-- .../layout/accessibility_shortcut_chooser_item.xml | 11 ++++++----- core/res/res/values/symbols.xml | 4 +++- 7 files changed, 27 insertions(+), 15 deletions(-) diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java index 72ebc58380b8..37871d0b5a10 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java @@ -74,7 +74,7 @@ abstract class AccessibilityTarget implements TargetOperations, OnTargetSelected holder.mCheckBoxView.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE); holder.mIconView.setImageDrawable(getIcon()); holder.mLabelView.setText(getLabel()); - holder.mSwitchItem.setVisibility(View.GONE); + holder.mStatusView.setVisibility(View.GONE); } @Override diff --git a/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java b/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java index b7605b7fc011..88e18db8da08 100644 --- a/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java +++ b/core/java/com/android/internal/accessibility/dialog/ShortcutTargetAdapter.java @@ -68,8 +68,8 @@ class ShortcutTargetAdapter extends TargetAdapter { holder.mIconView = convertView.findViewById(R.id.accessibility_shortcut_target_icon); holder.mLabelView = convertView.findViewById( R.id.accessibility_shortcut_target_label); - holder.mSwitchItem = convertView.findViewById( - R.id.accessibility_shortcut_target_switch_item); + holder.mStatusView = convertView.findViewById( + R.id.accessibility_shortcut_target_status); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); diff --git a/core/java/com/android/internal/accessibility/dialog/TargetAdapter.java b/core/java/com/android/internal/accessibility/dialog/TargetAdapter.java index 1efa17e520ae..28ac9210ac0d 100644 --- a/core/java/com/android/internal/accessibility/dialog/TargetAdapter.java +++ b/core/java/com/android/internal/accessibility/dialog/TargetAdapter.java @@ -19,7 +19,6 @@ package com.android.internal.accessibility.dialog; import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.ImageView; -import android.widget.Switch; import android.widget.TextView; /** @@ -30,6 +29,6 @@ abstract class TargetAdapter extends BaseAdapter { CheckBox mCheckBoxView; ImageView mIconView; TextView mLabelView; - Switch mSwitchItem; + TextView mStatusView; } } diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java index 3a42f7e0edb8..239e531dbfb8 100644 --- a/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java @@ -24,6 +24,7 @@ import android.content.Context; import android.view.View; import android.view.accessibility.AccessibilityManager.ShortcutType; +import com.android.internal.R; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder; @@ -49,7 +50,11 @@ class ToggleAccessibilityServiceTarget extends AccessibilityServiceTarget { final boolean isEditMenuMode = shortcutMenuMode == ShortcutMenuMode.EDIT; - holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE); - holder.mSwitchItem.setChecked(isAccessibilityServiceEnabled(getContext(), getId())); + holder.mStatusView.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE); + + final int statusResId = isAccessibilityServiceEnabled(getContext(), getId()) + ? R.string.accessibility_shortcut_menu_item_status_on + : R.string.accessibility_shortcut_menu_item_status_off; + holder.mStatusView.setText(getContext().getString(statusResId)); } } diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java index fcbf5eccbd9e..5ab9eb84f0e4 100644 --- a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java @@ -23,6 +23,7 @@ import android.provider.Settings; import android.view.View; import android.view.accessibility.AccessibilityManager.ShortcutType; +import com.android.internal.R; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder; @@ -46,8 +47,12 @@ class ToggleWhiteListingFeatureTarget extends AccessibilityTarget { final boolean isEditMenuMode = shortcutMenuMode == ShortcutMenuMode.EDIT; - holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE); - holder.mSwitchItem.setChecked(isFeatureEnabled()); + holder.mStatusView.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE); + + final int statusResId = isFeatureEnabled() + ? R.string.accessibility_shortcut_menu_item_status_on + : R.string.accessibility_shortcut_menu_item_status_off; + holder.mStatusView.setText(getContext().getString(statusResId)); } private boolean isFeatureEnabled() { diff --git a/core/res/res/layout/accessibility_shortcut_chooser_item.xml b/core/res/res/layout/accessibility_shortcut_chooser_item.xml index fff22d916b15..7cca1292af68 100644 --- a/core/res/res/layout/accessibility_shortcut_chooser_item.xml +++ b/core/res/res/layout/accessibility_shortcut_chooser_item.xml @@ -49,12 +49,13 @@ android:textColor="?attr/textColorPrimary" android:fontFamily="sans-serif-medium"/> - + android:textSize="20sp" + android:textColor="?attr/colorAccent" + android:fontFamily="sans-serif-medium"/> + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 431549713f44..e66f0a3a546a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3238,7 +3238,9 @@ - + + + -- GitLab From cefe39ffca3a52f5e008acc6cf54b546a2f5e4ba Mon Sep 17 00:00:00 2001 From: Mohammad Samiul Islam Date: Sun, 22 Mar 2020 21:32:33 +0000 Subject: [PATCH 014/166] Fail staged install if any apk-in-apex fails to install Bug: 152021141 Test: atest StagedInstallTest#testApexFailsToInstallIfApkInApexFailsToScan Test: atest ApexManagerTest#testReportErrorWithApkInApex Change-Id: I9ccc65e4b057a651dae796d5cd708a303fbd0372 --- .../com/android/server/pm/ApexManager.java | 61 +++++++++++- .../server/pm/PackageManagerService.java | 4 + .../com/android/server/pm/StagingManager.java | 93 +++++++++++++------ .../android/server/pm/ApexManagerTest.java | 16 ++++ 4 files changed, 141 insertions(+), 33 deletions(-) diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 1c41c2eb4702..4872b66ff1b4 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -60,7 +60,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; @@ -289,6 +288,21 @@ public abstract class ApexManager { */ abstract void registerApkInApex(AndroidPackage pkg); + /** + * Reports error raised during installation of apk-in-apex. + * + * @param scanDir the directory of the apex inside which apk-in-apex resides. + */ + abstract void reportErrorWithApkInApex(String scanDirPath); + + /** + * Returns true if there were no errors when installing apk-in-apex inside + * {@param apexPackageName}, otherwise false. + * + * @param apexPackageName Package name of the apk container of apex + */ + abstract boolean isApkInApexInstallSuccess(String apexPackageName); + /** * Returns list of {@code packageName} of apks inside the given apex. * @param apexPackageName Package name of the apk container of apex @@ -368,6 +382,13 @@ public abstract class ApexManager { @GuardedBy("mLock") private ArrayMap> mApksInApex = new ArrayMap<>(); + /** + * Contains the list of {@code Exception}s that were raised when installing apk-in-apex + * inside {@code apexModuleName}. + */ + @GuardedBy("mLock") + private Set mErrorWithApkInApex = new ArraySet<>(); + @GuardedBy("mLock") private List mAllPackagesCache; @@ -733,9 +754,7 @@ public abstract class ApexManager { @Override void registerApkInApex(AndroidPackage pkg) { synchronized (mLock) { - final Iterator it = mActiveApexInfosCache.iterator(); - while (it.hasNext()) { - final ActiveApexInfo aai = it.next(); + for (ActiveApexInfo aai : mActiveApexInfosCache) { if (pkg.getBaseCodePath().startsWith(aai.apexDirectory.getAbsolutePath())) { List apks = mApksInApex.get(aai.apexModuleName); if (apks == null) { @@ -748,6 +767,30 @@ public abstract class ApexManager { } } + @Override + void reportErrorWithApkInApex(String scanDirPath) { + synchronized (mLock) { + for (ActiveApexInfo aai : mActiveApexInfosCache) { + if (scanDirPath.startsWith(aai.apexDirectory.getAbsolutePath())) { + mErrorWithApkInApex.add(aai.apexModuleName); + } + } + } + } + + @Override + boolean isApkInApexInstallSuccess(String apexPackageName) { + synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); + String moduleName = mPackageNameToApexModuleName.get(apexPackageName); + if (moduleName == null) { + return false; + } + return !mErrorWithApkInApex.contains(moduleName); + } + } + @Override List getApksInApex(String apexPackageName) { synchronized (mLock) { @@ -1039,6 +1082,16 @@ public abstract class ApexManager { // No-op } + @Override + void reportErrorWithApkInApex(String scanDirPath) { + // No-op + } + + @Override + boolean isApkInApexInstallSuccess(String apexPackageName) { + return true; + } + @Override List getApksInApex(String apexPackageName) { return Collections.emptyList(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8b191dda5590..8e0fabf4c8f7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8972,6 +8972,10 @@ public class PackageManagerService extends IPackageManager.Stub + parseResult.scanFile, throwable); } + if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) { + mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath()); + } + // Delete invalid userdata apps if ((scanFlags & SCAN_AS_SYSTEM) == 0 && errorCode != PackageManager.INSTALL_SUCCEEDED) { diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 1c1e64d70bbd..9a297d601a6b 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -370,24 +370,9 @@ public class StagingManager { } /** - * Perform snapshot and restore as required both for APEXes themselves and for apks in APEX. - * Apks inside apex are not installed using apk-install flow. They are scanned from the system - * directory directly by PackageManager, as such, RollbackManager need to handle their data - * separately here. + * Utility function for extracting apex sessions out of multi-package/single session. */ - private void snapshotAndRestoreForApexSession(PackageInstallerSession session) { - if (!sessionContainsApex(session)) { - return; - } - - boolean doSnapshotOrRestore = - (session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0 - || session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK; - if (!doSnapshotOrRestore) { - return; - } - - // Find all the apex sessions that needs processing + private List extractApexSessions(PackageInstallerSession session) { List apexSessions = new ArrayList<>(); if (session.isMultiPackage()) { List childrenSessions = new ArrayList<>(); @@ -408,6 +393,50 @@ public class StagingManager { } else { apexSessions.add(session); } + return apexSessions; + } + + /** + * Checks if all apk-in-apex were installed without errors for all of the apex sessions. Throws + * error for any apk-in-apex failed to install. + * + * @throws PackageManagerException if any apk-in-apex failed to install + */ + private void checkInstallationOfApkInApexSuccessful(PackageInstallerSession session) + throws PackageManagerException { + final List apexSessions = extractApexSessions(session); + if (apexSessions.isEmpty()) { + return; + } + + for (PackageInstallerSession apexSession : apexSessions) { + String packageName = apexSession.getPackageName(); + if (!mApexManager.isApkInApexInstallSuccess(packageName)) { + throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + "Failed to install apk-in-apex of " + packageName); + } + } + } + + /** + * Perform snapshot and restore as required both for APEXes themselves and for apks in APEX. + * Apks inside apex are not installed using apk-install flow. They are scanned from the system + * directory directly by PackageManager, as such, RollbackManager need to handle their data + * separately here. + */ + private void snapshotAndRestoreForApexSession(PackageInstallerSession session) { + boolean doSnapshotOrRestore = + (session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0 + || session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK; + if (!doSnapshotOrRestore) { + return; + } + + // Find all the apex sessions that needs processing + final List apexSessions = extractApexSessions(session); + if (apexSessions.isEmpty()) { + return; + } final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class); final int[] allUsers = um.getUserIds(); @@ -545,18 +574,19 @@ public class StagingManager { return; } + // Check if apex packages in the session failed to activate if (hasApex) { if (apexSessionInfo == null) { - String errorMsg = "apexd did not know anything about a staged session supposed to" - + " be activated"; + final String errorMsg = "apexd did not know anything about a staged session " + + "supposed to be activated"; session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); abortCheckpoint(errorMsg); return; } if (isApexSessionFailed(apexSessionInfo)) { - String errorMsg = "APEX activation failed. Check logcat messages from apexd for " - + "more information."; + String errorMsg = "APEX activation failed. Check logcat messages from apexd " + + "for more information."; if (!TextUtils.isEmpty(mNativeFailureReason)) { errorMsg = "Session reverted due to crashing native process: " + mNativeFailureReason; @@ -567,21 +597,26 @@ public class StagingManager { return; } if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) { - // Apexd did not apply the session for some unknown reason. There is no guarantee - // that apexd will install it next time. Safer to proactively mark as failed. - String errorMsg = "Staged session " + session.sessionId + "at boot didn't " - + "activate nor fail. Marking it as failed anyway."; + // Apexd did not apply the session for some unknown reason. There is no + // guarantee that apexd will install it next time. Safer to proactively mark + // it as failed. + final String errorMsg = "Staged session " + session.sessionId + "at boot " + + "didn't activate nor fail. Marking it as failed anyway."; session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); abortCheckpoint(errorMsg); return; } - snapshotAndRestoreForApexSession(session); - Slog.i(TAG, "APEX packages in session " + session.sessionId - + " were successfully activated. Proceeding with APK packages, if any"); } - // The APEX part of the session is activated, proceed with the installation of APKs. + // Handle apk and apk-in-apex installation try { + if (hasApex) { + checkInstallationOfApkInApexSuccessful(session); + snapshotAndRestoreForApexSession(session); + Slog.i(TAG, "APEX packages in session " + session.sessionId + + " were successfully activated. Proceeding with APK packages, if any"); + } + // The APEX part of the session is activated, proceed with the installation of APKs. Slog.d(TAG, "Installing APK packages in session " + session.sessionId); installApksInSession(session); } catch (PackageManagerException e) { diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 3718c5c5a5e1..f5fe20acff2c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -273,6 +273,21 @@ public class ApexManagerTest { assertThat(mApexManager.uninstallApex(TEST_APEX_PKG)).isFalse(); } + @Test + public void testReportErrorWithApkInApex() throws RemoteException { + when(mApexService.getActivePackages()).thenReturn(createApexInfo(true, true)); + final ApexManager.ActiveApexInfo activeApex = mApexManager.getActiveApexInfos().get(0); + assertThat(activeApex.apexModuleName).isEqualTo(TEST_APEX_PKG); + + when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); + + assertThat(mApexManager.isApkInApexInstallSuccess(activeApex.apexModuleName)).isTrue(); + mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath()); + assertThat(mApexManager.isApkInApexInstallSuccess(activeApex.apexModuleName)).isFalse(); + } + private ApexInfo[] createApexInfo(boolean isActive, boolean isFactory) { File apexFile = copyRawResourceToFile(TEST_APEX_PKG, R.raw.apex_test); ApexInfo apexInfo = new ApexInfo(); @@ -281,6 +296,7 @@ public class ApexManagerTest { apexInfo.moduleName = TEST_APEX_PKG; apexInfo.modulePath = apexFile.getPath(); apexInfo.versionCode = 191000070; + apexInfo.preinstalledModulePath = apexFile.getPath(); return new ApexInfo[]{apexInfo}; } -- GitLab From da40cb86810f71a31fe7ac4373b455521bed6026 Mon Sep 17 00:00:00 2001 From: Ruslan Tkhakokhov Date: Wed, 22 Apr 2020 16:21:47 +0100 Subject: [PATCH 015/166] Special-case backup/restore of replaced settings Bug: 153940088 Test: atest SettingsProviderTest:SettingsHelperTest Values for some settings might be changed temporarily. If a backup happens at that moment, we want to backup the real values instead of the temporary ones. If a restore happens at that moment, we don't want to restore values for modified settings. See https://b.corp.google.com/issues/153940088#comment2 for context. Change-Id: I4866f56376ffa393220bbef828a4b876d586146b --- packages/SettingsProvider/Android.bp | 1 + .../providers/settings/SettingsHelper.java | 33 +++++++++- .../settings/SettingsHelperTest.java | 61 +++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index d67bd8da844f..9d042a4e15f6 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -34,6 +34,7 @@ android_test { ], static_libs: [ "androidx.test.rules", + "mockito-target-minus-junit4", "SettingsLibDisplayDensityUtils", "platform-test-annotations", "truth-prebuilt", diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index b6e31d26a088..d023d98f2b73 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -48,6 +48,8 @@ import java.util.Locale; public class SettingsHelper { private static final String TAG = "SettingsHelper"; private static final String SILENT_RINGTONE = "_silent"; + private static final String SETTINGS_REPLACED_KEY = "backup_skip_user_facing_data"; + private static final String SETTING_ORIGINAL_KEY_SUFFIX = "_original"; private static final float FLOAT_TOLERANCE = 0.01f; private Context mContext; @@ -121,6 +123,10 @@ public class SettingsHelper { */ public void restoreValue(Context context, ContentResolver cr, ContentValues contentValues, Uri destination, String name, String value, int restoredFromSdkInt) { + if (isReplacedSystemSetting(name)) { + return; + } + // Will we need a post-restore broadcast for this element? String oldValue = null; boolean sendBroadcast = false; @@ -203,7 +209,32 @@ public class SettingsHelper { } } // Return the original value - return value; + return isReplacedSystemSetting(name) ? getRealValueForSystemSetting(name) : value; + } + + /** + * The setting value might have been replaced temporarily. If that's the case, return the real + * value instead of the temporary one. + */ + @VisibleForTesting + public String getRealValueForSystemSetting(String setting) { + return Settings.System.getString(mContext.getContentResolver(), + setting + SETTING_ORIGINAL_KEY_SUFFIX); + } + + @VisibleForTesting + public boolean isReplacedSystemSetting(String setting) { + // This list should not be modified. + if (!Settings.System.MASTER_MONO.equals(setting) + && !Settings.System.SCREEN_OFF_TIMEOUT.equals(setting)) { + return false; + } + // If this flag is set, values for the system settings from the list above have been + // temporarily replaced. We don't want to back up the temporary value or run restore for + // such settings. + // TODO(154822946): Remove this logic in the next release. + return Settings.Secure.getInt(mContext.getContentResolver(), SETTINGS_REPLACED_KEY, + /* def */ 0) != 0; } /** diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java index d112facc2b0e..7baa2266756f 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java @@ -18,18 +18,79 @@ package com.android.providers.settings; import static junit.framework.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.media.AudioManager; +import android.net.Uri; import android.os.LocaleList; +import android.telephony.TelephonyManager; import androidx.test.runner.AndroidJUnit4; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; /** * Tests for the SettingsHelperTest */ @RunWith(AndroidJUnit4.class) public class SettingsHelperTest { + private static final String SETTING_KEY = "setting_key"; + private static final String SETTING_VALUE = "setting_value"; + private static final String SETTING_REAL_VALUE = "setting_real_value"; + + private SettingsHelper mSettingsHelper; + + @Mock private Context mContext; + @Mock private ContentResolver mContentResolver; + @Mock private AudioManager mAudioManager; + @Mock private TelephonyManager mTelephonyManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManager); + when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))).thenReturn( + mTelephonyManager); + + mSettingsHelper = spy(new SettingsHelper(mContext)); + } + + @Test + public void testOnBackupValue_settingReplaced_returnsRealValue() { + when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(true); + doReturn(SETTING_REAL_VALUE).when(mSettingsHelper).getRealValueForSystemSetting( + eq(SETTING_KEY)); + + assertEquals(SETTING_REAL_VALUE, mSettingsHelper.onBackupValue(SETTING_KEY, SETTING_VALUE)); + } + + @Test + public void testGetRealValue_settingNotReplaced_returnsSameValue() { + when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(false); + + assertEquals(SETTING_VALUE, mSettingsHelper.onBackupValue(SETTING_KEY, SETTING_VALUE)); + } + + @Test + public void testRestoreValue_settingReplaced_doesNotRestore() { + when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(true); + mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY, + SETTING_KEY, SETTING_VALUE, /* restoredFromSdkInt */ 0); + + verifyZeroInteractions(mContentResolver); + } + @Test public void testResolveLocales() throws Exception { // Empty string from backup server -- GitLab From ae8e0aea36f751a6876c9401a031a973822604e0 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Wed, 22 Apr 2020 16:24:34 -0700 Subject: [PATCH 016/166] BLAST: Avoid JNI call on creation JNI calls aren't free and we don't need to burn one to call update with the values we just passed in. Bug: 152501005 Test: Existing tests pass Change-Id: Iaf272edb03d4ec195b75e9ef7e6c122604025dcb --- core/java/android/view/ViewRootImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8b1e6cbdb6ff..6f85072a0472 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1793,8 +1793,9 @@ public final class ViewRootImpl implements ViewParent, // We only return the Surface the first time, as otherwise // it hasn't changed and there is no need to update. ret = mBlastBufferQueue.getSurface(); + } else { + mBlastBufferQueue.update(mBlastSurfaceControl, width, height); } - mBlastBufferQueue.update(mBlastSurfaceControl, width, height); return ret; } -- GitLab From 3aef4fe42407800eb83cdad54d2d5cc609ce4cf5 Mon Sep 17 00:00:00 2001 From: shubang Date: Wed, 1 Apr 2020 16:51:04 -0700 Subject: [PATCH 017/166] Tuner JNI: linearBlock and OnDestroyNotify referred to ag/10877916 by Henry quxiangfang@ Bug: 139308734 Test: mmm Change-Id: I7574d27cb1f582a5556e22eac4fff87335230b00 --- .../media/tv/tuner/filter/MediaEvent.java | 27 ++- media/jni/android_media_tv_Tuner.cpp | 179 ++++++++++++++++-- media/jni/android_media_tv_Tuner.h | 61 ++++-- 3 files changed, 230 insertions(+), 37 deletions(-) diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java index 247c1c47560a..af63070027a2 100644 --- a/media/java/android/media/tv/tuner/filter/MediaEvent.java +++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java @@ -28,14 +28,19 @@ import android.media.MediaCodec.LinearBlock; */ @SystemApi public class MediaEvent extends FilterEvent { - private native int nativeGetAudioHandle(); + private long mNativeContext; + private final Object mLock = new Object(); + + private native Long nativeGetAudioHandle(); + private native LinearBlock nativeGetLinearBlock(); + private native void nativeFinalize(); private final int mStreamId; private final boolean mIsPtsPresent; private final long mPts; private final long mDataLength; private final long mOffset; - private final LinearBlock mLinearBlock; + private LinearBlock mLinearBlock; private final boolean mIsSecureMemory; private final long mDataId; private final int mMpuSequenceNumber; @@ -103,7 +108,12 @@ public class MediaEvent extends FilterEvent { */ @Nullable public LinearBlock getLinearBlock() { - return mLinearBlock; + synchronized (mLock) { + if (mLinearBlock == null) { + mLinearBlock = nativeGetLinearBlock(); + } + return mLinearBlock; + } } /** @@ -163,4 +173,15 @@ public class MediaEvent extends FilterEvent { public AudioDescriptor getExtraMetaData() { return mExtraMetaData; } + + + /** + * Finalize the MediaEvent object. + * @hide + */ + @Override + protected void finalize() { + nativeFinalize(); + mNativeContext = 0; + } } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index ab311c0e245a..805831442756 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -21,8 +21,7 @@ #include "android_media_tv_Tuner.h" #include "android_runtime/AndroidRuntime.h" -#include -#include +#include #include #include #include @@ -145,6 +144,7 @@ struct fields_t { jfieldID descramblerContext; jfieldID dvrRecorderContext; jfieldID dvrPlaybackContext; + jfieldID mediaEventContext; jmethodID frontendInitID; jmethodID filterInitID; jmethodID timeFilterInitID; @@ -169,6 +169,12 @@ static fields_t gFields; static int IP_V4_LENGTH = 4; static int IP_V6_LENGTH = 16; +void DestroyCallback(const C2Buffer * /* buf */, void *arg) { + android::sp event = (android::MediaEvent *)arg; + event->mAvHandleRefCnt--; + event->finalize(); +} + namespace android { /////////////// LnbCallback /////////////////////// LnbCallback::LnbCallback(jobject lnbObj, LnbId id) : mId(id) { @@ -280,17 +286,69 @@ MQ& Dvr::getDvrMQ() { return *mDvrMQ; } -/////////////// FilterCallback /////////////////////// -//TODO: implement filter callback -jobject FilterCallback::handleToLinearBlock(const native_handle_t* handle, uint32_t size) { - ALOGD("FilterCallback::handleToLinearBlock"); - C2HandleIon* ion = new C2HandleIon(handle->data[0], size); - std::shared_ptr block = _C2BlockFactory::CreateLinearBlock(ion); +/////////////// C2DataIdInfo /////////////////////// + +C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, index) { + CHECK(isGlobal()); + CHECK_EQ(C2Param::INFO, kind()); + DummyInfo info{value}; + memcpy(this + 1, static_cast(&info) + 1, kParamSize - sizeof(C2Param)); +} + +/////////////// MediaEvent /////////////////////// + +MediaEvent::MediaEvent(sp iFilter, hidl_handle avHandle, + uint64_t dataId, uint64_t dataLength, jobject obj) : mIFilter(iFilter), + mDataId(dataId), mDataLength(dataLength), mBuffer(nullptr), + mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + mMediaEventObj = env->NewWeakGlobalRef(obj); + mAvHandle = native_handle_clone(avHandle.getNativeHandle()); +} + +MediaEvent::~MediaEvent() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->DeleteWeakGlobalRef(mMediaEventObj); + mMediaEventObj = NULL; + native_handle_delete(mAvHandle); + if (mIonHandle != NULL) { + delete mIonHandle; + } + if (mC2Buffer != NULL) { + mC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this); + } +} + +void MediaEvent::finalize() { + if (mAvHandleRefCnt == 0) { + mIFilter->releaseAvHandle(hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0); + native_handle_close(mAvHandle); + } +} + +jobject MediaEvent::getLinearBlock() { + ALOGD("MediaEvent::getLinearBlock"); + if (mAvHandle == NULL) { + return NULL; + } + if (mLinearBlockObj != NULL) { + return mLinearBlockObj; + } + mIonHandle = new C2HandleIon(mAvHandle->data[0], mDataLength); + std::shared_ptr block = _C2BlockFactory::CreateLinearBlock(mIonHandle); JNIEnv *env = AndroidRuntime::getJNIEnv(); std::unique_ptr context{new JMediaCodecLinearBlock}; context->mBlock = block; - + mC2Buffer = context->toC2Buffer(0, mDataLength); + if (mAvHandle->numInts > 0) { + // use first int in the native_handle as the index + int index = mAvHandle->data[mAvHandle->numFds]; + std::shared_ptr c2param = std::make_shared(index, mDataId); + std::shared_ptr info(std::static_pointer_cast(c2param)); + mC2Buffer->setInfo(info); + } + mC2Buffer->registerOnDestroyNotify(&DestroyCallback, this); jobject linearBlock = env->NewObject( env->FindClass("android/media/MediaCodec$LinearBlock"), @@ -300,9 +358,18 @@ jobject FilterCallback::handleToLinearBlock(const native_handle_t* handle, uint3 gFields.linearBlockSetInternalStateID, (jlong)context.release(), true); - return linearBlock; + mLinearBlockObj = env->NewWeakGlobalRef(linearBlock); + mAvHandleRefCnt++; + return mLinearBlockObj; } +uint64_t MediaEvent::getAudioHandle() { + mDataIdRefCnt++; + return mDataId; +} + +/////////////// FilterCallback /////////////////////// + jobjectArray FilterCallback::getSectionEvent( jobjectArray& arr, const std::vector& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -333,6 +400,7 @@ jobjectArray FilterCallback::getMediaEvent( "", "(IZJJJLandroid/media/MediaCodec$LinearBlock;" "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V"); + jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; @@ -358,12 +426,6 @@ jobjectArray FilterCallback::getMediaEvent( } jlong dataLength = static_cast(mediaEvent.dataLength); - const native_handle_t* h = NULL; - jobject block = NULL; - if (mediaEvent.avMemory != NULL) { - h = mediaEvent.avMemory.getNativeHandle(); - block = handleToLinearBlock(h, dataLength); - } jint streamId = static_cast(mediaEvent.streamId); jboolean isPtsPresent = static_cast(mediaEvent.isPtsPresent); @@ -376,8 +438,18 @@ jobjectArray FilterCallback::getMediaEvent( jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength, - offset, block, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData, + offset, NULL, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData, audioDescriptor); + + if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) { + sp mediaEventSp = + new MediaEvent(mIFilter, mediaEvent.avMemory, + mediaEvent.avDataId, dataLength, obj); + mediaEventSp->mAvHandleRefCnt++; + env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get()); + mediaEventSp->incStrong(obj); + } + env->SetObjectArrayElement(arr, i, obj); } return arr; @@ -594,10 +666,10 @@ Return FilterCallback::onFilterStatus(const DemuxFilterStatus status) { return Void(); } -void FilterCallback::setFilter(const jobject filter) { +void FilterCallback::setFilter(const sp filter) { ALOGD("FilterCallback::setFilter"); - JNIEnv *env = AndroidRuntime::getJNIEnv(); - mFilter = env->NewWeakGlobalRef(filter); + mFilter = filter->mFilterObj; + mIFilter = filter->mFilterSp; } FilterCallback::~FilterCallback() { @@ -1431,7 +1503,7 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { filterSp->incStrong(filterObj); env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get()); - callback->setFilter(filterObj); + callback->setFilter(filterSp); return filterObj; } @@ -2390,6 +2462,9 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { gFields.onDvrPlaybackStatusID = env->GetMethodID(dvrPlaybackClazz, "onPlaybackStatusChanged", "(I)V"); + jclass mediaEventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent"); + gFields.mediaEventContext = env->GetFieldID(mediaEventClazz, "mNativeContext", "J"); + jclass linearBlockClazz = env->FindClass("android/media/MediaCodec$LinearBlock"); gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "", "()V"); gFields.linearBlockSetInternalStateID = @@ -3507,6 +3582,52 @@ static jlong android_media_tv_Tuner_write_dvr_to_array( return copyData(env, dvrSp->mDvrMQ, dvrSp->mDvrMQEventFlag, buffer, offset, size); } +static sp getMediaEventSp(JNIEnv *env, jobject mediaEventObj) { + return (MediaEvent *)env->GetLongField(mediaEventObj, gFields.mediaEventContext); +} + +static jobject android_media_tv_Tuner_media_event_get_linear_block( + JNIEnv* env, jobject mediaEventObj) { + sp mediaEventSp = getMediaEventSp(env, mediaEventObj); + if (mediaEventSp == NULL) { + ALOGD("Failed get MediaEvent"); + return NULL; + } + + return mediaEventSp->getLinearBlock(); +} + +static jobject android_media_tv_Tuner_media_event_get_audio_handle( + JNIEnv* env, jobject mediaEventObj) { + sp mediaEventSp = getMediaEventSp(env, mediaEventObj); + if (mediaEventSp == NULL) { + ALOGD("Failed get MediaEvent"); + return NULL; + } + + android::Mutex::Autolock autoLock(mediaEventSp->mLock); + uint64_t audioHandle = mediaEventSp->getAudioHandle(); + jclass longClazz = env->FindClass("java/lang/Long"); + jmethodID longInit = env->GetMethodID(longClazz, "", "(J)V"); + + jobject longObj = env->NewObject(longClazz, longInit, static_cast(audioHandle)); + return longObj; +} + +static void android_media_tv_Tuner_media_event_finalize(JNIEnv* env, jobject mediaEventObj) { + sp mediaEventSp = getMediaEventSp(env, mediaEventObj); + if (mediaEventSp == NULL) { + ALOGD("Failed get MediaEvent"); + return; + } + + android::Mutex::Autolock autoLock(mediaEventSp->mLock); + mediaEventSp->mAvHandleRefCnt--; + mediaEventSp->finalize(); + + mediaEventSp->decStrong(mediaEventObj); +} + static const JNINativeMethod gTunerMethods[] = { { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init }, { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup }, @@ -3629,6 +3750,15 @@ static const JNINativeMethod gLnbMethods[] = { { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_lnb }, }; +static const JNINativeMethod gMediaEventMethods[] = { + { "nativeGetLinearBlock", "()Landroid/media/MediaCodec$LinearBlock;", + (void *)android_media_tv_Tuner_media_event_get_linear_block }, + { "nativeGetAudioHandle", "()Ljava/lang/Long;", + (void *)android_media_tv_Tuner_media_event_get_audio_handle }, + { "nativeFinalize", "()V", + (void *)android_media_tv_Tuner_media_event_finalize }, +}; + static bool register_android_media_tv_Tuner(JNIEnv *env) { if (AndroidRuntime::registerNativeMethods( env, "android/media/tv/tuner/Tuner", gTunerMethods, NELEM(gTunerMethods)) != JNI_OK) { @@ -3677,6 +3807,13 @@ static bool register_android_media_tv_Tuner(JNIEnv *env) { ALOGE("Failed to register lnb native methods"); return false; } + if (AndroidRuntime::registerNativeMethods( + env, "android/media/tv/tuner/filter/MediaEvent", + gMediaEventMethods, + NELEM(gMediaEventMethods)) != JNI_OK) { + ALOGE("Failed to register MediaEvent native methods"); + return false; + } return true; } diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 3da78acb2f90..c469a3ad8b76 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -18,10 +18,14 @@ #define _ANDROID_MEDIA_TV_TUNER_H_ #include +#include +#include +#include #include #include #include #include +#include #include #include "jni.h" @@ -30,6 +34,7 @@ using ::android::hardware::EventFlag; using ::android::hardware::MQDescriptorSync; using ::android::hardware::MessageQueue; using ::android::hardware::Return; +using ::android::hardware::hidl_handle; using ::android::hardware::hidl_vec; using ::android::hardware::kSynchronizedReadWrite; using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent; @@ -106,15 +111,48 @@ struct Dvr : public RefBase { int mFd; }; +struct MediaEvent : public RefBase { + MediaEvent(sp iFilter, hidl_handle avHandle, uint64_t dataId, + uint64_t dataLength, jobject obj); + ~MediaEvent(); + jobject getLinearBlock(); + uint64_t getAudioHandle(); + void finalize(); + + sp mIFilter; + native_handle_t* mAvHandle; + uint64_t mDataId; + uint64_t mDataLength; + uint8_t* mBuffer; + android::Mutex mLock; + int mDataIdRefCnt; + int mAvHandleRefCnt; + jweak mMediaEventObj; + jweak mLinearBlockObj; + C2HandleIon* mIonHandle; + std::shared_ptr mC2Buffer; +}; + +struct Filter : public RefBase { + Filter(sp sp, jobject obj); + ~Filter(); + int close(); + sp getIFilter(); + sp mFilterSp; + std::unique_ptr mFilterMQ; + EventFlag* mFilterMQEventFlag; + jweak mFilterObj; +}; + struct FilterCallback : public IFilterCallback { ~FilterCallback(); virtual Return onFilterEvent(const DemuxFilterEvent& filterEvent); virtual Return onFilterStatus(const DemuxFilterStatus status); - void setFilter(const jobject filter); - jobject handleToLinearBlock(const native_handle_t* handle, uint32_t size); + void setFilter(const sp filter); private: jweak mFilter; + sp mIFilter; jobjectArray getSectionEvent( jobjectArray& arr, const std::vector& events); jobjectArray getMediaEvent( @@ -144,17 +182,6 @@ struct FrontendCallback : public IFrontendCallback { FrontendId mId; }; -struct Filter : public RefBase { - Filter(sp sp, jobject obj); - ~Filter(); - int close(); - sp getIFilter(); - sp mFilterSp; - std::unique_ptr mFilterMQ; - EventFlag* mFilterMQEventFlag; - jweak mFilterObj; -}; - struct TimeFilter : public RefBase { TimeFilter(sp sp, jweak obj); ~TimeFilter(); @@ -219,6 +246,14 @@ private: static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); }; +class C2DataIdInfo : public C2Param { +public: + C2DataIdInfo(uint32_t index, uint64_t value); +private: + typedef C2GlobalParam DummyInfo; + static const size_t kParamSize = sizeof(DummyInfo); +}; + } // namespace android #endif // _ANDROID_MEDIA_TV_TUNER_H_ -- GitLab From 0417f6972ad6c3e1b575958701baa78dea7bd3f1 Mon Sep 17 00:00:00 2001 From: Rambo Wang Date: Wed, 22 Apr 2020 15:24:51 -0700 Subject: [PATCH 018/166] Restrict match conditions of TelephonyNetworkSpecifier#canBeSatisfied TelephonyNetworkSpecifier will now treat null as matching nothing. When the request specifies a TelephonyNetworkSpecifier while the network does not, this should not be treated as a match. Bug: 154703135 Test: atest android.net.TelephonyNetworkSpecifierTest Change-Id: I329110e929995c9eae6c6ce33b5414777acea1e1 --- .../net/TelephonyNetworkSpecifier.java | 7 +++-- .../net/TelephonyNetworkSpecifierTest.java | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java index aafebd7ecfc8..33c71d5b312a 100644 --- a/core/java/android/net/TelephonyNetworkSpecifier.java +++ b/core/java/android/net/TelephonyNetworkSpecifier.java @@ -98,9 +98,10 @@ public final class TelephonyNetworkSpecifier extends NetworkSpecifier implements /** @hide */ @Override public boolean canBeSatisfiedBy(NetworkSpecifier other) { - // Any generic requests should be satisfied by a specific telephony network. - // For simplicity, we treat null same as MatchAllNetworkSpecifier - return equals(other) || other == null || other instanceof MatchAllNetworkSpecifier; + // Although the only caller, NetworkCapabilities, already handled the case of + // MatchAllNetworkSpecifier, we do it again here in case the API will be used by others. + // TODO(b/154959809): consider implementing bi-directional specifier instead. + return equals(other) || other instanceof MatchAllNetworkSpecifier; } diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java index 47afed441ace..efb92033df1e 100644 --- a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java +++ b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java @@ -19,7 +19,10 @@ package android.net; import static com.android.testutils.ParcelUtilsKt.assertParcelSane; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import android.net.wifi.WifiNetworkSpecifier; import android.telephony.SubscriptionManager; import androidx.test.filters.SmallTest; @@ -32,6 +35,7 @@ import org.junit.Test; @SmallTest public class TelephonyNetworkSpecifierTest { private static final int TEST_SUBID = 5; + private static final String TEST_SSID = "Test123"; /** * Validate that IllegalArgumentException will be thrown if build TelephonyNetworkSpecifier @@ -79,4 +83,31 @@ public class TelephonyNetworkSpecifierTest { .build(); assertParcelSane(specifier, 1 /* fieldCount */); } + + /** + * Validate the behavior of method canBeSatisfiedBy(). + */ + @Test + public void testCanBeSatisfiedBy() { + final TelephonyNetworkSpecifier tns1 = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(TEST_SUBID) + .build(); + final TelephonyNetworkSpecifier tns2 = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(TEST_SUBID) + .build(); + final WifiNetworkSpecifier wns = new WifiNetworkSpecifier.Builder() + .setSsid(TEST_SSID) + .build(); + final MatchAllNetworkSpecifier mans = new MatchAllNetworkSpecifier(); + + // Test equality + assertEquals(tns1, tns2); + assertTrue(tns1.canBeSatisfiedBy(tns1)); + assertTrue(tns1.canBeSatisfiedBy(tns2)); + + // Test other edge cases. + assertFalse(tns1.canBeSatisfiedBy(null)); + assertFalse(tns1.canBeSatisfiedBy(wns)); + assertTrue(tns1.canBeSatisfiedBy(mans)); + } } -- GitLab From 1e8c57f3c95a3453e7b14817810dc13b91c745c9 Mon Sep 17 00:00:00 2001 From: Ruchir Rastogi Date: Mon, 27 Apr 2020 17:41:58 -0700 Subject: [PATCH 019/166] Reduce memory footprint of LogEvent This reduces the memory footprint by 16 bytes per LogEvent. Test: atest statsd_test Test: atest CtsStatsdHostTestCases Bug: 154857643 Change-Id: I1e541816377ae8b14d789dc4d1f0a4ec91eef998 --- cmds/statsd/src/logd/LogEvent.cpp | 14 +++++++------- cmds/statsd/src/logd/LogEvent.h | 22 +++++++++------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 10b1059796a0..8ec0173ce461 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -227,8 +227,8 @@ void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last, } // Check if at least one node was successfully parsed. if (mValues.size() - 1 > firstUidInChainIndex) { - mAttributionChainStartIndex = firstUidInChainIndex; - mAttributionChainEndIndex = mValues.size() - 1; + mAttributionChainStartIndex = static_cast(firstUidInChainIndex); + mAttributionChainEndIndex = static_cast(mValues.size() - 1); } parseAnnotations(numAnnotations, firstUidInChainIndex); @@ -249,7 +249,7 @@ void LogEvent::parseIsUidAnnotation(uint8_t annotationType) { } bool isUid = readNextValue(); - if (isUid) mUidFieldIndex = mValues.size() - 1; + if (isUid) mUidFieldIndex = static_cast(mValues.size() - 1); mValues[mValues.size() - 1].mAnnotations.setUidField(isUid); } @@ -290,7 +290,7 @@ void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) { } const bool exclusiveState = readNextValue(); - mExclusiveStateFieldIndex = mValues.size() - 1; + mExclusiveStateFieldIndex = static_cast(mValues.size() - 1); mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState); } @@ -408,7 +408,7 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) { parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); break; case ERROR_TYPE: - mErrorBitmask = readNextValue(); + /* mErrorBitmask =*/ readNextValue(); mValid = false; break; default: @@ -577,8 +577,8 @@ bool LogEvent::hasAttributionChain(std::pair* indexRange) const { } if (nullptr != indexRange) { - indexRange->first = mAttributionChainStartIndex; - indexRange->second = mAttributionChainEndIndex; + indexRange->first = static_cast(mAttributionChainStartIndex); + indexRange->second = static_cast(mAttributionChainEndIndex); } return true; diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 731b9661067a..53fb5d93e3ac 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -160,7 +160,7 @@ public: // } // Note that atomIndex is 1-indexed. inline int getUidFieldIndex() { - return mUidFieldIndex; + return static_cast(mUidFieldIndex); } // Returns whether this LogEvent has an AttributionChain. @@ -179,7 +179,7 @@ public: // } // Note that atomIndex is 1-indexed. inline int getExclusiveStateFieldIndex() const { - return mExclusiveStateFieldIndex; + return static_cast(mExclusiveStateFieldIndex); } // If a reset state is not sent in the StatsEvent, returns -1. Note that a @@ -212,10 +212,6 @@ public: return mValid; } - int32_t getErrorBitmask() const { - return mErrorBitmask; - } - private: /** * Only use this if copy is absolutely needed. @@ -316,16 +312,16 @@ private: // The pid of the logging client (defaults to -1). int32_t mLogPid = -1; - // Bitmask of errors sent by StatsEvent/AStatsEvent. - int32_t mErrorBitmask = 0; - // Annotations bool mTruncateTimestamp = false; - int mUidFieldIndex = -1; - int mAttributionChainStartIndex = -1; - int mAttributionChainEndIndex = -1; - int mExclusiveStateFieldIndex = -1; int mResetState = -1; + + // Indexes within the FieldValue vector can be stored in 7 bits because + // that's the assumption enforced by the encoding used in FieldValue. + int8_t mUidFieldIndex = -1; + int8_t mAttributionChainStartIndex = -1; + int8_t mAttributionChainEndIndex = -1; + int8_t mExclusiveStateFieldIndex = -1; }; void writeExperimentIdsToProto(const std::vector& experimentIds, std::vector* protoOut); -- GitLab From a2b672ccd87a1baba5e9d7e5f9d59de8dd12919b Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Mon, 27 Apr 2020 18:35:14 -0700 Subject: [PATCH 020/166] Enable edge gesture if quickstep has never started Bug: 154580671 Test: Restart phone w/o touching nav bar, back works. Change-Id: I59bd060f2d06d273bc8e1eb1186273902ebe6c8e --- .../systemui/statusbar/phone/EdgeBackGestureHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index f9119c7a010f..ba8a63428cf6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -556,7 +556,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa private void updateDisabledForQuickstep() { int rotation = mContext.getResources().getConfiguration().windowConfiguration.getRotation(); - mDisabledForQuickstep = mStartingQuickstepRotation != rotation; + mDisabledForQuickstep = mStartingQuickstepRotation > -1 && + mStartingQuickstepRotation != rotation; } @Override -- GitLab From a07fb93cb06d5c4bcc00df6339eaa4904dfd0714 Mon Sep 17 00:00:00 2001 From: Yunfan Chen Date: Mon, 27 Apr 2020 18:35:14 +0900 Subject: [PATCH 021/166] Report caption insets change when layout This patch let DecorCaptionView report back to it's owner about height change, instead of let the DecorView query the height. The previous query points enableCaption() and updateDecorCaptionStatus() cannot ensure the caption layout is finished and attached to the phone window. Such that it may return a zero caption insets when it shouldn't be, if the app do window configuration change handling by restarting. The layout will ensure the caption insets get set when everything is ready. There will be no caption height change when resizing or moving, make sure the calculated insets always include the caption regardless of the frame position. Bug: 154792488 Test: Manuel test with message and settings app. Test: go/wm-smoke Test: atest InsetsControllerTest#testCaptionInsetsStateAssemble Test: atest InsetsSourceTest#testCalculateInsets_caption_resizing Change-Id: I1728628eccb32b912210a64fe3a1c9cbe9e3b302 --- core/java/android/view/InsetsSource.java | 6 +++--- .../com/android/internal/policy/DecorView.java | 10 ++++++++-- .../android/internal/widget/DecorCaptionView.java | 3 +++ .../src/android/view/InsetsSourceTest.java | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 033ccef3666d..d3f4b1bf9b81 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -116,15 +116,15 @@ public class InsetsSource implements Parcelable { if (!ignoreVisibility && !mVisible) { return Insets.NONE; } - if (!getIntersection(frame, relativeFrame, mTmpFrame)) { - return Insets.NONE; - } // During drag-move and drag-resizing, the caption insets position may not get updated // before the app frame get updated. To layout the app content correctly during drag events, // we always return the insets with the corresponding height covering the top. if (getType() == ITYPE_CAPTION_BAR) { return Insets.of(0, frame.height(), 0, 0); } + if (!getIntersection(frame, relativeFrame, mTmpFrame)) { + return Insets.NONE; + } // TODO: Currently, non-floating IME always intersects at bottom due to issues with cutout. // However, we should let the policy decide from the server. diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index f5d38ca37bb7..863659d7c4eb 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -2019,10 +2019,17 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind if (getForeground() != null) { drawableChanged(); } - getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight()); } } + /** + * An interface to be called when the caption visibility or height changed, to report the + * corresponding insets change to the InsetsController. + */ + public void notifyCaptionHeightChanged() { + getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight()); + } + void setWindow(PhoneWindow phoneWindow) { mWindow = phoneWindow; Context context = getContext(); @@ -2093,7 +2100,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mDecorCaptionView.onConfigurationChanged(displayWindowDecor); enableCaption(displayWindowDecor); } - getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight()); } void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java index 7a01024ffc36..21021457377a 100644 --- a/core/java/com/android/internal/widget/DecorCaptionView.java +++ b/core/java/com/android/internal/widget/DecorCaptionView.java @@ -30,6 +30,7 @@ import android.view.ViewOutlineProvider; import android.view.Window; import com.android.internal.R; +import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneWindow; import java.util.ArrayList; @@ -305,6 +306,8 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, } } + ((DecorView) mOwner.getDecorView()).notifyCaptionHeightChanged(); + // This assumes that the caption bar is at the top. mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(), mClose.getRight(), mClose.getBottom()); diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index b3f6fe9e88b9..c61f33e15b18 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -46,11 +47,13 @@ public class InsetsSourceTest { private InsetsSource mSource = new InsetsSource(ITYPE_NAVIGATION_BAR); private InsetsSource mImeSource = new InsetsSource(ITYPE_IME); + private InsetsSource mCaptionSource = new InsetsSource(ITYPE_CAPTION_BAR); @Before public void setUp() { mSource.setVisible(true); mImeSource.setVisible(true); + mCaptionSource.setVisible(true); } @Test @@ -101,6 +104,17 @@ public class InsetsSourceTest { assertEquals(Insets.of(0, 0, 0, 100), insets); } + @Test + public void testCalculateInsets_caption_resizing() { + mCaptionSource.setFrame(new Rect(0, 0, 100, 100)); + Insets insets = mCaptionSource.calculateInsets(new Rect(0, 0, 200, 200), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + insets = mCaptionSource.calculateInsets(new Rect(0, 0, 50, 200), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + insets = mCaptionSource.calculateInsets(new Rect(100, 100, 200, 500), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + @Test public void testCalculateInsets_invisible() { mSource.setFrame(new Rect(0, 0, 500, 100)); -- GitLab From 24d8a333bb33cd9312b325f40e442d702cf0d641 Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Fri, 24 Apr 2020 12:19:37 +0000 Subject: [PATCH 022/166] Address comments on NetworkStack AIDL v6 Address issues found during AIDL review: - Rename clientAddr to singleClientAddr - Do not use a ParcelableBundle for notifyNetworkTested or notifyDataStallSuspected; instead use AIDL parcelables for stronger backwards compatibility guarantees. Test: atest NetworkMonitorTest ConnectivityServiceTest ConnectivityServiceIntegrationTest, manual Bug: 153500847 Merged-In: Id9b71784e5f6294d203230e57737979e063ff0f8 Change-Id: Id9b71784e5f6294d203230e57737979e063ff0f8 --- .../net/dhcp/DhcpServingParamsParcelExt.java | 2 +- .../dhcp/DhcpServingParamsParcelExtTest.java | 2 +- .../networkstack/tethering/TetheringTest.java | 2 +- .../android/server/ConnectivityService.java | 79 ++++++++++++------- .../server/ConnectivityServiceTest.java | 55 ++++++------- 5 files changed, 83 insertions(+), 57 deletions(-) diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java index 82a26beadacf..4f8ad8a221ef 100644 --- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java +++ b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java @@ -169,7 +169,7 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { *

If not set, the default value is null. */ public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) { - this.clientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr); + this.singleClientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr); return this; } diff --git a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java index f8eb1476bad0..a8857b2e5cb0 100644 --- a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java +++ b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java @@ -110,7 +110,7 @@ public class DhcpServingParamsParcelExtTest { @Test public void testSetClientAddr() { mParcel.setSingleClientAddr(TEST_CLIENT_ADDRESS); - assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.clientAddr); + assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.singleClientAddr); } private static Inet4Address inet4Addr(String addr) { diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 0c86eeb6cd4d..0363f5f9989f 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -1689,7 +1689,7 @@ public class TetheringTest { final DhcpServingParamsParcel params = dhcpParamsCaptor.getValue(); assertEquals(serverAddr, intToInet4AddressHTH(params.serverAddr).getHostAddress()); assertEquals(24, params.serverAddrPrefixLength); - assertEquals(clientAddrParceled, params.clientAddr); + assertEquals(clientAddrParceled, params.singleClientAddr); } @Test diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 552331ede99b..e4eb585e989d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -18,6 +18,14 @@ package com.android.server; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK; +import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK; +import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT; +import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_DNS_EVENTS; +import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_TCP_METRICS; +import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS; +import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS; +import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; @@ -72,6 +80,7 @@ import android.net.ConnectionInfo; import android.net.ConnectivityDiagnosticsManager.ConnectivityReport; import android.net.ConnectivityDiagnosticsManager.DataStallReport; import android.net.ConnectivityManager; +import android.net.DataStallReportParcelable; import android.net.ICaptivePortal; import android.net.IConnectivityDiagnosticsCallback; import android.net.IConnectivityManager; @@ -108,6 +117,7 @@ import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; import android.net.NetworkState; +import android.net.NetworkTestResultParcelable; import android.net.NetworkUtils; import android.net.NetworkWatchlistManager; import android.net.PrivateDnsConfigParcel; @@ -2820,14 +2830,6 @@ public class ConnectivityService extends IConnectivityManager.Stub handleNetworkTested(nai, results.mTestResult, (results.mRedirectUrl == null) ? "" : results.mRedirectUrl); - - // Invoke ConnectivityReport generation for this Network test event. - final Message m = - mConnectivityDiagnosticsHandler.obtainMessage( - ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED, - new ConnectivityReportEvent(results.mTimestampMillis, nai)); - m.setData(msg.getData()); - mConnectivityDiagnosticsHandler.sendMessage(m); break; } case EVENT_PROVISIONING_NOTIFICATION: { @@ -3006,23 +3008,33 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) { - notifyNetworkTestedWithExtras(testResult, redirectUrl, SystemClock.elapsedRealtime(), - PersistableBundle.EMPTY); + // Legacy version of notifyNetworkTestedWithExtras. + // Would only be called if the system has a NetworkStack module older than the + // framework, which does not happen in practice. } @Override - public void notifyNetworkTestedWithExtras( - int testResult, - @Nullable String redirectUrl, - long timestampMillis, - @NonNull PersistableBundle extras) { - final Message msg = - mTrackerHandler.obtainMessage( - EVENT_NETWORK_TESTED, - new NetworkTestedResults( - mNetId, testResult, timestampMillis, redirectUrl)); - msg.setData(new Bundle(extras)); + public void notifyNetworkTestedWithExtras(NetworkTestResultParcelable p) { + final Message msg = mTrackerHandler.obtainMessage( + EVENT_NETWORK_TESTED, + new NetworkTestedResults( + mNetId, p.result, p.timestampMillis, p.redirectUrl)); mTrackerHandler.sendMessage(msg); + + // Invoke ConnectivityReport generation for this Network test event. + final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(mNetId); + if (nai == null) return; + final Message m = mConnectivityDiagnosticsHandler.obtainMessage( + ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED, + new ConnectivityReportEvent(p.timestampMillis, nai)); + + final PersistableBundle extras = new PersistableBundle(); + extras.putInt(KEY_NETWORK_VALIDATION_RESULT, p.result); + extras.putInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, p.probesSucceeded); + extras.putInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK, p.probesAttempted); + + m.setData(new Bundle(extras)); + mConnectivityDiagnosticsHandler.sendMessage(m); } @Override @@ -3071,12 +3083,25 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public void notifyDataStallSuspected( - long timestampMillis, int detectionMethod, PersistableBundle extras) { - final Message msg = - mConnectivityDiagnosticsHandler.obtainMessage( - ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED, - detectionMethod, mNetId, timestampMillis); + public void notifyDataStallSuspected(DataStallReportParcelable p) { + final Message msg = mConnectivityDiagnosticsHandler.obtainMessage( + ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED, + p.detectionMethod, mNetId, p.timestampMillis); + + final PersistableBundle extras = new PersistableBundle(); + switch (p.detectionMethod) { + case DETECTION_METHOD_DNS_EVENTS: + extras.putInt(KEY_DNS_CONSECUTIVE_TIMEOUTS, p.dnsConsecutiveTimeouts); + break; + case DETECTION_METHOD_TCP_METRICS: + extras.putInt(KEY_TCP_PACKET_FAIL_RATE, p.tcpPacketFailRate); + extras.putInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS, + p.tcpMetricsCollectionPeriodMillis); + break; + default: + log("Unknown data stall detection method, ignoring: " + p.detectionMethod); + return; + } msg.setData(new Bundle(extras)); // NetworkStateTrackerHandler currently doesn't take any actions based on data diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index a478e68c7dba..a992778fd45f 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -144,6 +144,7 @@ import android.net.ConnectivityManager.PacketKeepalive; import android.net.ConnectivityManager.PacketKeepaliveCallback; import android.net.ConnectivityManager.TooManyRequestsException; import android.net.ConnectivityThread; +import android.net.DataStallReportParcelable; import android.net.IConnectivityDiagnosticsCallback; import android.net.IDnsResolver; import android.net.IIpConnectivityMetrics; @@ -170,6 +171,7 @@ import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; import android.net.NetworkState; +import android.net.NetworkTestResultParcelable; import android.net.NetworkUtils; import android.net.ProxyInfo; import android.net.ResolverParamsParcel; @@ -196,7 +198,6 @@ import android.os.Looper; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; -import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; @@ -580,14 +581,6 @@ public class ConnectivityServiceTest { } private class TestNetworkAgentWrapper extends NetworkAgentWrapper { - private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS - | NETWORK_VALIDATION_PROBE_HTTP - | NETWORK_VALIDATION_PROBE_HTTPS; - private static final int VALIDATION_RESULT_VALID = VALIDATION_RESULT_BASE - | NETWORK_VALIDATION_RESULT_VALID; - private static final int VALIDATION_RESULT_PARTIAL = VALIDATION_RESULT_BASE - | NETWORK_VALIDATION_PROBE_FALLBACK - | NETWORK_VALIDATION_RESULT_PARTIAL; private static final int VALIDATION_RESULT_INVALID = 0; private static final long DATA_STALL_TIMESTAMP = 10L; @@ -595,12 +588,10 @@ public class ConnectivityServiceTest { private INetworkMonitor mNetworkMonitor; private INetworkMonitorCallbacks mNmCallbacks; - private int mNmValidationResult = VALIDATION_RESULT_BASE; + private int mNmValidationResult = VALIDATION_RESULT_INVALID; private int mProbesCompleted; private int mProbesSucceeded; private String mNmValidationRedirectUrl = null; - private PersistableBundle mValidationExtras = PersistableBundle.EMPTY; - private PersistableBundle mDataStallExtras = PersistableBundle.EMPTY; private boolean mNmProvNotificationRequested = false; private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); @@ -668,8 +659,13 @@ public class ConnectivityServiceTest { } mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded); - mNmCallbacks.notifyNetworkTestedWithExtras( - mNmValidationResult, mNmValidationRedirectUrl, TIMESTAMP, mValidationExtras); + final NetworkTestResultParcelable p = new NetworkTestResultParcelable(); + p.result = mNmValidationResult; + p.probesAttempted = mProbesCompleted; + p.probesSucceeded = mProbesSucceeded; + p.redirectUrl = mNmValidationRedirectUrl; + p.timestampMillis = TIMESTAMP; + mNmCallbacks.notifyNetworkTestedWithExtras(p); if (mNmValidationRedirectUrl != null) { mNmCallbacks.showProvisioningNotification( @@ -751,9 +747,9 @@ public class ConnectivityServiceTest { } void setNetworkValid(boolean isStrictMode) { - mNmValidationResult = VALIDATION_RESULT_VALID; + mNmValidationResult = NETWORK_VALIDATION_RESULT_VALID; mNmValidationRedirectUrl = null; - int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTP; + int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS; if (isStrictMode) { probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS; } @@ -765,8 +761,9 @@ public class ConnectivityServiceTest { void setNetworkInvalid(boolean isStrictMode) { mNmValidationResult = VALIDATION_RESULT_INVALID; mNmValidationRedirectUrl = null; - int probesCompleted = VALIDATION_RESULT_BASE; - int probesSucceeded = VALIDATION_RESULT_INVALID; + int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS + | NETWORK_VALIDATION_PROBE_HTTP; + int probesSucceeded = 0; // If the isStrictMode is true, it means the network is invalid when NetworkMonitor // tried to validate the private DNS but failed. if (isStrictMode) { @@ -782,7 +779,7 @@ public class ConnectivityServiceTest { mNmValidationRedirectUrl = redirectUrl; // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet. - int probesCompleted = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS; + int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP; int probesSucceeded = VALIDATION_RESULT_INVALID; if (isStrictMode) { probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; @@ -791,18 +788,20 @@ public class ConnectivityServiceTest { } void setNetworkPartial() { - mNmValidationResult = VALIDATION_RESULT_PARTIAL; + mNmValidationResult = NETWORK_VALIDATION_RESULT_PARTIAL; mNmValidationRedirectUrl = null; - int probesCompleted = VALIDATION_RESULT_BASE; - int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS; + int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS + | NETWORK_VALIDATION_PROBE_FALLBACK; + int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK; setProbesStatus(probesCompleted, probesSucceeded); } void setNetworkPartialValid(boolean isStrictMode) { setNetworkPartial(); - mNmValidationResult |= VALIDATION_RESULT_VALID; - int probesCompleted = VALIDATION_RESULT_BASE; - int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS; + mNmValidationResult |= NETWORK_VALIDATION_RESULT_VALID; + int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS + | NETWORK_VALIDATION_PROBE_HTTP; + int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP; // Suppose the partial network cannot pass the private DNS validation as well, so only // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded. if (isStrictMode) { @@ -838,8 +837,10 @@ public class ConnectivityServiceTest { } void notifyDataStallSuspected() throws Exception { - mNmCallbacks.notifyDataStallSuspected( - DATA_STALL_TIMESTAMP, DATA_STALL_DETECTION_METHOD, mDataStallExtras); + final DataStallReportParcelable p = new DataStallReportParcelable(); + p.detectionMethod = DATA_STALL_DETECTION_METHOD; + p.timestampMillis = DATA_STALL_TIMESTAMP; + mNmCallbacks.notifyDataStallSuspected(p); } } -- GitLab From 2986572ae8b84a4607af2cc123c163cef173b38f Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Fri, 17 Apr 2020 17:58:33 +0100 Subject: [PATCH 023/166] Record supported extension versions at rollback creation. This adds a property to the Rollback class to record which extension versions were supported at the time of creation. This will be used to block commit of a rollback if it may result in an installed app's minExtensionVersion being violated. See go/sdk-extensions-and-rollback for details. Bug: 152737927 Test: atest RollbackStoreTest Test: atest RollbackTest Merged-In: If770a615a1b23505477d786d6ab204e32d9af4c5 Change-Id: If770a615a1b23505477d786d6ab204e32d9af4c5 --- .../java/android/os/ext/SdkExtensions.java | 3 + .../com/android/server/rollback/Rollback.java | 25 +++- .../rollback/RollbackManagerServiceImpl.java | 20 +++- .../server/rollback/RollbackStore.java | 45 ++++++- .../server/rollback/RollbackStoreTest.java | 110 +++++++++++++++++- .../server/rollback/RollbackUnitTest.java | 4 +- 6 files changed, 189 insertions(+), 18 deletions(-) diff --git a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java index c268ff4291e4..6c25f2849cea 100644 --- a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java +++ b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java @@ -38,6 +38,9 @@ public class SdkExtensions { private static final int R_EXTENSION_INT; static { + // Note: when adding more extension versions, the logic that records current + // extension versions when saving a rollback must also be updated. + // At the time of writing this is in RollbackManagerServiceImpl#getExtensionVersions() R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0); } diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 885f561d2a19..9171501270d0 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -39,6 +39,7 @@ import android.os.UserManager; import android.text.TextUtils; import android.util.IntArray; import android.util.Slog; +import android.util.SparseIntArray; import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; @@ -173,6 +174,12 @@ class Rollback { @GuardedBy("mLock") private int mNumPackageSessionsWithSuccess; + /** + * The extension versions supported at the time of rollback creation. May be null if not set + * at creation time. + */ + @Nullable private final SparseIntArray mExtensionVersions; + /** * Constructs a new, empty Rollback instance. * @@ -182,9 +189,11 @@ class Rollback { * @param userId the user that performed the install with rollback enabled. * @param installerPackageName the installer package name from the original install session. * @param packageSessionIds the session ids for all packages in the install. + * @param extensionVersions the extension versions supported at the time of rollback creation */ Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId, - String installerPackageName, int[] packageSessionIds) { + String installerPackageName, int[] packageSessionIds, + SparseIntArray extensionVersions) { this.info = new RollbackInfo(rollbackId, /* packages */ new ArrayList<>(), /* isStaged */ stagedSessionId != -1, @@ -197,11 +206,12 @@ class Rollback { mState = ROLLBACK_STATE_ENABLING; mTimestamp = Instant.now(); mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0]; + mExtensionVersions = extensionVersions; } Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId, String installerPackageName) { - this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null); + this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null, null); } /** @@ -209,7 +219,7 @@ class Rollback { */ Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId, @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress, - int userId, String installerPackageName) { + int userId, String installerPackageName, SparseIntArray extensionVersions) { this.info = info; mUserId = userId; mInstallerPackageName = installerPackageName; @@ -219,6 +229,7 @@ class Rollback { mState = state; mApkSessionId = apkSessionId; mRestoreUserDataInProgress = restoreUserDataInProgress; + mExtensionVersions = extensionVersions; // TODO(b/120200473): Include this field during persistence. This field will be used to // decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting // this field is not backward compatible. We won't fix b/120200473 until S to minimize the @@ -282,6 +293,14 @@ class Rollback { return mInstallerPackageName; } + /** + * Returns the extension versions that were supported at the time that the rollback was created, + * as a mapping from SdkVersion to ExtensionVersion. + */ + @Nullable SparseIntArray getExtensionVersions() { + return mExtensionVersions; + } + /** * Returns true if the rollback is in the ENABLING state. */ diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 83e99b008b68..6726cc829c81 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -41,6 +41,7 @@ import android.content.rollback.IRollbackManager; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Binder; +import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerExecutor; @@ -49,12 +50,14 @@ import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.os.ext.SdkExtensions; import android.provider.DeviceConfig; import android.util.IntArray; import android.util.Log; import android.util.LongArrayQueue; import android.util.Slog; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; @@ -1274,16 +1277,29 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (parentSession.isStaged()) { rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId, - installerPackageName, packageSessionIds); + installerPackageName, packageSessionIds, getExtensionVersions()); } else { rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId, - installerPackageName, packageSessionIds); + installerPackageName, packageSessionIds, getExtensionVersions()); } mRollbacks.add(rollback); return rollback; } + private SparseIntArray getExtensionVersions() { + // This list must be updated whenever the current API level is increased, or should be + // replaced when we have another way of determining the relevant SDK versions. + final int[] relevantSdkVersions = { Build.VERSION_CODES.R }; + + SparseIntArray result = new SparseIntArray(relevantSdkVersions.length); + for (int i = 0; i < relevantSdkVersions.length; i++) { + result.put(relevantSdkVersions[i], + SdkExtensions.getExtensionVersion(relevantSdkVersions[i])); + } + return result; + } + /** * Returns the Rollback associated with the given session if parent or child session id matches. * Returns null if not found. diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 7b046c1aa3a6..f186d65baaa1 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -21,6 +21,7 @@ import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.rollback.Rollback.rollbackStateFromString; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; @@ -28,6 +29,7 @@ import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.util.IntArray; import android.util.Slog; +import android.util.SparseIntArray; import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; @@ -176,6 +178,35 @@ class RollbackStore { return ceSnapshotInodes; } + private static @Nullable JSONArray extensionVersionsToJson( + @Nullable SparseIntArray extensionVersions) throws JSONException { + if (extensionVersions == null) { + return null; + } + JSONArray array = new JSONArray(); + for (int i = 0; i < extensionVersions.size(); i++) { + JSONObject entryJson = new JSONObject(); + entryJson.put("sdkVersion", extensionVersions.keyAt(i)); + entryJson.put("extensionVersion", extensionVersions.valueAt(i)); + array.put(entryJson); + } + return array; + } + + private static @Nullable SparseIntArray extensionVersionsFromJson(@Nullable JSONArray json) + throws JSONException { + if (json == null) { + return null; + } + SparseIntArray extensionVersions = new SparseIntArray(json.length()); + for (int i = 0; i < json.length(); i++) { + JSONObject entry = json.getJSONObject(i); + extensionVersions.append( + entry.getInt("sdkVersion"), entry.getInt("extensionVersion")); + } + return extensionVersions; + } + private static JSONObject rollbackInfoToJson(RollbackInfo rollback) throws JSONException { JSONObject json = new JSONObject(); json.put("rollbackId", rollback.getRollbackId()); @@ -200,10 +231,10 @@ class RollbackStore { * backupDir assigned. */ Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName, - int[] packageSessionIds) { + int[] packageSessionIds, SparseIntArray extensionVersions) { File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId)); return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName, - packageSessionIds); + packageSessionIds, extensionVersions); } /** @@ -211,10 +242,11 @@ class RollbackStore { * backupDir assigned. */ Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId, - String installerPackageName, int[] packageSessionIds) { + String installerPackageName, int[] packageSessionIds, + SparseIntArray extensionVersions) { File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId)); return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, - packageSessionIds); + packageSessionIds, extensionVersions); } /** @@ -272,6 +304,8 @@ class RollbackStore { dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress()); dataJson.put("userId", rollback.getUserId()); dataJson.putOpt("installerPackageName", rollback.getInstallerPackageName()); + dataJson.putOpt( + "extensionVersions", extensionVersionsToJson(rollback.getExtensionVersions())); PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json")); pw.println(dataJson.toString()); @@ -316,7 +350,8 @@ class RollbackStore { dataJson.getInt("apkSessionId"), dataJson.getBoolean("restoreUserDataInProgress"), dataJson.optInt("userId", USER_SYSTEM), - dataJson.optString("installerPackageName", "")); + dataJson.optString("installerPackageName", ""), + extensionVersionsFromJson(dataJson.optJSONArray("extensionVersions"))); } private static JSONObject toJson(VersionedPackage pkg) throws JSONException { diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java index 64d05f07e64e..6afdfba70f58 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.util.IntArray; +import android.util.SparseIntArray; import android.util.SparseLongArray; import com.google.common.truth.Correspondence; @@ -83,7 +84,7 @@ public class RollbackStoreTest { } }; - private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':" + private static final String JSON_ROLLBACK_NO_EXT = "{'info':{'rollbackId':123,'packages':" + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55}," + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':" + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'}," @@ -103,6 +104,28 @@ public class RollbackStoreTest { + "'restoreUserDataInProgress':true, 'userId':0," + "'installerPackageName':'some.installer'}"; + private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':" + + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55}," + + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':" + + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'}," + + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'isApkInApex':false," + + "'installedUsers':" + + "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6}," + + "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546," + + "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips'," + + "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test'," + + "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18," + + "'appId':-12,'seInfo':''}],'isApex':false,'isApkInApex':false," + + "'installedUsers':[55,79]," + + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello'," + + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}]," + + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z'," + + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1," + + "'restoreUserDataInProgress':true, 'userId':0," + + "'installerPackageName':'some.installer'," + + "'extensionVersions':[{'sdkVersion':5,'extensionVersion':25}," + + "{'sdkVersion':30,'extensionVersion':71}]}"; + @Rule public TemporaryFolder mFolder = new TemporaryFolder(); @@ -119,7 +142,10 @@ public class RollbackStoreTest { @Test public void createNonStaged() { - Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null); + SparseIntArray extensionVersions = new SparseIntArray(); + extensionVersions.put(30, 71); + Rollback rollback = mRollbackStore.createNonStagedRollback( + ID, USER, INSTALLER, null, extensionVersions); assertThat(rollback.getBackupDir().getAbsolutePath()) .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID); @@ -128,11 +154,16 @@ public class RollbackStoreTest { assertThat(rollback.info.getRollbackId()).isEqualTo(ID); assertThat(rollback.info.getPackages()).isEmpty(); assertThat(rollback.isEnabling()).isTrue(); + assertThat(rollback.getExtensionVersions().toString()) + .isEqualTo(extensionVersions.toString()); } @Test public void createStaged() { - Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER, null); + SparseIntArray extensionVersions = new SparseIntArray(); + extensionVersions.put(30, 71); + Rollback rollback = mRollbackStore.createStagedRollback( + ID, 897, USER, INSTALLER, null, extensionVersions); assertThat(rollback.getBackupDir().getAbsolutePath()) .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID); @@ -143,11 +174,17 @@ public class RollbackStoreTest { assertThat(rollback.info.getRollbackId()).isEqualTo(ID); assertThat(rollback.info.getPackages()).isEmpty(); assertThat(rollback.isEnabling()).isTrue(); + assertThat(rollback.getExtensionVersions().toString()) + .isEqualTo(extensionVersions.toString()); } @Test public void saveAndLoadRollback() { - Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null); + SparseIntArray extensionVersions = new SparseIntArray(); + extensionVersions.put(5, 25); + extensionVersions.put(30, 71); + Rollback origRb = mRollbackStore.createNonStagedRollback( + ID, USER, INSTALLER, null, extensionVersions); origRb.setRestoreUserDataInProgress(true); origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2)); @@ -195,9 +232,63 @@ public class RollbackStoreTest { assertRollbacksAreEquivalent(loadedRb, origRb); } + @Test + public void loadFromJsonNoExtensionVersions() throws Exception { + Rollback expectedRb = + mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null); + + expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z")); + expectedRb.setRestoreUserDataInProgress(true); + expectedRb.info.getCausePackages().add(new VersionedPackage("hello", 23)); + expectedRb.info.getCausePackages().add(new VersionedPackage("something", 999)); + expectedRb.info.setCommittedSessionId(45654465); + + PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55), + new VersionedPackage("blah1", 50), new IntArray(), new ArrayList<>(), + false, false, new IntArray(), new SparseLongArray()); + pkgInfo1.getPendingBackups().add(59); + pkgInfo1.getPendingBackups().add(1245); + pkgInfo1.getPendingBackups().add(124544); + pkgInfo1.getCeSnapshotInodes().put(546546, 345689375); + pkgInfo1.getCeSnapshotInodes().put(2222, 81641654445L); + pkgInfo1.getCeSnapshotInodes().put(1, -6); + + pkgInfo1.getPendingRestores().add( + new PackageRollbackInfo.RestoreInfo(498, 32322, "wombles")); + pkgInfo1.getPendingRestores().add( + new PackageRollbackInfo.RestoreInfo(-895, 1, "pingu")); + + pkgInfo1.getSnapshottedUsers().add(498468432); + pkgInfo1.getSnapshottedUsers().add(1111); + pkgInfo1.getSnapshottedUsers().add(98464); + + PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28), + new VersionedPackage("com.chips.test", 48), new IntArray(), new ArrayList<>(), + false, false, new IntArray(), new SparseLongArray()); + pkgInfo2.getPendingBackups().add(5); + + pkgInfo2.getPendingRestores().add( + new PackageRollbackInfo.RestoreInfo(18, -12, "")); + + pkgInfo2.getSnapshottedUsers().add(55); + pkgInfo2.getSnapshottedUsers().add(79); + + expectedRb.info.getPackages().add(pkgInfo1); + expectedRb.info.getPackages().add(pkgInfo2); + + Rollback parsedRb = RollbackStore.rollbackFromJson( + new JSONObject(JSON_ROLLBACK_NO_EXT), expectedRb.getBackupDir()); + + assertRollbacksAreEquivalent(parsedRb, expectedRb); + } + @Test public void loadFromJson() throws Exception { - Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null); + SparseIntArray extensionVersions = new SparseIntArray(); + extensionVersions.put(5, 25); + extensionVersions.put(30, 71); + Rollback expectedRb = mRollbackStore.createNonStagedRollback( + ID, USER, INSTALLER, null, extensionVersions); expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z")); expectedRb.setRestoreUserDataInProgress(true); @@ -246,7 +337,7 @@ public class RollbackStoreTest { @Test public void saveAndDelete() { - Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null); + Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null); RollbackStore.saveRollback(rollback); @@ -294,6 +385,13 @@ public class RollbackStoreTest { assertThat(a.getUserId()).isEqualTo(b.getUserId()); assertThat(a.getInstallerPackageName()).isEqualTo(b.getInstallerPackageName()); + + if (a.getExtensionVersions() == null) { + assertThat(b.getExtensionVersions()).isNull(); + } else { + assertThat(b.getExtensionVersions().toString()) + .isEqualTo(a.getExtensionVersions().toString()); + } } private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) { diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index 1a6c6b4011cd..e74891c7b3f8 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -317,7 +317,7 @@ public class RollbackUnitTest { public void notifySessionWithSuccess() { int[] sessionIds = new int[]{ 7777, 8888 }; Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER, - sessionIds); + sessionIds, null); // The 1st invocation returns false because not all child sessions are notified. assertThat(rollback.notifySessionWithSuccess()).isFalse(); // The 2nd invocation returns true because now all child sessions are notified. @@ -328,7 +328,7 @@ public class RollbackUnitTest { public void allPackagesEnabled() { int[] sessionIds = new int[]{ 7777, 8888 }; Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER, - sessionIds); + sessionIds, null); // #allPackagesEnabled returns false when 1 out of 2 packages is enabled. rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false)); assertThat(rollback.allPackagesEnabled()).isFalse(); -- GitLab From d4957437b7eabb42c6887a63b3b38df803cb5061 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 24 Apr 2020 22:06:29 +0800 Subject: [PATCH 024/166] Extensive handling of fixed rotation launching app This fixes a case that display does not rotate after multiple activities in a different rotation than display are launched. - Link the transform state from the existing one to the new launching activity. So the heavy duplicated calculation is omitted and they can be recognized as a group to continue to update display rotation. - Add a dedicated transition listener so it will not miss to handle display rotation if the reported token is different. That avoids leakage if somehow the transition is not notified then the listener is not unregistered. The path to finish fixed rotation is also simplified because there is always a transition listener to handle the incoming record. Fixes: 154911677 Test: atest DisplayContentTests#testApplyTopFixedRotationTransform Change-Id: Id370d7980d11553905b6a051482e3765ed61dc39 --- .../com/android/server/wm/DisplayContent.java | 94 ++++++++++++------- .../com/android/server/wm/WindowToken.java | 36 +++---- .../server/wm/DisplayContentTests.java | 32 +++++-- .../wm/RecentsAnimationControllerTest.java | 4 - .../android/server/wm/WindowTokenTests.java | 4 +- 5 files changed, 101 insertions(+), 69 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c41029b20651..5b2ccf43833d 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -498,6 +498,9 @@ class DisplayContent extends WindowContainer mWinAddedSinceNullFocus = new ArrayList<>(); @@ -928,6 +931,7 @@ class DisplayContent extends WindowContainer applyRotation(oldRotation, newRotation)); mFixedRotationLaunchingApp = null; } @@ -5494,6 +5494,34 @@ class DisplayContent extends WindowContainer { * rotated by the given rotated display info, frames and insets. */ private static class FixedRotationTransformState { - final WindowToken mOwner; final DisplayInfo mDisplayInfo; final DisplayFrames mDisplayFrames; final InsetsState mInsetsState; @@ -133,10 +132,9 @@ class WindowToken extends WindowContainer { final ArrayList> mRotatedContainers = new ArrayList<>(3); boolean mIsTransforming = true; - FixedRotationTransformState(WindowToken owner, DisplayInfo rotatedDisplayInfo, + FixedRotationTransformState(DisplayInfo rotatedDisplayInfo, DisplayFrames rotatedDisplayFrames, InsetsState rotatedInsetsState, Configuration rotatedConfig, int currentRotation) { - mOwner = owner; mDisplayInfo = rotatedDisplayInfo; mDisplayFrames = rotatedDisplayFrames; mInsetsState = rotatedInsetsState; @@ -482,6 +480,14 @@ class WindowToken extends WindowContainer { return mFixedRotationTransformState != null; } + /** Returns {@code true} if the given token shares the same transform. */ + boolean hasFixedRotationTransform(WindowToken token) { + if (mFixedRotationTransformState == null || token == null) { + return false; + } + return this == token || mFixedRotationTransformState == token.mFixedRotationTransformState; + } + boolean isFinishingFixedRotationTransform() { return mFixedRotationTransformState != null && !mFixedRotationTransformState.mIsTransforming; @@ -520,15 +526,14 @@ class WindowToken extends WindowContainer { final InsetsState insetsState = new InsetsState(); mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, insetsState, mDisplayContent.getConfiguration().uiMode); - mFixedRotationTransformState = new FixedRotationTransformState(this, info, displayFrames, + mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, insetsState, new Configuration(config), mDisplayContent.getRotation()); onConfigurationChanged(getParent().getConfiguration()); } /** * Reuses the {@link FixedRotationTransformState} (if any) from the other WindowToken to this - * one. This takes the same effect as {@link #applyFixedRotationTransform}, but the linked state - * can only be cleared by the state owner. + * one. This takes the same effect as {@link #applyFixedRotationTransform}. */ void linkFixedRotationTransform(WindowToken other) { if (mFixedRotationTransformState != null) { @@ -543,28 +548,15 @@ class WindowToken extends WindowContainer { onConfigurationChanged(getParent().getConfiguration()); } - /** - * Finishes the transform and continue updating the orientation change of display. Only the - * state owner can finish the transform state. - */ void finishFixedRotationTransform() { - if (mFixedRotationTransformState == null || mFixedRotationTransformState.mOwner != this) { - return; - } - final boolean changed = - mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this); - // If it is not the launching app or the display is not rotated, make sure the transform is - // cleared and the configuration is restored from parent. - if (!changed) { - clearFixedRotationTransform(null /* applyDisplayRotation */); - } + finishFixedRotationTransform(null /* applyDisplayRotation */); } /** - * Clears the transform and apply display rotation if the action is given. If the display will + * Finishes the transform and apply display rotation if the action is given. If the display will * not rotate, the transformed containers are restored to their original states. */ - void clearFixedRotationTransform(Runnable applyDisplayRotation) { + void finishFixedRotationTransform(Runnable applyDisplayRotation) { final FixedRotationTransformState state = mFixedRotationTransformState; if (state == null) { return; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index e02ea813cdd7..cba89d0aea2d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -790,9 +790,7 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); - final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE - ? SCREEN_ORIENTATION_PORTRAIT - : SCREEN_ORIENTATION_LANDSCAPE; + final int newOrientation = getRotatedOrientation(dc); final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer) @@ -812,9 +810,7 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); - final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE - ? SCREEN_ORIENTATION_PORTRAIT - : SCREEN_ORIENTATION_LANDSCAPE; + final int newOrientation = getRotatedOrientation(dc); final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer) @@ -1083,7 +1079,8 @@ public class DisplayContentTests extends WindowTestsBase { mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); mDisplayContent.mOpeningApps.add(app); - app.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + final int newOrientation = getRotatedOrientation(mDisplayContent); + app.setRequestedOrientation(newOrientation); assertTrue(app.isFixedRotationTransforming()); assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly( @@ -1124,12 +1121,25 @@ public class DisplayContentTests extends WindowTestsBase { mWallpaperWindow.mToken.onAnimationLeashCreated(t, null /* leash */); verify(t, never()).setPosition(any(), eq(0), eq(0)); + // Launch another activity before the transition is finished. + final ActivityRecord app2 = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setDisplay(mDisplayContent).build().getTopMostActivity(); + mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN, + false /* alwaysKeepCurrent */); + mDisplayContent.mOpeningApps.add(app2); + app2.setRequestedOrientation(newOrientation); + + // The activity should share the same transform state as the existing one. + assertTrue(app.hasFixedRotationTransform(app2)); + + // The display should be rotated after the launch is finished. mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token); // The animation in old rotation should be cancelled. assertFalse(closingApp.isAnimating()); - // The display should be rotated after the launch is finished. + // The fixed rotation should be cleared and the new rotation is applied to display. assertFalse(app.hasFixedRotationTransform()); + assertFalse(app2.hasFixedRotationTransform()); assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation); } @@ -1292,6 +1302,12 @@ public class DisplayContentTests extends WindowTestsBase { assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop))); } + private static int getRotatedOrientation(DisplayContent dc) { + return dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE + ? SCREEN_ORIENTATION_PORTRAIT + : SCREEN_ORIENTATION_LANDSCAPE; + } + private static List reverseList(List list) { final ArrayList result = new ArrayList<>(list); Collections.reverse(result); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index f19550ced0bf..409889b660b7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -401,10 +401,6 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertEquals(Configuration.ORIENTATION_PORTRAIT, wallpapers.get(0).getConfiguration().orientation); - // Wallpaper's transform state is controlled by home, so the invocation should be no-op. - wallpaperWindowToken.finishFixedRotationTransform(); - assertTrue(wallpaperWindowToken.hasFixedRotationTransform()); - // Wallpaper's transform state should be cleared with home. homeActivity.finishFixedRotationTransform(); assertFalse(wallpaperWindowToken.hasFixedRotationTransform()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 535d53eeef71..23a097eb0c7c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -136,7 +136,7 @@ public class WindowTokenTests extends WindowTestsBase { } @Test - public void testClearFixedRotationTransform() { + public void testFinishFixedRotationTransform() { final WindowToken appToken = mAppWindow.mToken; final WindowToken wallpaperToken = mWallpaperWindow.mToken; final Configuration config = new Configuration(mDisplayContent.getConfiguration()); @@ -152,7 +152,7 @@ public class WindowTokenTests extends WindowTestsBase { assertEquals(targetRotation, wallpaperToken.getWindowConfiguration().getRotation()); // The display doesn't rotate, the transformation will be canceled. - mAppWindow.mToken.clearFixedRotationTransform(null /* applyDisplayRotation */); + mAppWindow.mToken.finishFixedRotationTransform(); // The window tokens should restore to the original rotation. assertEquals(originalRotation, appToken.getWindowConfiguration().getRotation()); -- GitLab From dc217b9d20d6e85d84aa8107a6bdef8ef2e05aa5 Mon Sep 17 00:00:00 2001 From: Sundong Ahn Date: Thu, 23 Apr 2020 20:08:55 +0900 Subject: [PATCH 025/166] Change to using sysprop for services.core.unboosted The configstore service was deprecated. So change to use sysprop instead of configstore Bug: 124531214 Test: 1. check reading property values 2. uninstall configstore && check reading property values 3. uninstall configstore && check reading default values Change-Id: Ia4471a58fdc042d3a011d965582c2ec02f338f3c --- services/core/Android.bp | 1 + .../android/server/wm/WindowManagerService.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/services/core/Android.bp b/services/core/Android.bp index 77773edc28ca..9e483f919b47 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -130,6 +130,7 @@ java_library_static { "dnsresolver_aidl_interface-V4-java", "netd_event_listener_interface-java", "overlayable_policy_aidl-java", + "SurfaceFlingerProperties", ], } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8eb4b2659cc0..4eba8b0b55b4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -195,6 +195,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; +import android.sysprop.SurfaceFlingerProperties; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -304,7 +305,9 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; @@ -4648,6 +4651,11 @@ public class WindowManagerService extends IWindowManager.Stub } private static boolean queryWideColorGamutSupport() { + boolean defaultValue = false; + Optional hasWideColorProp = SurfaceFlingerProperties.has_wide_color_display(); + if (hasWideColorProp.isPresent()) { + return hasWideColorProp.get(); + } try { ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); OptionalBool hasWideColor = surfaceFlinger.hasWideColorDisplay(); @@ -4656,11 +4664,18 @@ public class WindowManagerService extends IWindowManager.Stub } } catch (RemoteException e) { // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store + } catch (NoSuchElementException e) { + return defaultValue; } return false; } private static boolean queryHdrSupport() { + boolean defaultValue = false; + Optional hasHdrProp = SurfaceFlingerProperties.has_HDR_display(); + if (hasHdrProp.isPresent()) { + return hasHdrProp.get(); + } try { ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay(); @@ -4669,6 +4684,8 @@ public class WindowManagerService extends IWindowManager.Stub } } catch (RemoteException e) { // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store + } catch (NoSuchElementException e) { + return defaultValue; } return false; } -- GitLab From cbc5d2721b8f97e619f4bcf63cb6c58c6f2e7580 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 24 Apr 2020 23:26:46 +0800 Subject: [PATCH 026/166] Polish fixed rotated recents animation For the cases that move recents or home to top, the transformed state is kept until the transition of task-to-front is done. This fixes the flickering when switching between recents and the activity that supported PiP. Also eliminated an additional toggle of fixed rotation transform from finishing recents animation. Fixes: 154588225 Test: atest RecentsAnimationControllerTest Change-Id: I92c6dfac1d030f76f62a10bd0596f935adffb63c --- .../com/android/server/wm/DisplayContent.java | 60 +++++++++++++++++-- .../server/wm/RecentsAnimationController.java | 11 ++-- .../wm/RecentsAnimationControllerTest.java | 18 +++++- 3 files changed, 78 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5b2ccf43833d..a1620fe410c7 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -494,6 +494,7 @@ class DisplayContent extends WindowContainer Date: Sat, 25 Apr 2020 01:58:23 +0800 Subject: [PATCH 027/166] Clear fixed rotation if display config does not change There is a timing that when remote rotation is completed, the orientation from sensor has been updated again. If the transition of rotated app hasn't complete, the display is still in previous orientation, then if it is the same as the last value from sensor, the display configuration does not change. So in this case the rotated app needs to be restored to avoid showing different orientation than the display. Fixes: 153327533 Test: ActivityRecordTests#testActivityOnCancelFixedRotationTransform Test: Add delay to execute DisplayRotation#continueRotation. Put device in landscape with a portrait home on top, launch an activity without fixed orientation, and then rotate the device to portrait immediately. The activity should launch in landscape and rotate to portrait with animation. Change-Id: Ic966923a751d94cc6ea86a83c2f600424999799c --- .../com/android/server/wm/DisplayContent.java | 25 +++++++++++++++++-- .../server/wm/ActivityRecordTests.java | 14 ++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a1620fe410c7..c66ff330edbd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1271,6 +1271,12 @@ class DisplayContent extends WindowContainer Date: Mon, 27 Apr 2020 18:51:27 +0800 Subject: [PATCH 028/166] Fix freeform window can't resize after maximize window and restore The freeform window mode would change to fullscreen if it maximzed window size, that could set the touch region crop to its stack bounds. And we should clear that after it restore to freeform window mode. Bug: 152248369 Test: enter freeform window mode, open an app and maximize it, restore and resize it. Change-Id: I7249a682ca3ed0f4ce89fc05413c8d24c8dc83de --- services/core/java/com/android/server/wm/WindowState.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a3387ab42ab3..f6473fd8acd1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3346,6 +3346,7 @@ class WindowState extends WindowContainer implements WindowManagerP final ActivityStack stack = task.getStack(); if (stack == null || inFreeformWindowingMode()) { + handle.setTouchableRegionCrop(null); return; } -- GitLab From 35895da39b2280a2ba8f7f8b2977c7fc6f0bf404 Mon Sep 17 00:00:00 2001 From: Will Brockman Date: Mon, 20 Apr 2020 15:11:49 -0400 Subject: [PATCH 029/166] Statsd TestDrive tool: Write output to stdout. Also: Option for less verbose output. Bug: 149914234 Test: atest --host test/com/android/statsd/shelltools/testdrive/TestDriveTest.java Test: out/host/linux-x86/bin/statsd_testdrive -terse -one 29 33 48 62 63 64 90 149 244 245 246 259 260 Change-Id: I4ed4cfdd85eb5382ce28846ed6ed81b4dd3f5334 --- .../shelltools/testdrive/TestDrive.java | 59 +++++++++++++++++-- .../shelltools/testdrive/TestDriveTest.java | 46 +++++++++++---- 2 files changed, 91 insertions(+), 14 deletions(-) diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index 8ac251e39a18..a97f132a2d5f 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -25,6 +25,7 @@ import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; import com.android.internal.os.StatsdConfigProto.StatsdConfig; import com.android.internal.os.StatsdConfigProto.TimeUnit; import com.android.os.AtomsProto.Atom; +import com.android.os.StatsLog; import com.android.os.StatsLog.ConfigMetricsReport; import com.android.os.StatsLog.ConfigMetricsReportList; import com.android.os.StatsLog.StatsLogReport; @@ -78,11 +79,12 @@ public class TestDrive { @VisibleForTesting String mDeviceSerial = null; + @VisibleForTesting + Dumper mDumper = new BasicDumper(); public static void main(String[] args) { final Configuration configuration = new Configuration(); - - TestDrive testDrive = new TestDrive(); + final TestDrive testDrive = new TestDrive(); Utils.setUpLogger(LOGGER, false); if (!testDrive.processArgs(configuration, args, @@ -94,7 +96,7 @@ public class TestDrive { configuration.createConfig(), configuration.hasPulledAtoms(), configuration.hasPushedAtoms()); if (reports != null) { - configuration.dumpMetrics(reports); + configuration.dumpMetrics(reports, testDrive.mDumper); } } @@ -116,6 +118,9 @@ public class TestDrive { if (remaining_args >= 2 && arg.equals("-one")) { LOGGER.info("Creating one event metric to catch all pushed atoms."); configuration.mOnePushedAtomEvent = true; + } else if (remaining_args >= 2 && arg.equals("-terse")) { + LOGGER.info("Terse output format."); + mDumper = new TerseDumper(); } else if (remaining_args >= 3 && arg.equals("-p")) { configuration.mAdditionalAllowedPackage = args[++first_arg]; } else if (remaining_args >= 3 && arg.equals("-s")) { @@ -198,12 +203,13 @@ public class TestDrive { String mAdditionalAllowedPackage = null; private final Set mTrackedMetrics = new HashSet<>(); - private void dumpMetrics(ConfigMetricsReportList reportList) { + private void dumpMetrics(ConfigMetricsReportList reportList, Dumper dumper) { // We may get multiple reports. Take the last one. ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1); for (StatsLogReport statsLog : report.getMetricsList()) { if (isTrackedMetric(statsLog.getMetricId())) { LOGGER.info(statsLog.toString()); + dumper.dump(statsLog); } } } @@ -341,6 +347,51 @@ public class TestDrive { } } + interface Dumper { + void dump(StatsLogReport report); + } + + static class BasicDumper implements Dumper { + @Override + public void dump(StatsLogReport report) { + System.out.println(report.toString()); + } + } + + static class TerseDumper extends BasicDumper { + @Override + public void dump(StatsLogReport report) { + if (report.hasGaugeMetrics()) { + dumpGaugeMetrics(report); + } + if (report.hasEventMetrics()) { + dumpEventMetrics(report); + } + } + void dumpEventMetrics(StatsLogReport report) { + final List data = report.getEventMetrics().getDataList(); + if (data.isEmpty()) { + return; + } + long firstTimestampNanos = data.get(0).getElapsedTimestampNanos(); + for (StatsLog.EventMetricData event : data) { + final double deltaSec = (event.getElapsedTimestampNanos() - firstTimestampNanos) + / 1e9; + System.out.println( + String.format("+%.3fs: %s", deltaSec, event.getAtom().toString())); + } + } + void dumpGaugeMetrics(StatsLogReport report) { + final List data = report.getGaugeMetrics().getDataList(); + if (data.isEmpty()) { + return; + } + for (StatsLog.GaugeMetricData gauge : data) { + System.out.println(gauge.toString()); + } + } + } + private static String pushConfig(StatsdConfig config, String deviceSerial) throws IOException, InterruptedException { File configFile = File.createTempFile("statsdconfig", ".config"); diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java index 9d986e7c09cf..363fac0c78ba 100644 --- a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java +++ b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java @@ -41,24 +41,21 @@ public class TestDriveTest { static class Expect { public boolean success; public Integer[] atoms; - public boolean onePushedAtomEvent; - public String extraPackage; + public boolean onePushedAtomEvent = false; + public String extraPackage = null; public String target; + public boolean terse = false; static Expect success(Integer... atoms) { - return new Expect(true, atoms, false, null, + return new Expect(true, atoms, TARGET); } - Expect(boolean success, Integer[] atoms, boolean onePushedAtomEvent, String extraPackage, - String target) { + Expect(boolean success, Integer[] atoms, String target) { this.success = success; this.atoms = atoms; - this.onePushedAtomEvent = onePushedAtomEvent; - this.extraPackage = extraPackage; this.target = target; } - static final Expect FAILURE = new Expect(false, null, - false, null, null); + static final Expect FAILURE = new Expect(false, null, null); Expect onePushedAtomEvent() { this.onePushedAtomEvent = true; return this; @@ -67,6 +64,10 @@ public class TestDriveTest { this.extraPackage = TestDriveTest.PACKAGE; return this; } + Expect terse() { + this.terse = true; + return this; + } } @Parameterized.Parameter(0) @@ -118,6 +119,10 @@ public class TestDriveTest { Expect.FAILURE}, // Two connected devices, no indication of which to use new Object[]{new String[]{"-one", "244", "245"}, TARGET_ONLY, null, Expect.success(244, 245).onePushedAtomEvent()}, + new Object[]{new String[]{"-terse", "-one", "244", "245"}, TARGET_ONLY, null, + Expect.success(244, 245).onePushedAtomEvent().terse()}, + new Object[]{new String[]{"-one", "-terse", "244", "245"}, TARGET_ONLY, null, + Expect.success(244, 245).onePushedAtomEvent().terse()}, new Object[]{new String[]{"-p", PACKAGE, "244", "245"}, TARGET_ONLY, null, Expect.success(244, 245).extraPackage()}, new Object[]{new String[]{"-p", PACKAGE, "-one", "244", "245"}, TARGET_ONLY, null, @@ -132,7 +137,23 @@ public class TestDriveTest { Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "244", "245"}, TARGET_AND_OTHER, null, - Expect.success(244, 245).extraPackage().onePushedAtomEvent()} + Expect.success(244, 245).extraPackage().onePushedAtomEvent()}, + new Object[]{new String[]{"-terse", "-one", "-p", PACKAGE, "-s", TARGET, + "244", "245"}, + TARGET_AND_OTHER, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, + new Object[]{new String[]{"-one", "-terse", "-p", PACKAGE, "-s", TARGET, + "244", "245"}, + TARGET_AND_OTHER, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, + new Object[]{new String[]{"-one", "-p", PACKAGE, "-terse", "-s", TARGET, + "244", "245"}, + TARGET_AND_OTHER, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}, + new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "-terse", + "244", "245"}, + TARGET_AND_OTHER, null, + Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()} ); } @@ -162,6 +183,11 @@ public class TestDriveTest { assertArrayEquals(mExpect.atoms, collectAtoms(mConfiguration)); assertEquals(mExpect.onePushedAtomEvent, mConfiguration.mOnePushedAtomEvent); assertEquals(mExpect.target, mTestDrive.mDeviceSerial); + if (mExpect.terse) { + assertEquals(TestDrive.TerseDumper.class, mTestDrive.mDumper.getClass()); + } else { + assertEquals(TestDrive.BasicDumper.class, mTestDrive.mDumper.getClass()); + } } else { assertFalse(result); } -- GitLab From c084789311ddaf7f8588cf0478532efa1ece2b92 Mon Sep 17 00:00:00 2001 From: Evan Laird Date: Fri, 24 Apr 2020 14:12:56 -0400 Subject: [PATCH 030/166] Allow for longer strings in priorty onboarding screen - Up the CHAR_LIMIT to 75 for priority onboarding strings - set maxlines to 2 - Update some paddings to make things look good when the text does wrap Test: manual Fixes: 154614037 Change-Id: I84a84077d1efe69de0611c3a970c45aca38d7c59 --- .../layout/priority_onboarding_half_shell.xml | 40 ++++++++++++------- packages/SystemUI/res/values/strings.xml | 8 ++-- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml index ccb4f7832a62..c27b3a9b3bf4 100644 --- a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml +++ b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml @@ -30,7 +30,6 @@ android:layout_width="@dimen/qs_panel_width" android:layout_height="wrap_content" android:paddingTop="16dp" - android:paddingBottom="16dp" android:paddingStart="16dp" android:paddingEnd="16dp" android:orientation="vertical" @@ -44,7 +43,10 @@ android:id="@+id/show_at_top_tip" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="4dp" + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:paddingStart="4dp" + android:paddingEnd="4dp" android:orientation="horizontal" > @@ -75,7 +77,10 @@ android:id="@+id/show_avatar_tip" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="4dp" + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:paddingStart="4dp" + android:paddingEnd="4dp" android:orientation="horizontal" > @@ -108,7 +113,10 @@ android:id="@+id/floating_bubble_tip" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="4dp" + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:paddingStart="4dp" + android:paddingEnd="4dp" android:orientation="horizontal" > @@ -123,13 +131,13 @@ @@ -140,7 +148,10 @@ android:id="@+id/ignore_dnd_tip" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="4dp" + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:paddingStart="4dp" + android:paddingEnd="4dp" android:orientation="horizontal" > @@ -155,13 +166,13 @@ @@ -173,7 +184,8 @@ android:id="@+id/button_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="4dp" + android:paddingStart="4dp" + android:paddingEnd="4dp" android:orientation="horizontal" > Standby - + Show at top of conversation section - + Show profile picture on lock screen - + Appear as a floating bubble on top of apps - + Interrupt Do Not Disturb Got it -- GitLab From 1e088f1ea5f6967b5dd4702e60fbe50ce5235c6d Mon Sep 17 00:00:00 2001 From: Bernardo Rufino Date: Tue, 28 Apr 2020 16:40:25 +0100 Subject: [PATCH 031/166] Add logging for BAL Most are build-time strippable and defaulted to off. One is a warning that would've enabled us to diagnose b/152621860. Expanded the already existent log in case an activity is blocked. Bug: 152621860 Test: Observe logcat Change-Id: Ieee8079a827c4e353987094d7d6d6faec5856c20 --- .../android/server/wm/ActivityStarter.java | 56 +++++++++++++++++++ .../wm/ActivityTaskManagerDebugConfig.java | 1 + .../server/wm/WindowProcessController.java | 34 +++++++++++ 3 files changed, 91 insertions(+) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index df53227e27fd..4ea990285269 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -61,6 +61,7 @@ import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; @@ -108,6 +109,7 @@ import android.os.UserManager; import android.service.voice.IVoiceInteractionSession; import android.text.TextUtils; import android.util.ArraySet; +import android.util.DebugUtils; import android.util.Pools.SynchronizedPool; import android.util.Slog; @@ -1195,6 +1197,9 @@ class ActivityStarter { final int callingAppId = UserHandle.getAppId(callingUid); if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID || callingAppId == Process.NFC_UID) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Activity start allowed for important callingUid (" + callingUid + ")"); + } return false; } @@ -1208,6 +1213,11 @@ class ActivityStarter { final boolean isCallingUidPersistentSystemProcess = callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid + + ", isCallingUidPersistentSystemProcess = " + + isCallingUidPersistentSystemProcess); + } return false; } // take realCallingUid into consideration @@ -1229,35 +1239,66 @@ class ActivityStarter { if (realCallingUid != callingUid) { // don't abort if the realCallingUid has a visible window if (realCallingUidHasAnyVisibleWindow) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid + + ") has visible (non-toast) window"); + } return false; } // if the realCallingUid is a persistent system process, abort if the IntentSender // wasn't whitelisted to start an activity if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid + + ") is persistent system process AND intent sender whitelisted " + + "(allowBackgroundActivityStart = true)"); + } return false; } // don't abort if the realCallingUid is an associated companion app if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid), realCallingUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid + + ") is companion app"); + } return false; } } // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, + "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND " + + "permission granted for uid " + + callingUid); + } return false; } // don't abort if the caller has the same uid as the recents component if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid + + ") is recents"); + } return false; } // don't abort if the callingUid is the device owner if (mService.isDeviceOwner(callingUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid + + ") is device owner"); + } return false; } // don't abort if the callingUid has companion device final int callingUserId = UserHandle.getUserId(callingUid); if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid + + ") is companion app"); + } return false; } // If we don't have callerApp at this point, no caller was provided to startActivity(). @@ -1273,6 +1314,10 @@ class ActivityStarter { if (callerApp != null) { // first check the original calling process if (callerApp.areBackgroundActivityStartsAllowed()) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Background activity start allowed: callerApp process (pid = " + + callerApp.getPid() + ", uid = " + callerAppUid + ") is whitelisted"); + } return false; } // only if that one wasn't whitelisted, check the other ones @@ -1282,6 +1327,11 @@ class ActivityStarter { for (int i = uidProcesses.size() - 1; i >= 0; i--) { final WindowProcessController proc = uidProcesses.valueAt(i); if (proc != callerApp && proc.areBackgroundActivityStartsAllowed()) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, + "Background activity start allowed: process " + proc.getPid() + + " from uid " + callerAppUid + " is whitelisted"); + } return false; } } @@ -1297,9 +1347,15 @@ class ActivityStarter { Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage + "; callingUid: " + callingUid + "; isCallingUidForeground: " + isCallingUidForeground + + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow + + "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class, + "PROCESS_STATE_", callingUidProcState) + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess + "; realCallingUid: " + realCallingUid + "; isRealCallingUidForeground: " + isRealCallingUidForeground + + "; realCallingUidHasAnyVisibleWindow: " + realCallingUidHasAnyVisibleWindow + + "; realCallingUidProcState: " + DebugUtils.valueToString(ActivityManager.class, + "PROCESS_STATE_", realCallingUidProcState) + "; isRealCallingUidPersistentSystemProcess: " + isRealCallingUidPersistentSystemProcess + "; originatingPendingIntent: " + originatingPendingIntent diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java index 7f09a071308a..da0bfd67e353 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java @@ -65,6 +65,7 @@ public class ActivityTaskManagerDebugConfig { static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false; static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false; static final boolean DEBUG_RESULTS = DEBUG_ALL || false; + static final boolean DEBUG_ACTIVITY_STARTS = DEBUG_ALL || false; public static final boolean DEBUG_CLEANUP = DEBUG_ALL || false; public static final boolean DEBUG_METRICS = DEBUG_ALL || false; diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 41bd70726e71..fe68cd6110f2 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -29,6 +29,7 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STARTED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; @@ -430,6 +431,11 @@ public class WindowProcessController extends ConfigurationContainer mAtm.getLastStopAppSwitchesTime() || mLastActivityFinishTime > mAtm.getLastStopAppSwitchesTime()) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "[WindowProcessController(" + mPid + + ")] Activity start allowed: within " + + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period"); + } return true; } + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "[WindowProcessController(" + mPid + ")] Activity start within " + + ACTIVITY_BG_START_GRACE_PERIOD_MS + + "ms grace period but also within stop app switch window"); + } + } // allow if the proc is instrumenting with background activity starts privs if (mInstrumentingWithBackgroundActivityStartPrivileges) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "[WindowProcessController(" + mPid + + ")] Activity start allowed: process instrumenting with background " + + "activity starts privileges"); + } return true; } // allow if the caller has an activity in any foreground task if (hasActivityInVisibleTask()) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "[WindowProcessController(" + mPid + + ")] Activity start allowed: process has activity in foreground task"); + } return true; } // allow if the caller is bound by a UID that's currently foreground if (isBoundByForegroundUid()) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "[WindowProcessController(" + mPid + + ")] Activity start allowed: process bound by foreground uid"); + } return true; } return false; -- GitLab From 128372810ddc47891b5cb93f6a61844bb474e425 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Mon, 27 Apr 2020 19:12:25 -0700 Subject: [PATCH 032/166] Give Divider back its touch-region Divider used to have a touch-region that allowed apps to receive touch events that would normally overlap the divider window as long as they weren't in the center. Re-introduce this by allowing windowless windows to pass through a touch-region. This region is always constrained to the window size to prevent security issues. Bug: 154840035 Test: Enter split and observe touch events away from center of divider. Change-Id: I85310c78beda7bd5574df78e2a83462e965d1154 --- core/java/android/view/IWindowSession.aidl | 2 +- .../android/view/SurfaceControlViewHost.java | 8 +++++ .../android/view/WindowlessWindowManager.java | 32 +++++++++++++++++-- .../systemui/stackdivider/DividerView.java | 17 +++++++++- .../stackdivider/DividerWindowManager.java | 9 ++++++ .../android/systemui/wm/SystemWindows.java | 27 ++++++++++++++++ .../java/com/android/server/wm/Session.java | 4 +-- .../server/wm/WindowManagerService.java | 16 +++++++--- 8 files changed, 104 insertions(+), 11 deletions(-) diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 25e81114d86f..926d8fc5a368 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -342,5 +342,5 @@ interface IWindowSession { * Update the flags on an input channel associated with a particular surface. */ void updateInputChannel(in IBinder channelToken, int displayId, in SurfaceControl surface, - int flags); + int flags, in Region region); } diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 3d6da6f71b3f..7ec008c901bc 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -233,6 +233,14 @@ public class SurfaceControlViewHost { return mViewRoot.mWindow; } + /** + * @return the WindowlessWindowManager instance that this host is attached to. + * @hide + */ + public @NonNull WindowlessWindowManager getWindowlessWM() { + return mWm; + } + /** * @hide */ diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 397bce44b25e..95d6d651cc79 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -16,16 +16,19 @@ package android.view; +import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.util.MergedConfiguration; import java.util.HashMap; +import java.util.Objects; /** * A simplistic implementation of IWindowSession. Rather than managing Surfaces @@ -42,6 +45,7 @@ public class WindowlessWindowManager implements IWindowSession { WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); int mDisplayId; IBinder mInputChannelToken; + Region mInputRegion; State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IBinder inputChannelToken) { mSurfaceControl = sc; @@ -95,6 +99,30 @@ public class WindowlessWindowManager implements IWindowSession { mResizeCompletionForWindow.put(window, callback); } + protected void setTouchRegion(IBinder window, @Nullable Region region) { + State state; + synchronized (this) { + // Do everything while locked so that we synchronize with relayout. This should be a + // very infrequent operation. + state = mStateForWindow.get(window); + if (state == null) { + return; + } + if (Objects.equals(region, state.mInputRegion)) { + return; + } + state.mInputRegion = region != null ? new Region(region) : null; + if (state.mInputChannelToken != null) { + try { + mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, + state.mSurfaceControl, state.mParams.flags, state.mInputRegion); + } catch (RemoteException e) { + Log.e(TAG, "Failed to update surface input channel: ", e); + } + } + } + } + /** * IWindowSession implementation. */ @@ -234,7 +262,7 @@ public class WindowlessWindowManager implements IWindowSession { && state.mInputChannelToken != null) { try { mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc, - attrs.flags); + attrs.flags, state.mInputRegion); } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); } @@ -409,7 +437,7 @@ public class WindowlessWindowManager implements IWindowSession { @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags) { + int flags, Region region) { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 2df450604d3b..060760a2f940 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -29,6 +29,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; +import android.graphics.Region; import android.graphics.Region.Op; import android.hardware.display.DisplayManager; import android.os.Bundle; @@ -338,12 +339,12 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); if (mFirstLayout) { // Wait for first layout so that the ViewRootImpl surface has been created. initializeSurfaceState(); mFirstLayout = false; } - super.onLayout(changed, left, top, right, bottom); int minimizeLeft = 0; int minimizeTop = 0; if (mDockSide == WindowManager.DOCKED_TOP) { @@ -783,6 +784,20 @@ public class DividerView extends FrameLayout implements OnTouchListener, setResizeDimLayer(t, false /* secondary */, 0.f /* alpha */); t.apply(); mTiles.releaseTransaction(t); + + // Get the actually-visible bar dimensions (relative to full window). This is a thin + // bar going through the center. + final Rect dividerBar = isHorizontalDivision() + ? new Rect(0, mDividerInsets, mSplitLayout.mDisplayLayout.width(), + mDividerInsets + mDividerSize) + : new Rect(mDividerInsets, 0, mDividerInsets + mDividerSize, + mSplitLayout.mDisplayLayout.height()); + final Region touchRegion = new Region(dividerBar); + // Add in the "draggable" portion. While not visible, this is an expanded area that the + // user can interact with. + touchRegion.union(new Rect(mHandle.getLeft(), mHandle.getTop(), + mHandle.getRight(), mHandle.getBottom())); + mWindowManager.setTouchRegion(touchRegion); } public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java index 729df3887915..6ea3132ac942 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import android.graphics.PixelFormat; +import android.graphics.Region; import android.os.Binder; import android.view.View; import android.view.WindowManager; @@ -103,4 +104,12 @@ public class DividerWindowManager { mSystemWindows.updateViewLayout(mView, mLp); } } + + /** Sets the touch region to `touchRegion`. Use null to unset.*/ + public void setTouchRegion(Region touchRegion) { + if (mView == null) { + return; + } + mSystemWindows.setTouchableRegion(mView, touchRegion); + } } diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java index 0b6e4b2ab598..64cac84ff24e 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java +++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java @@ -23,7 +23,9 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.os.Bundle; +import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.MergedConfiguration; @@ -138,6 +140,23 @@ public class SystemWindows { root.relayout((WindowManager.LayoutParams) params); } + /** + * Sets the touchable region of a view's window. This will be cropped to the window size. + * @param view + * @param region + */ + public void setTouchableRegion(@NonNull View view, Region region) { + SurfaceControlViewHost root = mViewRoots.get(view); + if (root == null) { + return; + } + WindowlessWindowManager wwm = root.getWindowlessWM(); + if (!(wwm instanceof SysUiWindowManager)) { + return; + } + ((SysUiWindowManager) wwm).setTouchableRegionForWindow(view, region); + } + /** * Adds a root for system-ui window management with no views. Only useful for IME. */ @@ -289,6 +308,14 @@ public class SystemWindows { SurfaceControl getSurfaceControlForWindow(View rootView) { return getSurfaceControl(rootView); } + + void setTouchableRegionForWindow(View rootView, Region region) { + IBinder token = rootView.getWindowToken(); + if (token == null) { + return; + } + setTouchRegion(token, region); + } } class ContainerWindow extends IWindow.Stub { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 56147f216e73..bf20cb907b71 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -679,10 +679,10 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags) { + int flags, Region region) { final long identity = Binder.clearCallingIdentity(); try { - mService.updateInputChannel(channelToken, displayId, surface, flags); + mService.updateInputChannel(channelToken, displayId, surface, flags, region); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 84d749f148fb..9c6e779dfa49 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8023,7 +8023,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, - name, applicationHandle, flags); + name, applicationHandle, flags, null /* region */); clientChannel.transferTo(outInputChannel); clientChannel.dispose(); @@ -8035,7 +8035,7 @@ public class WindowManagerService extends IWindowManager.Stub private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, int displayId, SurfaceControl surface, String name, - InputApplicationHandle applicationHandle, int flags) { + InputApplicationHandle applicationHandle, int flags, Region region) { InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); h.token = channelToken; h.name = name; @@ -8055,7 +8055,13 @@ public class WindowManagerService extends IWindowManager.Stub h.inputFeatures = 0; - h.replaceTouchableRegionWithCrop(null); + if (region == null) { + h.replaceTouchableRegionWithCrop(null); + } else { + h.touchableRegion.set(region); + h.replaceTouchableRegionWithCrop = false; + h.setTouchableRegionCrop(surface); + } SurfaceControl.Transaction t = mTransactionFactory.get(); t.setInputWindowInfo(surface, h); @@ -8069,7 +8075,7 @@ public class WindowManagerService extends IWindowManager.Stub * is undefined. */ void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, - int flags) { + int flags, Region region) { final InputApplicationHandle applicationHandle; final String name; final EmbeddedWindowController.EmbeddedWindow win; @@ -8084,7 +8090,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, - applicationHandle, flags); + applicationHandle, flags, region); } /** Return whether layer tracing is enabled */ -- GitLab From 72e144cd18094c70769b344d2cbb15f6f5a68f03 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 28 Apr 2020 10:53:40 -0700 Subject: [PATCH 033/166] Allow pre-R apps to test compressed ARSC error This change removes the initial target SDK check which prevents apps that target pre-Q from testing that they fail to be install if the compatibility change is enabled for pre-Q packages. Bug: 132742131 Test: adb shell am compat enable 132742131 for app with compressed ARSC that targets pre-R Change-Id: I7e568a9e99045c09565bb372e454f573b954fea5 --- .../android/content/pm/parsing/ParsingPackageUtils.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index c61362feaeae..770d08d3b84d 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -346,12 +346,11 @@ public class ParsingPackageUtils { } final ParsingPackage pkg = result.getResult(); - if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.R - && assets.containsAllocatedTable()) { + if (assets.containsAllocatedTable()) { final ParseResult deferResult = input.deferError( - "Targeting R+ (version" + Build.VERSION_CODES.R + " and above) requires the" - + " resources.arsc of installed APKs to be stored uncompressed and" - + " aligned on a 4-byte boundary", + "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires" + + " the resources.arsc of installed APKs to be stored uncompressed" + + " and aligned on a 4-byte boundary", DeferredError.RESOURCES_ARSC_COMPRESSED); if (deferResult.isError()) { return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED, -- GitLab From e16fffe6c1030053187a719fe7cba48be639f093 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 27 Apr 2020 17:59:29 -0700 Subject: [PATCH 034/166] Import translations. DO NOT MERGE Change-Id: Idcb6396ba8b7c30e3f2bef76736207aa2118a76b Auto-generated-cl: translation import --- core/res/res/values-af/strings.xml | 12 ++++++++++-- core/res/res/values-am/strings.xml | 12 ++++++++++-- core/res/res/values-ar/strings.xml | 12 ++++++++++-- core/res/res/values-as/strings.xml | 12 ++++++++++-- core/res/res/values-az/strings.xml | 12 ++++++++++-- core/res/res/values-b+sr+Latn/strings.xml | 12 ++++++++++-- core/res/res/values-be/strings.xml | 12 ++++++++++-- core/res/res/values-bg/strings.xml | 12 ++++++++++-- core/res/res/values-bn/strings.xml | 12 ++++++++++-- core/res/res/values-bs/strings.xml | 12 ++++++++++-- core/res/res/values-ca/strings.xml | 12 ++++++++++-- core/res/res/values-cs/strings.xml | 12 ++++++++++-- core/res/res/values-da/strings.xml | 16 ++++++++++++---- core/res/res/values-de/strings.xml | 12 ++++++++++-- core/res/res/values-el/strings.xml | 12 ++++++++++-- core/res/res/values-en-rAU/strings.xml | 12 ++++++++++-- core/res/res/values-en-rCA/strings.xml | 12 ++++++++++-- core/res/res/values-en-rGB/strings.xml | 12 ++++++++++-- core/res/res/values-en-rIN/strings.xml | 12 ++++++++++-- core/res/res/values-en-rXC/strings.xml | 12 ++++++++++-- core/res/res/values-es-rUS/strings.xml | 12 ++++++++++-- core/res/res/values-es/strings.xml | 12 ++++++++++-- core/res/res/values-et/strings.xml | 12 ++++++++++-- core/res/res/values-eu/strings.xml | 12 ++++++++++-- core/res/res/values-fa/strings.xml | 12 ++++++++++-- core/res/res/values-fi/strings.xml | 12 ++++++++++-- core/res/res/values-fr-rCA/strings.xml | 12 ++++++++++-- core/res/res/values-fr/strings.xml | 12 ++++++++++-- core/res/res/values-gl/strings.xml | 12 ++++++++++-- core/res/res/values-gu/strings.xml | 12 ++++++++++-- core/res/res/values-hi/strings.xml | 12 ++++++++++-- core/res/res/values-hr/strings.xml | 12 ++++++++++-- core/res/res/values-hu/strings.xml | 12 ++++++++++-- core/res/res/values-hy/strings.xml | 12 ++++++++++-- core/res/res/values-in/strings.xml | 12 ++++++++++-- core/res/res/values-is/strings.xml | 12 ++++++++++-- core/res/res/values-it/strings.xml | 12 ++++++++++-- core/res/res/values-iw/strings.xml | 12 ++++++++++-- core/res/res/values-ja/strings.xml | 12 ++++++++++-- core/res/res/values-ka/strings.xml | 12 ++++++++++-- core/res/res/values-kk/strings.xml | 12 ++++++++++-- core/res/res/values-km/strings.xml | 12 ++++++++++-- core/res/res/values-kn/strings.xml | 12 ++++++++++-- core/res/res/values-ko/strings.xml | 12 ++++++++++-- core/res/res/values-ky/strings.xml | 12 ++++++++++-- core/res/res/values-lo/strings.xml | 12 ++++++++++-- core/res/res/values-lt/strings.xml | 12 ++++++++++-- core/res/res/values-lv/strings.xml | 12 ++++++++++-- core/res/res/values-mk/strings.xml | 12 ++++++++++-- core/res/res/values-ml/strings.xml | 12 ++++++++++-- core/res/res/values-mn/strings.xml | 12 ++++++++++-- core/res/res/values-mr/strings.xml | 12 ++++++++++-- core/res/res/values-ms/strings.xml | 12 ++++++++++-- core/res/res/values-my/strings.xml | 12 ++++++++++-- core/res/res/values-nb/strings.xml | 12 ++++++++++-- core/res/res/values-ne/strings.xml | 12 ++++++++++-- core/res/res/values-nl/strings.xml | 12 ++++++++++-- core/res/res/values-or/strings.xml | 14 +++++++++++--- core/res/res/values-pa/strings.xml | 12 ++++++++++-- core/res/res/values-pl/strings.xml | 12 ++++++++++-- core/res/res/values-pt-rBR/strings.xml | 12 ++++++++++-- core/res/res/values-pt-rPT/strings.xml | 12 ++++++++++-- core/res/res/values-pt/strings.xml | 12 ++++++++++-- core/res/res/values-ro/strings.xml | 12 ++++++++++-- core/res/res/values-ru/strings.xml | 12 ++++++++++-- core/res/res/values-si/strings.xml | 12 ++++++++++-- core/res/res/values-sk/strings.xml | 12 ++++++++++-- core/res/res/values-sl/strings.xml | 12 ++++++++++-- core/res/res/values-sq/strings.xml | 12 ++++++++++-- core/res/res/values-sr/strings.xml | 12 ++++++++++-- core/res/res/values-sv/strings.xml | 12 ++++++++++-- core/res/res/values-sw/strings.xml | 12 ++++++++++-- core/res/res/values-ta/strings.xml | 12 ++++++++++-- core/res/res/values-te/strings.xml | 12 ++++++++++-- core/res/res/values-th/strings.xml | 12 ++++++++++-- core/res/res/values-tl/strings.xml | 12 ++++++++++-- core/res/res/values-tr/strings.xml | 12 ++++++++++-- core/res/res/values-uk/strings.xml | 12 ++++++++++-- core/res/res/values-ur/strings.xml | 12 ++++++++++-- core/res/res/values-uz/strings.xml | 12 ++++++++++-- core/res/res/values-vi/strings.xml | 12 ++++++++++-- core/res/res/values-zh-rCN/strings.xml | 12 ++++++++++-- core/res/res/values-zh-rHK/strings.xml | 12 ++++++++++-- core/res/res/values-zh-rTW/strings.xml | 12 ++++++++++-- core/res/res/values-zu/strings.xml | 12 ++++++++++-- 85 files changed, 853 insertions(+), 173 deletions(-) diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 9d152aaaa582..b0ee1c85f585 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1631,6 +1631,10 @@ "As jy albei volumesleutels vir \'n paar sekondes hou, skakel dit %1$s, \'n toeganklikheidkenmerk, aan. Dit kan verander hoe jou toestel werk.\n\nJy kan hierdie kortpad na \'n ander kenmerk in Instellings en Toeganklikheid verander." "Skakel aan" "Moenie aanskakel nie" + + + + "Gee %1$s volle beheer oor jou toestel?" "As jy %1$s aanskakel, sal jou toestel nie jou skermslot gebruik om data-enkripsie te verbeter nie." "Volle beheer is gepas vir programme wat jou help met toeganklikheidsbehoeftes, maar nie vir die meeste programme nie." @@ -2038,13 +2042,17 @@ "Kennisgewings" "Kitsinstellings" "Kragdialoog" - "Wissel verdeelde skerm" "Sluitskerm" "Skermkiekie" - "Toeganklikheid-kieslys" + + + + "%1$s se onderskrifbalk." "%1$s is in die BEPERK-groep geplaas" "%1$s:" + + "Gesprek" "Groepsgesprek" "%1$d+" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index af6e15f57a70..d48726919903 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1631,6 +1631,10 @@ "ሁለቱንም የድምፅ ቁልፎች ወደ ታች ለጥቂት ሰከንዶች መያዝ የተደራሽነት ባሕሪያትን %1$s ያበራል። ይህ የእርስዎ መሣሪያ እንዴት እንደሚሠራ ሊለውጥ ይችላል።\n\nበቅንብሮች > ተደራሽነት ውስጥ ወደ ሌላ ባሕሪ ይህን አቋራጭ መለወጥ ይችላሉ።" "አብራ" "አታብራ" + + + + "%1$s ሙሉ የመሣሪያዎ ቁጥጥር እንዲኖረው ይፈቀድለት?" "%1$sን ካበሩት መሳሪያዎ የውሂብ ምስጠራን ለማላቅ የማያ ገጽ መቆለፊያዎን አይጠቀምም።" "ሙሉ ቁጥጥር ከተደራሽነት ፍላጎቶች ጋር እርስዎን ለሚያግዝዎት መተግበሪያዎች ተገቢ ነው ሆኖም ግን ለአብዛኛዎቹ መተግበሪያዎች አይሆንም።" @@ -2038,13 +2042,17 @@ "ማሳወቂያዎች" "ፈጣን ቅንብሮች" "የኃይል መገናኛ" - "የተከፈለ ማያን ቀያይር" "የማያ ገጽ ቁልፍ" "ቅጽበታዊ ገጽ እይታ" - "የተደራሽነት ምናሌ" + + + + "የ%1$s የሥዕል ገላጭ ጽሑፍ አሞሌ።" "%1$s ወደ የRESTRICTED ባልዲ ተከትቷል" "%1$s፦" + + "ውይይት" "የቡድን ውይይት" "%1$d+" diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index dfd729e1b204..051aa964d4f4 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1719,6 +1719,10 @@ "‏يؤدي الضغط مع الاستمرار لبضع ثوانٍ على كلا مفتاحَي التحكّم في مستوى الصوت إلى تفعيل %1$s وهي إحدى ميزات إمكانية الوصول. يمكن أن يؤدي هذا الإجراء إلى تغيير كيفية عمل جهازك.\n\nيمكنك تغيير هذا الاختصار لاستخدامه مع ميزة أخرى في الإعدادات > أدوات تمكين الوصول." "تفعيل" "عدم التفعيل" + + + + "هل تريد السماح لخدمة %1$s بالتحكّم الكامل في جهازك؟" "في حال تفعيل %1$s، لن يستخدم جهازك ميزة قفل الشاشة لتحسين ترميز البيانات." "إنّ التحكّم الكامل ليس ملائمًا لمعظم التطبيقات، باستثناء التطبيقات المعنية بسهولة الاستخدام." @@ -2174,13 +2178,17 @@ "الإشعارات" "الإعدادات السريعة" "مربّع حوار الطاقة" - "تبديل \"تقسيم الشاشة\"" "شاشة القفل" "لقطة شاشة" - "قائمة \"سهولة الاستخدام\"" + + + + "شريط الشرح لتطبيق %1$s." "تم وضع %1$s في الحزمة \"محظورة\"." "%1$s:" + + "محادثة" "محادثة جماعية" "%1$d+" diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index ecf0394a20d2..755361a81942 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1631,6 +1631,10 @@ "দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা %1$s অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।" "অন কৰক" "অন নকৰিব" + + + + "%1$sক আপোনাৰ ডিভাইচটোৰ সম্পূর্ণ নিয়ন্ত্ৰণ দিবনে?" "যদি আপুনি %1$s অন কৰে, তেন্তে আপোনাৰ ডিভাইচটোৱে ডেটা এনক্ৰিপশ্বনৰ গুণগত মান উন্নত কৰিবলৈ স্ক্ৰীন লক ব্যৱহাৰ নকৰে।" "আপোনাক সাধ্য সুবিধাৰ প্ৰয়োজনসমূহৰ জৰিয়তে সহায় কৰা এপ্‌সমূহৰ বাবে সম্পূর্ণ নিয়ন্ত্ৰণৰ সুবিধাটো সঠিক যদিও অধিকাংশ এপৰ বাবে এয়া সঠিক নহয়।" @@ -2038,13 +2042,17 @@ "জাননীসমূহ" "ক্ষিপ্ৰ ছেটিংসমূহ" "পাৱাৰ ডায়লগ" - "বিভাজিত স্ক্ৰীন ট’গল কৰক" "লক স্ক্ৰীন" "স্ক্ৰীণশ্বট" - "সাধ্য সুবিধাৰ মেনু" + + + + "%1$sৰ কেপশ্বন বাৰ।" "%1$sক সীমাবদ্ধ বাকেটটোত ৰখা হৈছে" "%1$s:" + + "বাৰ্তালাপ" "গোটত কৰা বাৰ্তালাপ" "%1$d+" diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 1e633d390212..049a84612177 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -1631,6 +1631,10 @@ "Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyası olan %1$s aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nAyarlar və Əlçatımlılıq bölməsində bu qısayolu başqa bir funksiyata dəyişə bilərsiniz." "Aktiv edin" "Aktiv etməyin" + + + + "%1$s xidmətinin cihaza tam nəzarət etməsinə icazə verilsin?" "%1$s aktiv olarsa, cihazınız data şifrələnməsini genişləndirmək üçün ekran kilidini istifadə etməyəcək." "Tam nəzarət əlçatımlılıq ehtiyaclarınızı ödəyən bəzi tətbiqlər üçün uyğundur." @@ -2038,13 +2042,17 @@ "Bildirişlər" "Sürətli Ayarlar" "Yandırıb-söndürmə dialoqu" - "Bölünmüş Ekrana keçid" "Kilid Ekranı" "Ekran şəkli" - "Əlçatımlılıq Menyusu" + + + + "%1$s başlıq paneli." "%1$s MƏHDUDLAŞDIRILMIŞ səbətinə yerləşdirilib" "%1$s:" + + "Söhbət" "Qrup Söhbəti" "%1$d+" diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index abcecb7b9e93..181871fe8653 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -1653,6 +1653,10 @@ "Ako zadržite oba tastera za jačinu zvuka par sekundi, uključuje se %1$s, funkcija pristupačnosti. To može da promeni način rada uređaja.\n\nMožete da promenite funkciju na koju se odnosi ova prečica u odeljku Podešavanja > Pristupačnost." "Uključi" "Ne uključuj" + + + + "Želite li da dozvolite da usluga %1$s ima potpunu kontrolu nad uređajem?" "Ako uključite uslugu %1$s, uređaj neće koristiti zaključavanje ekrana da bi poboljšao šifrovanje podataka." "Potpuna kontrola je primerena za aplikacije koje vam pomažu kod usluga pristupačnosti, ali ne i za većinu aplikacija." @@ -2072,13 +2076,17 @@ "Obaveštenja" "Brza podešavanja" "Dijalog napajanja" - "Uključite/isključite podeljeni ekran" "Zaključani ekran" "Snimak ekrana" - "Meni Pristupačnost" + + + + "Traka sa naslovima aplikacije %1$s." "Paket %1$s je dodat u segment OGRANIČENO" "%1$s:" + + "Konverzacija" "Grupna konverzacija" "%1$d+" diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index eb2f01bc6be1..306a25a029bc 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1675,6 +1675,10 @@ "Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае службу \"%1$s\", якая з\'яўляецца спецыяльнай магчымасцю. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nВы можаце задаць гэта спалучэнне клавіш для іншай функцыі ў меню \"Налады > Спецыяльныя магчымасці\"." "Уключыць" "Не ўключаць" + + + + "Дазволіць сэрвісу \"%1$s\" мець поўны кантроль над вашай прыладай?" "Калі вы ўключыце сэрвіс \"%1$s\", на прыладзе не будзе выкарыстоўвацца блакіроўка экрана для паляпшэння шыфравання даных." "Поўны кантроль прызначаны для сэрвісаў спецыяльных магчымасцей, аднак не падыходзіць для большасці праграм." @@ -2106,13 +2110,17 @@ "Апавяшчэнні" "Хуткія налады" "Дыялогавае акно сілкавання" - "Пераключальнік падзеленага экрана" "Экран блакіроўкі" "Здымак экрана" - "Меню спецыяльных магчымасцей" + + + + "Панэль субцітраў праграмы \"%1$s\"." "Пакет \"%1$s\" дададзены ў АБМЕЖАВАНУЮ групу" "%1$s:" + + "Размова" "Групавая размова" "%1$d+" diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 9e73d422eb6f..91f76cd22f8b 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1631,6 +1631,10 @@ "Натиснете двата бутона за силата на звука и ги задръжте за няколко секунди, за да включите функцията за достъпност %1$s. Това може да промени начина, по който работи устройството ви.\n\nМожете да зададете друга функция за този пряк път от „Настройки“ > „Достъпност“." "Включване" "Без включване" + + + + "Искате ли да разрешите на %1$s да има пълен контрол над устройството ви?" "Ако включите %1$s, устройството ви няма да подобрява шифроването на данни посредством опцията ви за заключване на екрана." "Пълният контрол е подходящ за приложенията, които помагат на потребителите със специални нужди, но не и за повечето приложения." @@ -2038,13 +2042,17 @@ "Известия" "Бързи настройки" "Диалогов прозорец за захранването" - "Превключване на разделения екран" "Заключен екран" "Екранна снимка" - "Меню за достъпност" + + + + "Лента за надписи на %1$s." "Пакетът %1$s е поставен в ОГРАНИЧЕНИЯ контейнер" "%1$s:" + + "Разговор" "Групов разговор" "Над %1$d" diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 9f064a2eca7a..9ad8c8195eed 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1631,6 +1631,10 @@ "উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে %1$s চালু হয়ে যাবে। এটি একটি অ্যাক্সেসিবিলিটি ফিচার। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nসেটিংস > অ্যাক্সেসিবিলিটি থেকে আপনি এই শর্টকাট পরিবর্তন করতে পারবেন।" "চালু করুন" "চালু করবেন না" + + + + "%1$s অ্যাপটিকে আপনার ডিভাইসে সম্পূর্ণ নিয়ন্ত্রণের অনুমতি দিতে চান?" "%1$s চালু করলে, ডেটা এনক্রিপশন উন্নত করার উদ্দেশ্যে আপনার ডিভাইস স্ক্রিন লক ব্যবহার করবে না।" "যে অ্যাপগুলি আপনাকে অ্যাক্সেসিবিলিটির প্রয়োজন মেটাতে সাহায্য করে সেই অ্যাপগুলির জন্য সম্পূর্ণ নিয়ন্ত্রণের বিষয়টি উপযুক্ত, কিন্তু তা বলে সমস্ত অ্যাপের জন্য নয়।" @@ -2038,13 +2042,17 @@ "বিজ্ঞপ্তি" "দ্রুত সেটিংস" "পাওয়ার ডায়লগ" - "স্প্লিট স্ক্রিন টগল করুন" "লক স্ক্রিন" "স্ক্রিনশট" - "অ্যাক্সেসিবিলিটি মেনু" + + + + "%1$s-এর ক্যাপশন বার।" "%1$s সীমাবদ্ধ গ্রুপে অন্তর্ভুক্ত করা হয়েছে" "%1$s:" + + "কথোপকথন" "গ্রুপ কথোপকথন" "%1$d+" diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index d4fee2ef051f..ab305ba52f39 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1655,6 +1655,10 @@ "Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkciju pristupačnosti %1$s. Ovo može promijeniti način rada uređaja.\n\nOvu prečicu možete zamijeniti drugom funkcijom u odjeljku Postavke > Pristupačnost." "Uključi" "Nemoj uključivati" + + + + "Dozvoliti da usluga %1$s ima punu kontrolu nad vašim uređajem?" "Ako uključite uslugu %1$s, uređaj neće koristiti zaključavanje ekrana za poboljšanje šifriranja podataka." "Puna kontrola je prikladna za aplikacije koje vam pomažu kod potreba za pristupačnosti, ali nije za većinu aplikacija." @@ -2074,13 +2078,17 @@ "Obavještenja" "Brze postavke" "Dijaloški okvir za napajanje" - "Uključi/isključi podijeljeni ekran" "Zaključavanje ekrana" "Snimak ekrana" - "Meni za pristupačnost" + + + + "Traka za natpis aplikacije %1$s." "Paket %1$s je stavljen u odjeljak OGRANIČENO" "%1$s:" + + "Razgovor" "Grupni razgovor" "%1$d+" diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index d7211bd3b954..8cbd3ec88c49 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1631,6 +1631,10 @@ "Si mantens premudes les dues tecles de volum durant uns segons, la funció d\'accessibilitat %1$s s\'activarà. Això podria canviar el funcionament del teu dispositiu.\n\nPots canviar la funció d\'aquesta drecera a Configuració > Accessibilitat." "Activa" "No activis" + + + + "Vols permetre que %1$s controli el teu dispositiu per complet?" "Si actives %1$s, el dispositiu no farà servir el bloqueig de pantalla per millorar l\'encriptació de dades." "El control total és adequat per a les aplicacions que t\'ajuden amb l\'accessibilitat, però no per a la majoria de les aplicacions." @@ -2038,13 +2042,17 @@ "Notificacions" "Configuració ràpida" "Quadre de diàleg d\'engegada" - "Commuta Pantalla dividida" "Pantalla de bloqueig" "Captura de pantalla" - "Menú d\'accessibilitat" + + + + "Barra de títol de l\'aplicació %1$s." "%1$s s\'ha transferit al segment RESTRINGIT" "%1$s:" + + "Conversa" "Conversa de grup" "%1$d+" diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 8de20cbd1943..3f57bbf92f03 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1675,6 +1675,10 @@ "Podržením obou tlačítek hlasitosti po dobu několika sekund zapnete funkci pro usnadnění přístupu %1$s. Tato funkce může změnit fungování zařízení.\n\nZkratku můžete nastavit na jinou funkci v Nastavení > Přístupnost." "Zapnout" "Nezapínat" + + + + "Chcete službě %1$s povolit, aby nad vaším zařízením měla plnou kontrolu?" "Pokud zapnete službu %1$s, zařízení nebude používat zámek obrazovky k vylepšení šifrování dat." "Plná kontrola je vhodná u aplikací, které vám pomáhají s usnadněním přístupu, nikoli u většiny aplikací." @@ -2106,13 +2110,17 @@ "Oznámení" "Rychlé nastavení" "Dialogové okno k napájení" - "Přepnout rozdělenou obrazovku" "Obrazovka uzamčení" "Snímek obrazovky" - "Nabídka usnadnění přístupu" + + + + "Popisek aplikace %1$s." "Balíček %1$s byl vložen do sekce OMEZENO" "%1$s:" + + "Konverzace" "Skupinová konverzace" "%1$d+" diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 2c135fbdf124..2a083867a528 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1631,6 +1631,10 @@ "Hvis du holder begge lydstyrkeknapperne nede i et par sekunder, aktiveres hjælpefunktionen %1$s. Det kan ændre på, hvordan din enhed fungerer.\n\nDu kan ændre denne genvej til en anden funktion i Indstillinger > Hjælpefunktioner." "Aktivér" "Aktivér ikke" + + + + "Vil du give %1$s fuld kontrol over din enhed?" "Hvis du aktiverer %1$s, vil enheden ikke benytte skærmlåsen til at forbedre datakrypteringen." "Fuld kontrol er velegnet til apps, der hjælper dig med hjælpefunktioner, men ikke de fleste apps." @@ -1795,8 +1799,8 @@ "Batterisparefunktionen gør følgende for at spare på batteriet:\n·Aktiverer Mørkt tema\n·Deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og andre funktioner som f.eks. \"Hey Google\"\n\n""Få flere oplysninger" "Batterisparefunktionen gør følgende for at spare på batteriet:\n·Aktiverer Mørkt tema\n·Deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og andre funktioner som f.eks. \"Hey Google\"" "Datasparefunktionen forhindrer nogle apps i at sende eller modtage data i baggrunden for at reducere dataforbruget. En app, der er i brug, kan få adgang til data, men gør det måske ikke så ofte. Dette kan f.eks. betyde, at billeder ikke vises, før du trykker på dem." - "Vil du slå Datasparefunktion til?" - "Slå til" + "Vil du aktivere Datasparefunktion?" + "Aktivér" I %1$d minutter (indtil %2$s) I %1$d minutter (indtil %2$s) @@ -2038,13 +2042,17 @@ "Notifikationer" "Kvikmenu" "Dialogboks om strøm" - "Slå Opdelt skærm til eller fra" "Låseskærm" "Screenshot" - "Menuen Hjælpefunktioner" + + + + "Titellinje for %1$s." "%1$s er blevet placeret i samlingen BEGRÆNSET" "%1$s:" + + "Samtale" "Gruppesamtale" "%1$d+" diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 3567a96b8dde..fe344a1c745b 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1631,6 +1631,10 @@ "Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfe \"%1$s\". Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nUnter \"Einstellungen > \"Bedienungshilfen\" kannst du dieser Verknüpfung eine andere Funktion zuweisen." "Aktivieren" "Nicht aktivieren" + + + + "%1$s die vollständige Kontrolle über dein Gerät geben?" "Wenn du %1$s aktivierst, verwendet dein Gerät nicht die Displaysperre, um die Datenverschlüsselung zu verbessern." "Die vollständige Kontrolle sollte nur für die Apps aktiviert werden, die dir den Zugang zu den App-Funktionen erleichtern. Das ist in der Regel nur ein kleiner Teil der Apps." @@ -2038,13 +2042,17 @@ "Benachrichtigungen" "Schnelleinstellungen" "Kleines Fenster für Akkustand" - "\"Bildschirm teilen\" ein-/ausschalten" "Sperrbildschirm" "Screenshot" - "Menü \"Bedienungshilfen\"" + + + + "Untertitelleiste von %1$s." "%1$s wurde in den BESCHRÄNKT-Bucket gelegt" "%1$s:" + + "Unterhaltung" "Gruppenunterhaltung" "%1$d+" diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 05b5e5281f7d..7caaa67a52df 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1631,6 +1631,10 @@ "Μπορείτε να ενεργοποιήσετε τη λειτουργία %1$s, η οποία είναι μία από τις λειτουργίες προσβασιμότητας, πατώντας παρατεταμένα ταυτόχρονα τα δύο πλήκτρα έντασης ήχου για μερικά δευτερόλεπτα. Αυτό ενδέχεται να αλλάξει τον τρόπο λειτουργίας της συσκευής σας.\n\nΜπορείτε να αλλάξετε αυτή τη συντόμευση σε μια άλλη λειτουργία στις Ρυθμίσεις > Προσβασιμότητα." "Ενεργοποίηση" "Να μην ενεργοποιηθούν" + + + + "Να επιτρέπεται στην υπηρεσία %1$s να έχει τον πλήρη έλεγχο της συσκευής σας;" "Εάν ενεργοποιήσετε την υπηρεσία %1$s, η συσκευή σας δεν θα χρησιμοποιεί το κλείδωμα οθόνης για τη βελτίωση της κρυπτογράφησης δεδομένων." "Ο πλήρης έλεγχος είναι κατάλληλος για εφαρμογές που εξυπηρετούν τις ανάγκες προσβασιμότητάς σας, αλλά όχι για όλες τις εφαρμογές." @@ -2038,13 +2042,17 @@ "Ειδοποιήσεις" "Γρήγορες ρυθμίσεις" "Παράθυρο διαλόγου λειτουργίας συσκευής" - "Εναλλαγή διαχωρισμού οθόνης" "Οθόνη κλειδώματος" "Στιγμιότυπο οθόνης" - "Μενού προσβασιμότητας" + + + + "Γραμμή υποτίτλων για την εφαρμογή %1$s." "Το πακέτο %1$s τοποθετήθηκε στον κάδο ΠΕΡΙΟΡΙΣΜΕΝΗΣ ΠΡΟΣΒΑΣΗΣ." "%1$s:" + + "Συνομιλία" "Ομαδική συνομιλία" "%1$d+" diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index d7047c57a238..13c08206bb5d 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -1631,6 +1631,10 @@ "Holding down both volume keys for a few seconds turns on %1$s, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility." "Turn on" "Don’t turn on" + + + + "Allow %1$s to have full control of your device?" "If you turn on %1$s, your device won’t use your screen lock to enhance data encryption." "Full control is appropriate for apps that help you with accessibility needs, but not for most apps." @@ -2038,13 +2042,17 @@ "Notifications" "Quick Settings" "Power Dialogue" - "Toggle Split Screen" "Lock Screen" "Screenshot" - "Accessibility menu" + + + + "Caption bar of %1$s." "%1$s has been put into the RESTRICTED bucket" "%1$s:" + + "Conversation" "Group conversation" "%1$d+" diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index a7ab4275e89a..48ff9f3851fc 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1631,6 +1631,10 @@ "Holding down both volume keys for a few seconds turns on %1$s, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility." "Turn on" "Don’t turn on" + + + + "Allow %1$s to have full control of your device?" "If you turn on %1$s, your device won’t use your screen lock to enhance data encryption." "Full control is appropriate for apps that help you with accessibility needs, but not for most apps." @@ -2038,13 +2042,17 @@ "Notifications" "Quick Settings" "Power Dialogue" - "Toggle Split Screen" "Lock Screen" "Screenshot" - "Accessibility menu" + + + + "Caption bar of %1$s." "%1$s has been put into the RESTRICTED bucket" "%1$s:" + + "Conversation" "Group conversation" "%1$d+" diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index d7047c57a238..13c08206bb5d 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1631,6 +1631,10 @@ "Holding down both volume keys for a few seconds turns on %1$s, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility." "Turn on" "Don’t turn on" + + + + "Allow %1$s to have full control of your device?" "If you turn on %1$s, your device won’t use your screen lock to enhance data encryption." "Full control is appropriate for apps that help you with accessibility needs, but not for most apps." @@ -2038,13 +2042,17 @@ "Notifications" "Quick Settings" "Power Dialogue" - "Toggle Split Screen" "Lock Screen" "Screenshot" - "Accessibility menu" + + + + "Caption bar of %1$s." "%1$s has been put into the RESTRICTED bucket" "%1$s:" + + "Conversation" "Group conversation" "%1$d+" diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index d7047c57a238..13c08206bb5d 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -1631,6 +1631,10 @@ "Holding down both volume keys for a few seconds turns on %1$s, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility." "Turn on" "Don’t turn on" + + + + "Allow %1$s to have full control of your device?" "If you turn on %1$s, your device won’t use your screen lock to enhance data encryption." "Full control is appropriate for apps that help you with accessibility needs, but not for most apps." @@ -2038,13 +2042,17 @@ "Notifications" "Quick Settings" "Power Dialogue" - "Toggle Split Screen" "Lock Screen" "Screenshot" - "Accessibility menu" + + + + "Caption bar of %1$s." "%1$s has been put into the RESTRICTED bucket" "%1$s:" + + "Conversation" "Group conversation" "%1$d+" diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 77d2f3160bbc..67a0381f100b 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -1631,6 +1631,10 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎Holding down both volume keys for a few seconds turns on ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎, an accessibility feature. This may change how your device works.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎You can change this shortcut to another feature in Settings > Accessibility.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎Turn on‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‎Don’t turn on‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎Allow ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ to have full control of your device?‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎If you turn on ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎, your device won’t use your screen lock to enhance data encryption.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‎‏‎Full control is appropriate for apps that help you with accessibility needs, but not for most apps.‎‏‎‎‏‎" @@ -2038,13 +2042,17 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‎Notifications‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‏‎Quick Settings‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎Power Dialog‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎Toggle Split Screen‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎Lock Screen‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎Screenshot‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎Accessibility Menu‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎Caption bar of ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ has been put into the RESTRICTED bucket‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎:‎‏‎‎‏‎" + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‎Conversation‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎Group Conversation‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎%1$d‎‏‎‎‏‏‏‎+‎‏‎‎‏‎" diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 0fc7ec58523b..6c0ebbddb345 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1631,6 +1631,10 @@ "Si mantienes presionadas ambas teclas de volumen durante unos segundos, se activará la función de accesibilidad %1$s. Esto podría cambiar la forma en que funciona tu dispositivo.\n\nPuedes cambiar este acceso directo a otra función en Configuración > Accesibilidad." "Activar" "No activar" + + + + "¿Deseas permitir que %1$s tenga el control total del dispositivo?" "Si activas %1$s, el dispositivo no utilizará el bloqueo de pantalla para mejorar la encriptación de datos." "El control total es apropiado para las apps que te ayudan con las necesidades de accesibilidad, pero no para la mayoría de las apps." @@ -2038,13 +2042,17 @@ "Notificaciones" "Configuración rápida" "Diálogo de encendido" - "Activar o desactivar pantalla dividida" "Bloquear pantalla" "Captura de pantalla" - "Menú de accesibilidad" + + + + "Barra de subtítulos de %1$s." "Se colocó %1$s en el depósito RESTRICTED" "%1$s:" + + "Conversación" "Conversación en grupo" "+%1$d" diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index b923564be9b9..541ca8a7b04d 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1631,6 +1631,10 @@ "Al mantener pulsadas ambas teclas de volumen durante unos segundos se activa %1$s, una función de accesibilidad. Esta función puede modificar el funcionamiento del dispositivo.\n\nPuedes asignar este acceso directo a otra función en Ajustes > Accesibilidad." "Activar" "No activar" + + + + "¿Permitir que %1$s pueda controlar totalmente tu dispositivo?" "Si activas %1$s, el dispositivo no utilizará el bloqueo de pantalla para mejorar el cifrado de datos." "El control total es adecuado para las aplicaciones de accesibilidad, pero no para la mayoría de las aplicaciones." @@ -2038,13 +2042,17 @@ "Notificaciones" "Ajustes rápidos" "Abrir cuadro de diálogo" - "Activar o desactivar la pantalla dividida" "Pantalla de bloqueo" "Captura de pantalla" - "Menú de accesibilidad" + + + + "Barra de subtítulos de %1$s." "%1$s se ha incluido en el grupo de restringidos" "%1$s:" + + "Conversación" "Conversación de grupo" "+ %1$d" diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 518aa60d2bb1..e36be2e3ec49 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1631,6 +1631,10 @@ "Kui hoiate mõlemat helitugevuse nuppu mõni sekund all, lülitatakse sisse juurdepääsufunktsioon %1$s. See võib teie seadme tööviisi muuta.\n\nSelle otsetee saab asendada muu otseteega jaotises Seaded > Juurdepääsetavus." "Lülita sisse" "Ära lülita sisse" + + + + "Kas anda teenusele %1$s teie seadme üle täielik kontroll?" "Kui lülitate sisse teenuse %1$s, ei kasuta seade andmete krüpteerimise täiustamiseks ekraanilukku." "Täielik haldusõigus sobib rakendustele, mis pakuvad juurdepääsufunktsioone. Enamiku rakenduste puhul seda ei soovitata." @@ -2038,13 +2042,17 @@ "Märguanded" "Kiirseaded" "Energiasäästja dialoog" - "Vaheta jagatud ekraanikuva" "Lukustuskuva" "Ekraanipilt" - "Juurdepääsetavuse menüü" + + + + "Rakenduse %1$s pealkirjariba." "%1$s on lisatud salve PIIRANGUTEGA" "%1$s:" + + "Vestlus" "Grupivestlus" "%1$d+" diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index a7777fd0270b..10b3970cf0e7 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1631,6 +1631,10 @@ "Eduki sakatuta bolumen-teklak segundo batzuez %1$s izeneko erabilerraztasun-eginbidea aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nLasterbide hau beste eginbide batengatik aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera." "Aktibatu" "Ez aktibatu" + + + + "Gailua guztiz kontrolatzeko baimena eman nahi diozu %1$s zerbitzuari?" "%1$s aktibatzen baduzu, gailuak ez du pantailaren blokeoa erabiliko datuen enkriptatzea hobetzeko." "Erabilerraztasun-beharrak asetzen dituzten aplikazioetan da egokia kontrol osoa, baina ez aplikazio gehienetan." @@ -2038,13 +2042,17 @@ "Jakinarazpenak" "Ezarpen bizkorrak" "Piztu edo itzaltzeko leihoa" - "Aktibatu/Desaktibatu pantaila zatitua" "Pantaila blokeatua" "Pantaila-argazkia" - "Erabilerraztasun-menua" + + + + "%1$s aplikazioko azpitituluen barra." "Murriztuen edukiontzian ezarri da %1$s" "%1$s:" + + "Elkarrizketa" "Taldeko elkarrizketa" "%1$d+" diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 474c80306bef..c7fc1cb515e1 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1631,6 +1631,10 @@ "با پایین نگه داشتن هردو کلید میزان صدا به‌مدت چند ثانیه، %1$s (یکی از ویژگی‌های دسترس‌پذیری) روشن می‌شود. با این کار نحوه عملکرد دستگاهتان تغییر می‌کند.\n\nمی‌توانید در «تنظیمات > دسترس‌پذیری»،‌این میان‌بر را به ویژگی دیگری تغییر دهید." "روشن کردن" "روشن نشود" + + + + "به %1$s اجازه می‌دهید بر دستگاهتان کنترل کامل داشته باشد؟" "اگر %1$s را روشن کنید، دستگاه شما از قفل صفحه شما جهت بهبود رمزگذاری اطلاعات استفاده نخواهد کرد." "کنترل کامل برای بیشتر برنامه‌ها مناسب نیست، به‌جز برنامه‌هایی که به شما در زمینه نیازهای دسترس‌پذیری کمک می‌کند." @@ -2038,13 +2042,17 @@ "اعلان‌ها" "تنظیمات سریع" "کادر گفتگوی روشن/خاموش" - "تغییر وضعیت صفحهٔ دونیمه" "صفحه قفل" "نماگرفت" - "منوی دسترس‌پذیری" + + + + "نوار شرح %1$s." "%1$s در سطل «محدودشده» قرار گرفت" "%1$s:" + + "مکالمه" "مکالمه گروهی" "+%1$d" diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 24693271aaa4..fd682a4593e5 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1631,6 +1631,10 @@ "Molempien äänenvoimakkuuspainikkeiden pitkään painaminen laittaa päälle esteettömyysominaisuuden %1$s. Tämä voi muuttaa laitteesi toimintaa.\n\nVoit muuttaa tätä pikanäppäintä kohdassa Asetukset > Esteettömyys." "Laita päälle" "Älä laita päälle" + + + + "Saako %1$s laitteesi täyden käyttöoikeuden?" "Jos %1$s otetaan käyttöön, laitteesi ei käytä näytön lukitusta tiedon salauksen parantamiseen." "Täysi käyttöoikeus sopii esteettömyyssovelluksille, mutta ei useimmille sovelluksille." @@ -2038,13 +2042,17 @@ "Ilmoitukset" "Pika-asetukset" "Virran valintaikkuna" - "Jaettu näyttö päälle/pois" "Lukitusnäyttö" "Kuvakaappaus" - "Esteettömyysvalikko" + + + + "Tekstityspalkki: %1$s" "%1$s on nyt rajoitettujen ryhmässä" "%1$s:" + + "Keskustelu" "Ryhmäkeskustelu" "%1$d+" diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 515637d1366e..efcee1456bf5 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1631,6 +1631,10 @@ "Si vous maintenez enfoncées les deux touches de volume pendant quelques secondes, vous activez la fonctionnalité d\'accessibilité %1$s. Cela peut modifier le fonctionnement de votre appareil.\n\nPour attribuer ce raccourci à une autre fonctionnalité, sélectionnez Paramètres > Accessibilité." "Activer" "Ne pas activer" + + + + "Permettre à %1$s de commander complètement votre appareil?" "Si vous activez %1$s, votre appareil n\'utilisera pas le verrouillage de l\'écran pour améliorer le chiffrement des données." "Le contrôle total convient aux applications qui répondent à vos besoins d\'accessibilité. Il ne convient pas à la plupart des applications." @@ -2038,13 +2042,17 @@ "Notifications" "Paramètres rapides" "Boîte de dialogue sur l\'alimentation" - "Basculer l\'écran partagé" "Écran de verrouillage" "Capture d\'écran" - "Menu d\'accessibilité" + + + + "Barre de légende de l\'application %1$s." "%1$s a été placé dans le compartiment RESTREINT" "%1$s :" + + "Conversation" "Conversation de groupe" "%1$d+" diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 86facd466c42..8d0687dc8a72 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1631,6 +1631,10 @@ "Si vous appuyez sur les deux touches de volume pendant quelques secondes, vous activez la fonctionnalité d\'accessibilité %1$s. Cela peut affecter le fonctionnement de votre appareil.\n\nPour attribuer ce raccourci à une autre fonctionnalité, accédez à Paramètres > Accessibilité." "Activer" "Ne pas activer" + + + + "Accorder le contrôle total de votre appareil au service %1$s ?" "Si vous activez %1$s, votre appareil n\'utilisera pas le verrouillage de l\'écran pour améliorer le chiffrement des données." "Le contrôle total convient aux applications qui répondent à vos besoins d\'accessibilité. Il ne convient pas à la plupart des applications." @@ -2038,13 +2042,17 @@ "Notifications" "Configuration rapide" "Boîte de dialogue Marche/Arrêt" - "Activer/Désactiver l\'écran partagé" "Verrouiller l\'écran" "Capture d\'écran" - "Menu d\'accessibilité" + + + + "Barre de légende de l\'application %1$s." "%1$s a été placé dans le bucket RESTRICTED" "%1$s :" + + "Conversation" "Conversation de groupe" "%1$d ou +" diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index c7cdd1351e05..0496810a3aa8 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1631,6 +1631,10 @@ "Ao manter as dúas teclas de volume premidas durante uns segundos actívase %1$s, unha función de accesibilidade. Esta acción pode cambiar o funcionamento do dispositivo.\n\nPodes cambiar o uso deste atallo para outra función en Configuración > Accesibilidade." "Activar" "Non activar" + + + + "Queres permitir que %1$s poida controlar totalmente o teu dispositivo?" "Se activas %1$s, o dispositivo non utilizará o teu bloqueo de pantalla para mellorar a encriptación de datos." "O control total é adecuado para as aplicacións que che axudan coa accesibilidade, pero non para a maioría das aplicacións." @@ -2038,13 +2042,17 @@ "Notificacións" "Configuración rápida" "Cadro de diálogo de acendido/apagado" - "Activar/desactivar pantalla dividida" "Pantalla de bloqueo" "Captura de pantalla" - "Menú de accesibilidade" + + + + "Barra de subtítulos de %1$s." "%1$s incluíuse no grupo RESTRINXIDO" "%1$s:" + + "Conversa" "Conversa de grupo" ">%1$d" diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index c35b7e6ca0c0..e518096a334e 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1631,6 +1631,10 @@ "બન્ને વૉલ્યૂમ કીને થોડી સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા એવી %1$s ચાલુ થઈ જાય છે. આનાથી તમારા ડિવાઇસની કામ કરવાની રીત બદલાઈ શકે છે.\n\nતમે સેટિંગ > ઍક્સેસિબિલિટીમાં જઈને આ શૉર્ટકટને બીજી સુવિધામાં બદલી શકો છો." "ચાલુ કરો" "ચાલુ કરશો નહીં" + + + + "શું %1$sને તમારા ડિવાઇસના સંપૂર્ણ નિયંત્રણની મંજૂરી આપીએ?" "જો તમે %1$s ચાલુ કરશો, તો તમારું ડિવાઇસ ડેટા એન્ક્રિપ્શનને બહેતર બનાવવા તમારા સ્ક્રીન લૉકનો ઉપયોગ કરશે નહીં." "ઍક્સેસિબિલિટી સંબંધિત આવશ્યકતા માટે સહાય કરતી ઍપ માટે સંપૂર્ણ નિયંત્રણ યોગ્ય છે, પણ મોટા ભાગની ઍપ માટે યોગ્ય નથી." @@ -2038,13 +2042,17 @@ "નોટિફિકેશન" "ઝડપી સેટિંગ" "પાવર સંવાદ" - "સ્ક્રીનને વિભાજિત કરવાની ક્રિયા ટૉગલ કરો" "લૉક સ્ક્રીન" "સ્ક્રીનશૉટ" - "ઍક્સેસિબિલિટી મેનૂ" + + + + "%1$sનું કૅપ્શન બાર." "%1$sને પ્રતિબંધિત સમૂહમાં મૂકવામાં આવ્યું છે" "%1$s:" + + "વાતચીત" "ગ્રૂપ વાતચીત" "%1$d+" diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 569b284684bf..0d2c90279d09 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1631,6 +1631,10 @@ "आवाज़ कम और ज़्यादा करने वाले दोनों बटन को कुछ सेकंड तक दबाकर रखने से %1$s चालू हो जाती है, जो एक सुलभता सुविधा है. ऐसा करने से आपके डिवाइस के काम करने के तरीके में बदलाव हो सकता है.\n\nआप सेटिंग और सुलभता में जाकर इस शॉर्टकट को किसी दूसरी सुविधा के लिए बदल सकते हैं." "चालू करें" "चालू न करें" + + + + "%1$s को अपना डिवाइस पूरी तरह कंट्रोल करने की मंज़ूरी दें?" "अगर आप %1$s को चालू करते हैं, तो डेटा को एन्क्रिप्ट (सुरक्षित) करने के तरीके को बेहतर बनाने के लिए आपका डिवाइस सेट किए गए स्क्रीन लॉक का इस्तेमाल नहीं करेगा." "पूरी तरह नियंत्रित करने की अनुमति उन ऐप्लिकेशन के लिए ठीक है जो सुलभता से जुड़ी ज़रूरतों के लिए बने हैं, लेकिन ज़्यादातर ऐप्लिकेशन के लिए यह ठीक नहीं है." @@ -2038,13 +2042,17 @@ "सूचनाएं खोलें" "फटाफट सेटिंग खोलें" "पावर डायलॉग खोलें" - "स्प्लिट स्क्रीन पर टॉगल करें" "स्क्रीन लॉक करें" "स्क्रीनशॉट लें" - "सुलभता मेन्यू" + + + + "%1$s का कैप्शन बार." "%1$s को प्रतिबंधित बकेट में रखा गया है" "%1$s:" + + "बातचीत" "ग्रुप में बातचीत" "%1$d+" diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index a7ae6862fcdc..cab12f6eb8af 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1653,6 +1653,10 @@ "Ako na nekoliko sekundi pritisnete obje tipke za glasnoću, uključuje se značajka pristupačnosti %1$s. Time se može promijeniti način na koji vaš uređaj radi.\n\nZnačajku na koju se taj prečac odnosi možete promijeniti u odjeljku Postavke > Pristupačnost." "Uključi" "Nemoj uključiti" + + + + "Želite li dopustiti usluzi %1$s potpunu kontrolu nad uređajem?" "Ako uključite %1$s, vaš uređaj neće upotrebljavati zaključavanje zaslona za bolju enkripciju podataka." "Potpuna kontrola prikladna je za aplikacije koje vam pomažu s potrebama pristupačnosti, ali ne i za većinu aplikacija." @@ -2072,13 +2076,17 @@ "Obavijesti" "Brze postavke" "Dijalog napajanja" - "Uključite ili isključite podijeljeni zaslon" "Zaključajte zaslon" "Snimka zaslona" - "Izbornik pristupačnosti" + + + + "Traka naslova aplikacije %1$s." "Paket %1$s premješten je u spremnik OGRANIČENO" "%1$s:" + + "Razgovor" "Grupni razgovor" "%1$d+" diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 179531530f97..fb2a5ad17544 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1631,6 +1631,10 @@ "A(z) %1$s kisegítő lehetőség bekapcsolásához tartsa nyomva néhány másodpercig mindkét hangerőgombot. Ez hatással lehet az eszköz működésére.\n\nEzt a gyorsparancsot a Beállítások > Kisegítő lehetőségek pontban módosíthatja másik funkció használatára." "Bekapcsolás" "Nem kapcsolom be" + + + + "Teljes körű vezérlést biztosít eszköze felett a(z) %1$s szolgáltatás számára?" "Ha engedélyezi a(z) %1$s szolgáltatást, az eszköz nem fogja használni a képernyőzárat az adattitkosítás növelése érdekében." "A teljes vezérlés indokolt olyan alkalmazásoknál, amelyek kisegítő lehetőségeket nyújtanak, a legtöbb alkalmazásnál azonban nem." @@ -2038,13 +2042,17 @@ "Értesítések" "Gyorsbeállítások" "Akkumulátorral kapcsolatos párbeszédpanel" - "Osztott képernyő be- vagy kikapcsolása" "Lezárási képernyő" "Képernyőkép" - "Kisegítő lehetőségek menü" + + + + "A(z) %1$s alkalmazás címsora." "A következő csomag a KORLÁTOZOTT csoportba került: %1$s" "%1$s:" + + "Beszélgetés" "Csoportos beszélgetés" "%1$d+" diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 73c0c0c722ef..019e9897ec01 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1631,6 +1631,10 @@ "Ձայնի կարգավորման երկու կոճակները մի քանի վայրկյան սեղմած պահելով կմիացնեք %1$s ծառայությունը, որը հատուկ գործառույթ է։ Դրա արդյունքում սարքի աշխատաեղանակը կարող է փոխվել։\n\nԱյս դյուրանցումը մեկ այլ գործառույթով փոխելու համար անցեք Կարգավորումներ > Հատուկ գործառույթներ։" "Միացնել" "Չմիացնել" + + + + "Թույլատրե՞լ %1$s ծառայությանը կառավարել ձեր սարքը" "Եթե միացնեք %1$s ծառայությունը, ձեր սարքը չի օգտագործի էկրանի կողպումը՝ տվյալների գաղտնագրումը բարելավելու համար:" "Ամբողջական վերահսկումն անհրաժեշտ է միայն այն հավելվածներին, որոնք օգնում են ձեզ հատուկ գործառույթներից օգտվելիս։" @@ -2038,13 +2042,17 @@ "Ծանուցումներ" "Արագ կարգավորումներ" "Սնուցման պատուհան" - "Միացնել/անջատել էկրանի տրոհումը" "Կողպէկրան" "Սքրինշոթ" - "Հատուկ գործառույթների ընտրացանկ" + + + + "%1$s հավելվածի ենթագրերի գոտին։" "%1$s փաթեթը գցվեց ՍԱՀՄԱՆԱՓԱԿՎԱԾ զամբյուղի մեջ" "%1$s՝" + + "Նամակագրություն" "Խմբային նամակագրություն" "%1$d+" diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 338471d89a51..f8af76cbe293 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1631,6 +1631,10 @@ "Menahan kedua tombol volume selama beberapa detik akan mengaktifkan %1$s, yang merupakan fitur aksesibilitas. Tindakan ini dapat mengubah cara kerja perangkat Anda.\n\nAnda dapat mengubah pintasan ini ke fitur lain di Setelan > Aksesibilitas." "Aktifkan" "Jangan aktifkan" + + + + "Izinkan %1$s memiliki kontrol penuh atas perangkat Anda?" "Jika Anda mengaktifkan %1$s, perangkat tidak akan menggunakan kunci layar untuk meningkatkan enkripsi data." "Kontrol penuh sesuai untuk aplikasi yang membantu Anda terkait kebutuhan aksesibilitas, tetapi tidak untuk sebagian besar aplikasi." @@ -2038,13 +2042,17 @@ "Notifikasi" "Setelan Cepat" "Dialog Daya" - "Aktifkan/Nonaktifkan Layar Terpisah" "Layar Kunci" "Screenshot" - "Menu Aksesibilitas" + + + + "Kolom teks %1$s." "%1$s telah dimasukkan ke dalam bucket DIBATASI" "%1$s:" + + "Percakapan" "Percakapan Grup" "%1$d+" diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 6c017e83adf7..010186ce24b1 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -1631,6 +1631,10 @@ "Ef báðum hljóðstyrkstökkunum er haldið inni í nokkrar sekúndur er kveikt á aðgengiseiginleikanum %1$s. Þetta getur breytt því hvernig tækið virkar.\n\nÞú getur breytt þessari flýtileið í annan eiginleika í Stillingar > Aðgengi." "Kveikja" "Ekki kveikja" + + + + "Viltu leyfa %1$s að hafa fulla stjórn yfir tækinu þínu?" "Ef þú kveikir á %1$s mun tækið ekki nota skjálásinn til að efla dulkóðun gagna." "Full stjórnun er viðeigandi fyrir forrit sem hjálpa þér ef þú hefur ekki aðgang, en ekki fyrir flest forrit." @@ -2038,13 +2042,17 @@ "Tilkynningar" "Flýtistillingar" "Gluggi til að slökkva/endurræsa" - "Breyta skjáskiptingu" "Lásskjár" "Skjámynd" - "Aðgengisvalmynd" + + + + "Skjátextastika %1$s." "%1$s var sett í flokkinn TAKMARKAÐ" "%1$s:" + + "Samtal" "Hópsamtal" "%1$d+" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index ed0a6bcbb0ee..94ab2b46bd38 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1631,6 +1631,10 @@ "Se tieni premuti entrambi i tasti del volume per qualche secondo verrà attivata la funzione di accessibilità %1$s. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nPuoi associare questa scorciatoia a un\'altra funzionalità in Impostazioni > Accessibilità." "Attiva" "Non attivare" + + + + "Vuoi consentire a %1$s di avere il controllo totale del tuo dispositivo?" "Se attivi %1$s, il dispositivo non utilizzerà il blocco schermo per migliorare la crittografia dei dati." "Il pieno controllo è appropriato per le app che rispondono alle tue esigenze di accessibilità, ma non per gran parte delle app." @@ -2038,13 +2042,17 @@ "Notifiche" "Impostazioni rapide" "Finestra di dialogo Alimentazione" - "Attiva/disattiva schermo diviso" "Schermata di blocco" "Screenshot" - "Menu Accessibilità" + + + + "Barra del titolo di %1$s." "%1$s è stato inserito nel bucket RESTRICTED" "%1$s:" + + "Conversazione" "Conversazione di gruppo" "%1$d+" diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 87b670a82e96..71126c4bd762 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1675,6 +1675,10 @@ "‏ניתן ללחוץ על שני מקשי עוצמת הקול למשך מספר שניות כדי להפעיל את %1$s, תכונת נגישות. בעקבות זאת, ייתכן שאופן הפעולה של המכשיר ישתנה.\n\nאפשר לשנות את מקשי הקיצור האלה לתכונה נוספת ב\'הגדרות\' > \'נגישות\'." "אני רוצה להפעיל" "לא להפעיל" + + + + "ברצונך להעניק לשירות %1$s שליטה מלאה במכשיר?" "אם השירות %1$s יופעל, המכשיר לא ישתמש בנעילת המסך כדי לשפר את הצפנת הנתונים." "שליטה מלאה מתאימה לאפליקציות שעוזרות עם צורכי הנגישות שלך, אבל לא לרוב האפליקציות." @@ -2106,13 +2110,17 @@ "התראות" "הגדרות מהירות" "תיבת דו-שיח לגבי הסוללה" - "החלפת מצב של מסך מפוצל" "מסך הנעילה" "צילום מסך" - "תפריט נגישות" + + + + "סרגל כיתוב של %1$s." "%1$s התווספה לקטגוריה \'מוגבל\'" "%1$s:" + + "שיחה" "שיחה קבוצתית" "%1$d+" diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 17924e47a920..4b1650228c46 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1631,6 +1631,10 @@ "音量大と音量小の両方のボタンを数秒ほど長押しすると、ユーザー補助機能の %1$s が ON になります。この機能が ON になると、デバイスの動作が変わることがあります。\n\nこのショートカットは [設定] > [ユーザー補助] で別の機能に変更できます。" "ON にする" "ON にしない" + + + + "%1$s にデバイスのフル コントロールを許可しますか?" "%1$s をオンにすると、デバイスデータの暗号化の強化に画面ロックは使用されなくなります。" "フル コントロールは、ユーザー補助機能を必要とするユーザーをサポートするアプリには適していますが、ほとんどのアプリには適していません。" @@ -2038,13 +2042,17 @@ "通知" "クイック設定" "電源ダイアログ" - "分割画面の切り替え" "ロック画面" "スクリーンショット" - "ユーザー補助機能メニュー" + + + + "%1$s のキャプション バーです。" "%1$s は RESTRICTED バケットに移動しました。" "%1$s:" + + "会話" "グループの会話" "%1$d+" diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 5dac9b64da10..3a205d95cca4 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1631,6 +1631,10 @@ "ხმის ორივე ღილაკზე რამდენიმე წამის განმავლობაში დაჭერით ჩაირთვება %1$s, რომელიც მარტივი წვდომის ფუნქციაა. ამან შეიძლება შეცვალოს თქვენი მოწყობილობის მუშაობის პრინციპი.\n\nამ მალსახმობის შეცვლა სხვა ფუნქციით შეგიძლიათ აქ: პარამეტრები > აპები." "ჩართვა" "არ ჩაირთოს" + + + + "დართავთ ნებას %1$s-ს, სრულად მართოს თქვენი მოწყობილობა?" "%1$s-ს თუ ჩართავთ, მონაცემთა დაშიფვრის გასაძლიერებლად თქვენი მოწყობილობა ეკრანის დაბლოკვას არ გამოიყენებს." "სრული კონტროლი გამოსადეგია აპებისთვის, რომლებიც მარტივი წვდომის საჭიროებისას გეხმარებათ, მაგრამ არა აპების უმრავლესობისთვის." @@ -2038,13 +2042,17 @@ "შეტყობინებები" "სწრაფი პარამეტრები" "ელკვების დიალოგი" - "გაყოფილი ეკრანის გადართვა" "ჩაკეტილი ეკრანი" "ეკრანის ანაბეჭდი" - "მარტივი წვდომის მენიუ" + + + + "%1$s-ის სუბტიტრების ზოლი." "%1$s მოთავსდა კალათაში „შეზღუდული“" "%1$s:" + + "მიმოწერა" "ჯგუფური მიმოწერა" "%1$d+" diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index d2773a2f69a1..255d6ae1727b 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1631,6 +1631,10 @@ "Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, %1$s арнайы қызметі іске қосылады. Бұл – құрылғының жүмысына әсер етуі мүмкін.\n\nБұл таңбашаны басқа функцияға \"Параметрлер > Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз." "Қосылсын" "Қосылмасын" + + + + "%1$s қызметі құрылғыңызды толық басқаруына рұқсат етілсін бе?" "%1$s қоссаңыз, құрылғыңыз деректерді шифрлау үшін экранды бекітуді пайдаланбайды." "Арнайы мүмкіндіктер бойынша көмектесетін қолданбаларға ғана құрылғыны толық басқару рұқсатын берген дұрыс." @@ -2038,13 +2042,17 @@ "Хабарландырулар" "Жылдам параметрлер" "Қуат диалогтік терезесі" - "Экранды бөлу мүмкіндігін қосу/өшіру" "Құлып экраны" "Скриншот" - "Арнайы мүмкіндіктер мәзірі" + + + + "%1$s қолданбасының жазу жолағы." "%1$s ШЕКТЕЛГЕН себетке салынды." "%1$s:" + + "Чат" "Топтық чат" "%1$d+" diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 48af34027ed1..1c32ec1cd40d 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1633,6 +1633,10 @@ "ការសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ទាំងពីរ​ឱ្យជាប់​រយៈពេល​ពីរបីវិនាទី​នឹងបើក %1$s ដែលជា​មុខងារ​ភាពងាយប្រើ។ ការធ្វើ​បែបនេះ​អាចផ្លាស់ប្ដូរ​របៀបដែល​ឧបករណ៍​របស់អ្នក​ដំណើរការ។\n\nអ្នកអាច​ប្ដូរផ្លូវកាត់​នេះទៅ​មុខងារ​ផ្សេងទៀត​នៅក្នុង​ការកំណត់ > ភាពងាយស្រួល។" "បើក" "កុំបើក" + + + + "អនុញ្ញាតឱ្យ %1$s មានសិទ្ធិគ្រប់គ្រងឧបករណ៍​របស់អ្នក​ទាំងស្រុងឬ?" "បើ​អ្នក​បើក %1$s ឧបករណ៍របស់​អ្នកនឹង​មិន​ប្រើ​ការចាក់សោអេក្រង់របស់​អ្នក​ ដើម្បី​បង្កើន​ប្រសិទ្ធភាពការ​អ៊ីនគ្រីប​ទិន្នន័យ​ទេ។" "ការគ្រប់គ្រង​ទាំងស្រុងមានលក្ខណៈ​សមស្របសម្រាប់​កម្មវិធី ដែលជួយអ្នក​ទាក់ទងនឹងការប្រើមុខងារភាពងាយស្រួល ប៉ុន្តែមិនសមស្របសម្រាប់​កម្មវិធីភាគច្រើនទេ។" @@ -2040,13 +2044,17 @@ "ការជូនដំណឹង" "ការកំណត់រហ័ស" "ប្រអប់​ថាមពល" - "បិទ/បើក​មុខងារ​បំបែកអេក្រង់" "អេក្រង់ចាក់សោ" "រូបថតអេក្រង់" - "ម៉ឺនុយ​ភាពងាយស្រួល" + + + + "របារពណ៌នា​អំពី %1$s។" "%1$s ត្រូវបានដាក់​ទៅក្នុងធុង​ដែលបានដាក់កំហិត" "%1$s៖" + + "ការ​សន្ទនា" "ការសន្ទនា​ជាក្រុម" "%1$d+" diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 64002ef0d15f..75c4f1d37c04 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1631,6 +1631,10 @@ "ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವಾದ %1$s ಆನ್ ಆಗುತ್ತದೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\nನೀವು ಈ ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಅಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿನ ಮತ್ತೊಂದು ವೈಶಿಷ್ಟ್ಯಕ್ಕೆ ಬದಲಾಯಿಸಬಹುದು." "ಆನ್ ಮಾಡಿ" "ಆನ್ ಮಾಡಬೇಡಿ" + + + + "ನಿಮ್ಮ ಸಾಧನದ ಪೂರ್ಣ ನಿಯಂತ್ರಣ ಹೊಂದಲು %1$s ಗೆ ಅನುಮತಿಸಬೇಕೆ?" "ನೀವು %1$s ಅನ್ನು ಆನ್ ಮಾಡಿದರೆ, ನಿಮ್ಮ ಸಾಧನವು ಡೇಟಾ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಅನ್ನು ವರ್ಧಿಸಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ಲಾಕ್ ಅನ್ನು ಬಳಸುವುದಿಲ್ಲ." "ಪ್ರವೇಶಿಸುವಿಕೆಯ ಅವಶ್ಯಕತೆಗಳಿಗೆ ಸಹಾಯ ಮಾಡುವ ಆ್ಯಪ್‌ಗಳಿಗೆ ಪೂರ್ಣ ನಿಯಂತ್ರಣ ನೀಡುವುದು ಸೂಕ್ತವಾಗಿರುತ್ತದೆ, ಆದರೆ ಬಹುತೇಕ ಆ್ಯಪ್‌ಗಳಿಗೆ ಇದು ಸೂಕ್ತವಲ್ಲ." @@ -2038,13 +2042,17 @@ "ಅಧಿಸೂಚನೆಗಳು" "ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳು" "ಪವರ್ ಡೈಲಾಗ್" - "ಸ್ಪ್ಲಿಟ್-ಸ್ಕ್ರೀನ್ ಟಾಗಲ್ ಮಾಡಿ" "ಲಾಕ್ ಸ್ಕ್ರೀನ್" "ಸ್ಕ್ರೀನ್‌ಶಾಟ್" - "ಪ್ರವೇಶಿಸುವಿಕೆ ಮೆನು" + + + + "%1$s ಆ್ಯಪ್‌ನ ಶೀರ್ಷಿಕೆಯ ಪಟ್ಟಿ." "%1$s ಅನ್ನು ನಿರ್ಬಂಧಿತ ಬಕೆಟ್‌ಗೆ ಹಾಕಲಾಗಿದೆ" "%1$s:" + + "ಸಂವಾದ" "ಗುಂಪು ಸಂವಾದ" "%1$d+" diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index ac691ae1189c..8437ab6a8c1c 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1631,6 +1631,10 @@ "볼륨 키 2개를 몇 초 동안 길게 누르면 %1$s 접근성 기능이 사용 설정됩니다. 이렇게 되면 기기 작동 방식이 달라질 수 있습니다.\n\n설정 > 접근성에서 이 단축키를 다른 기능으로 변경할 수 있습니다." "사용 설정" "사용 안 함" + + + + "%1$s에서 기기를 완전히 제어하도록 허용하시겠습니까?" "%1$s을(를) 사용 설정하면 기기에서 데이터 암호화를 개선하기 위해 화면 잠금을 사용하지 않습니다." "접근성 관련 지원을 제공하는 앱에는 완벽한 제어권을 부여하는 것이 좋으나, 대부분의 앱에는 적합하지 않습니다." @@ -2038,13 +2042,17 @@ "알림" "빠른 설정" "전원 대화상자" - "화면 분할 모드 전환" "잠금 화면" "스크린샷" - "접근성 메뉴" + + + + "%1$s의 자막 표시줄입니다." "%1$s 항목이 RESTRICTED 버킷으로 이동함" "%1$s:" + + "대화" "그룹 대화" "%1$d+" diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 31c723ff8b67..1ba6094e85b9 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1631,6 +1631,10 @@ "Үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып турса, %1$s, атайын мүмкүнчүлүктөр күйгүзүлөт. Бул түзмөгүңүздүн ишин өзгөртүшү мүмкүн.\n\nБул ыкчам баскычты Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнөн башка функцияга өзгөртө аласыз." "Күйгүзүлсүн" "Күйгүзүлбөсүн" + + + + "%1$s кызматына түзмөгүңүздү толугу менен көзөмөлдөөгө уруксат бересизби?" "Эгер %1$s күйгүзүлсө, түзмөгүңүз дайын-даректерди шифрлөөнү күчтөндүрүү үчүн экраныңыздын кулпусун пайдаланбайт." "Толук көзөмөл атайын мүмкүнчүлүктөрдү пайдаланууга керек, бирок калган көпчүлүк колдонмолорго кереги жок." @@ -2038,13 +2042,17 @@ "Билдирмелер" "Ыкчам жөндөөлөр" "Кубат диалогу" - "Экранды бөлүүнү күйгүзүү же өчүрүү" "Кулпуланган экран" "Скриншот" - "Атайын мүмкүнчүлүктөр менюсу" + + + + "%1$s колдонмосунун маалымат тилкеси." "%1$s ЧЕКТЕЛГЕН чакага коюлган" "%1$s:" + + "Жазышуу" "Топтук маек" "%1$d+" diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 5ff7a692621a..083e4cc12c15 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1631,6 +1631,10 @@ "ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ສອງສາມວິນາທີເພື່ອເປີດໃຊ້ %1$s, ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ນີ້ອາດປ່ຽນວິທີການເຮັດວຽກຂອງອຸປະກອນທ່ານ.\n\nທ່ານສາມາດປ່ຽນທາງລັດນີ້ເປັນຄຸນສົມບັດອື່ນໄດ້ໃນການຕັ້ງຄ່າ > ການຊ່ວຍເຂົ້າເຖິງ." "ເປີດໃຊ້" "ບໍ່ເປີດ" + + + + "ອະນຸຍາດໃຫ້ %1$s ຄວບຄຸມອຸປະກອນທ່ານໄດ້ເຕັມຮູບແບບບໍ?" "ຫາກ​ທ່ານ​ເປີດ​ໃຊ້ %1$s, ​ອຸ​ປະ​ກອນ​ຂອງ​ທ່ານ​ຈະ​ບໍ່​ໃຊ້​ການ​ລັອກ​ໜ້າ​ຈໍ​ຂອງ​ທ່ານ​ເພື່ອ​ເພີ່ມ​ການ​ເຂົ້າ​ລະ​ຫັດ​ຂໍ້​ມູນ." "ການຄວບຄຸມແບບເຕັມຮູບແບບແມ່ນເໝາະສົມສຳລັບແອັບທີ່ຊ່ວຍທ່ານໃນດ້ານການຊ່ວຍເຂົ້າເຖິງ, ແຕ່ບໍ່ເໝາະສຳລັບທຸກແອັບ." @@ -2038,13 +2042,17 @@ "ການແຈ້ງເຕືອນ" "ການຕັ້ງຄ່າດ່ວນ" "ກ່ອງໂຕ້ຕອບການເປີດປິດ" - "ເປີດ/ປິດການແບ່ງໜ້າຈໍ" "ໜ້າຈໍລັອກ" "ຮູບໜ້າຈໍ" - "​ເມ​ນູ​ການ​ຊ່ວຍ​ເຂົ້າ​ເຖິງ" + + + + "ແຖບຄຳບັນຍາຍຂອງ %1$s." "%1$s ຖືກວາງໄວ້ໃນກະຕ່າ \"ຈຳກັດ\" ແລ້ວ" "%1$s:" + + "ການສົນທະນາ" "ການສົນທະນາກຸ່ມ" "%1$d+" diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 2b041565f416..8dd7fa1f7076 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1675,6 +1675,10 @@ "Paspaudus abu garsumo klavišus ir palaikius kelias sekundes įjungiama pritaikymo neįgaliesiems funkcija „%1$s“. Tai gali pakeisti įrenginio veikimą.\n\nGalite pakeisti šį spartųjį klavišą į kitą funkciją skiltyje „Nustatymai“ > „Pritaikomumas“." "Įjungti" "Neįjungti" + + + + "Leisti „%1$s“ valdyti visas įrenginio funkcijas?" "Jei įjungsite „%1$s“, įrenginyje nebus naudojamas ekrano užraktas siekiant patobulinti duomenų šifruotę." "Galimybę valdyti visas funkcijas patariama suteikti programoms, kurios padeda specialiųjų poreikių turintiems asmenims, bet ne daugumai programų." @@ -2106,13 +2110,17 @@ "Pranešimai" "Spartieji nustatymai" "Maitinimo dialogo langas" - "Perjungti išskaidyto ekrano režimą" "Užrakinimo ekranas" "Ekrano kopija" - "Pritaikomumo meniu" + + + + "Programos „%1$s“ antraštės juosta." "„%1$s“ įkeltas į grupę APRIBOTA" "%1$s:" + + "Pokalbis" "Grupės pokalbis" "%1$d+" diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index c80e3ac45e99..07693a9b2334 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1653,6 +1653,10 @@ "Turot nospiestus abus skaļuma taustiņus dažas sekundes, tiek ieslēgta pieejamības funkcija %1$s. Tas var mainīt ierīces darbību.\n\nŠo saīsni uz citu funkciju varat mainīt šeit: Iestatījumi > Pieejamība." "Ieslēgt" "Neieslēgt" + + + + "Vai atļaut pakalpojumam %1$s pilnībā kontrolēt jūsu ierīci?" "Ja ieslēgsiet pakalpojumu %1$s, ierīce neizmantos ekrāna bloķēšanu datu šifrēšanas uzlabošanai." "Pilnīga kontrole ir piemērota lietotnēm, kas nepieciešamas lietotājiem ar īpašām vajadzībām, taču ne lielākajai daļai lietotņu." @@ -2072,13 +2076,17 @@ "Paziņojumi" "Ātrie iestatījumi" "Barošanas dialoglodziņš" - "Pārslēgt ekrāna sadalīšanu" "Bloķēt ekrānu" "Ekrānuzņēmums" - "Pieejamības izvēlne" + + + + "Lietotnes %1$s subtitru josla." "Pakotne “%1$s” ir ievietota ierobežotā kopā." "%1$s:" + + "Saruna" "Grupas saruna" "%1$d+" diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index f7e23a8ad1ac..6988893f7a8e 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -1633,6 +1633,10 @@ "Ако ги задржите притиснати двете копчиња за јачина на звук неколку секунди, ќе се вклучи функцијата за пристапност %1$s. Ова може да го промени начинот на функционирање на уредот.\n\nМоже да ја измените кратенкава да биде за друга функција во „Поставки > Пристапност“." "Вклучи" "Не вклучувај" + + + + "Дали дозволувате %1$s да има целосна контрола врз вашиот уред?" "Ако вклучите %1$s, уредот нема да го користи заклучувањето на екранот за да го подобри шифрирањето на податоците." "Целосната контрола е соодветна за апликации што ви помагаат со потребите за пристапност, но не и за повеќето апликации." @@ -2040,13 +2044,17 @@ "Известувања" "Брзи поставки" "Дијалог за напојување" - "Вклучи/исклучи поделен екран" "Заклучен екран" "Слика од екранот" - "Мени за пристапност" + + + + "Насловна лента на %1$s." "%1$s е ставен во корпата ОГРАНИЧЕНИ" "%1$s:" + + "Разговор" "Групен разговор" "%1$d+" diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 4d88c993d256..8fa33ee82ab8 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1631,6 +1631,10 @@ "രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത് ഉപയോഗസഹായി ഫീച്ചറായ %1$s എന്നതിനെ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന വിധം ഇത് മാറ്റിയേക്കാം.\n\nക്രമീകരണം > ഉപയോഗസഹായി എന്നതിലെ മറ്റൊരു ഫീച്ചറിലേക്ക് നിങ്ങൾക്ക് ഈ കുറുക്കുവഴി മാറ്റാനാവും." "ഓണാക്കുക" "ഓണാക്കരുത്" + + + + "%1$s എന്നതിന് നിങ്ങളുടെ ഉപകരണത്തിന്മേൽ പൂർണ്ണ നിയന്ത്രണം അനുവദിക്കണോ?" "%1$s ഓണാക്കിയെങ്കിൽ, ഡാറ്റ എൻക്രിപ്‌ഷൻ മെച്ചപ്പെടുത്താൻ ഉപകരണം നിങ്ങളുടെ സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കില്ല." "ഉപയോഗസഹായി ആവശ്യങ്ങൾക്കായി നിങ്ങളെ സഹായിക്കുന്ന ആപ്പുകൾക്ക് പൂർണ്ണ നിയന്ത്രണം അനുയോജ്യമാണെങ്കിലും മിക്ക ആപ്പുകൾക്കും അനുയോജ്യമല്ല." @@ -2038,13 +2042,17 @@ "അറിയിപ്പുകൾ" "ദ്രുത ക്രമീകരണം" "പവർ ഡയലോഗ്" - "സ്‌ക്രീൻ വിഭജന മോഡ് മാറ്റുക" "ലോക്ക് സ്‌ക്രീൻ" "സ്ക്രീൻഷോട്ട്" - "ഉപയോഗസഹായി മെനു" + + + + "%1$s എന്നതിന്റെ അടിക്കുറിപ്പ് ബാർ." "%1$s നിയന്ത്രിത ബക്കറ്റിലേക്ക് നീക്കി" "%1$s:" + + "സംഭാഷണം" "ഗ്രൂപ്പ് സംഭാഷണം" "%1$d+" diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index f9963435bbaf..bbeb09cf5e94 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1631,6 +1631,10 @@ "Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарах нь хандалтын онцлог болох %1$s-г асаадаг. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nТа Тохиргоо > Хандалт хэсэгт энэ товчлолыг өөр онцлогт оноож өөрчлөх боломжтой." "Асаах" "Битгий асаа" + + + + "%1$s-д таны төхөөрөмжийг бүрэн хянахыг зөвшөөрөх үү?" "Хэрэв та %1$s-г асаавал таны төхөөрөмж өгөгдлийн шифрлэлтийг сайжруулахын тулд таны дэлгэцийн түгжээг ашиглахгүй." "Бүрэн хянах нь таны хандалтын үйлчилгээний шаардлагад тусалдаг аппуудад тохиромжтой боловч ихэнх аппад тохиромжгүй байдаг." @@ -2038,13 +2042,17 @@ "Мэдэгдэл" "Шуурхай тохиргоо" "Тэжээлийн харилцах цонх" - "Дэлгэц хуваахыг унтраах/асаах" "Дэлгэцийг түгжих" "Дэлгэцийн зураг дарах" - "Хандалтын цэс" + + + + "%1$s-н гарчгийн талбар." "%1$s-г ХЯЗГААРЛАСАН сагс руу орууллаа" "%1$s:" + + "Харилцан яриа" "Бүлгийн харилцан яриа" "%1$d+" diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index c46cb8d97527..9ee94369acc6 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -1631,6 +1631,10 @@ "दोन्ही व्हॉल्यूम की काही सेकंद धरून ठेवल्याने %1$s, एक अ‍ॅक्सेसिबिलिटी वैशिष्ट्य सुरू होते. यामुळे तुमचे डिव्हाइस कसे काम करते हे पूर्णपणे बदलते.\n\nतुम्ही हा शॉर्टकट सेटिंग्ज > अ‍ॅक्सेसिबिलिटी मध्ये बदलू शकता." "सुरू करा" "सुरू करू नका" + + + + "%1$s ला तुमचे डिव्हाइसच संपूर्णपणे नियंत्रित करायची अनुमती द्यायची का?" "तुम्ही %1$s सुरू केल्‍यास, तुमचे डिव्हाइस डेटा एंक्रिप्शनमध्ये सुधारणा करण्‍यासाठी स्क्रीन लॉक वापरणार नाही." "जी ॲप्स तुमच्या ॲक्सेसिबिलिटी गरजा पूर्ण करतात अशा ॲप्ससाठी संपूर्ण नियंत्रण योग्य आहे. पण ते सर्व ॲप्सना लागू होईल असे नाही." @@ -2038,13 +2042,17 @@ "सूचना" "क्विक सेटिंग्ज" "पॉवर डायलॉग" - "विभाजित स्क्रीन टॉगल करा" "स्‍क्रीन लॉक करा" "स्क्रीनशॉट" - "अ‍ॅक्सेसिबिलिटी मेनू" + + + + "%1$s चा शीर्षक बार." "%1$s हे प्रतिबंधित बादलीमध्ये ठेवण्यात आले आहे" "%1$s:" + + "संभाषण" "गट संभाषण" "%1$d+" diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 8a4148a91d6a..db9f4f22c1ee 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1631,6 +1631,10 @@ "Tindakan menahan kedua-dua kekunci kelantangan selama beberapa saat akan menghidupkan %1$s, iaitu satu ciri kebolehaksesan. Ini mungkin mengubah cara peranti anda berfungsi.\n\nAnda boleh menukar pintasan ini kepada ciri lain dalam Tetapan > Kebolehaksesan." "Hidupkan" "Jangan hidupkan" + + + + "Benarkan %1$s mempunyai kawalan penuh atas peranti anda?" "Jika anda menghidupkan %1$s, peranti anda tidak akan menggunakan kunci skrin anda untuk meningkatkan penyulitan data." "Kawalan penuh sesuai untuk apl yang membantu anda dengan keperluan kebolehaksesan tetapi bukan untuk kebanyakan apl." @@ -2038,13 +2042,17 @@ "Pemberitahuan" "Tetapan Pantas" "Dialog Kuasa" - "Togol Skrin Pisah" "Skrin Kunci" "Tangkapan skrin" - "Menu Kebolehaksesan" + + + + "Bar kapsyen %1$s." "%1$s telah diletakkan dalam baldi TERHAD" "%1$s:" + + "Perbualan" "Perbualan Kumpulan" "%1$d+" diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 7c6f3e72f9cc..c1f2b0231920 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -1631,6 +1631,10 @@ "အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုဖြစ်သော %1$s ကို ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nဤဖြတ်လမ်းလင့်ခ်ကို \'ဆက်တင်များ > အများသုံးစွဲနိုင်မှု\' တွင် နောက်ဝန်ဆောင်မှုတစ်ခုသို့ ပြောင်းနိုင်သည်။" "ဖွင့်ရန်" "မဖွင့်ပါနှင့်" + + + + "%1$s ကို သင့်စက်အား အပြည့်အဝထိန်းချုပ်ခွင့် ပေးလိုပါသလား။" "%1$s ဖွင့်လိုက်ပါက သင်၏စက်သည် ဒေတာအသွင်ဝှက်ခြင်း ပိုကောင်းမွန်စေရန် သင့်ဖန်သားပြင်လော့ခ်ကို သုံးမည်မဟုတ်ပါ။" "အများသုံးစွဲနိုင်မှု လိုအပ်ချက်များအတွက် အထောက်အကူပြုသည့် အက်ပ်များကို အပြည့်အဝထိန်းချုပ်ခြင်းသည် သင့်လျော်သော်လည်း အက်ပ်အများစုအတွက် မသင့်လျော်ပါ။" @@ -2038,13 +2042,17 @@ "အကြောင်းကြားချက်များ" "အမြန် ဆက်တင်များ" "ပါဝါ ဒိုင်ယာလော့" - "မျက်နှာပြင် ခွဲ၍ပြသခြင်းကို နှိပ်ပါ" "လော့ခ်မျက်နှာပြင်" "ဖန်သားပြင်ဓာတ်ပုံ" - "အများသုံးစွဲနိုင်မှု မီနူး" + + + + "%1$s၏ ခေါင်းစီး ဘား။" "%1$s ကို တားမြစ်ထားသော သိမ်းဆည်းမှုအတွင်းသို့ ထည့်ပြီးပါပြီ" "%1$s-" + + "စကားဝိုင်း" "အဖွဲ့စကားဝိုင်း" "%1$d+" diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 9f75ce2217f2..a6ce0e1f84c2 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1631,6 +1631,10 @@ "Hvis du holder inne begge volumtastene i noen sekunder, slår du på %1$s, en tilgjengelighetsfunksjon. Dette kan endre hvordan enheten din fungerer.\n\nDu kan endre denne snarveien til en annen funksjon i Innstillinger > Tilgjengelighet." "Slå på" "Ikke slå på" + + + + "Vil du gi %1$s full kontroll over enheten din?" "Hvis du slår på %1$s, bruker ikke enheten skjermlåsen til å forbedre datakryptering." "Full kontroll er passende for apper som hjelper deg med tilgjengelighetsbehov, men ikke for de fleste apper." @@ -2038,13 +2042,17 @@ "Varsler" "Hurtiginnstillinger" "Dialogboks for å slå av/på" - "Slå delt skjerm av/på" "Låseskjerm" "Skjermdump" - "Tilgjengelighet-meny" + + + + "Tekstingsfelt i %1$s." "%1$s er blitt plassert i TILGANGSBEGRENSET-toppmappen" "%1$s:" + + "Samtale" "Gruppesamtale" "%1$d+" diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 81115f835f8f..d58cbf0121a4 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1637,6 +1637,10 @@ "केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुले %1$s नामक पहुँचसम्बन्धी सुविधा सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nतपाईं सेटिङ > पहुँचमा गई यो सर्टकटमार्फत अर्को सुविधा खुल्ने बनाउन सक्नुहुन्छ।" "सक्रिय गरियोस्" "सक्रिय नगरियोस्" + + + + "%1$s लाई तपाईंको यन्त्र पूर्ण रूपमा नियन्त्रण गर्न दिने हो?" "तपाईंले %1$s सक्रिय गर्नुभयो भने तपाईंको यन्त्रले डेटा इन्क्रिप्ट गर्ने सुविधाको स्तरोन्नति गर्न तपाईंको स्क्रिन लक सुविधाको प्रयोग गर्ने छैन।" "तपाईंलाई पहुँच राख्न आवश्यक पर्ने कुरामा सहयोग गर्ने अनुप्रयोगहरूमाथि पूर्ण नियन्त्रण गर्नु उपयुक्त हुन्छ तर अधिकांश अनुप्रयोगहरूका हकमा यस्तो नियन्त्रण उपयुक्त हुँदैन।" @@ -2044,13 +2048,17 @@ "सूचनाहरू" "द्रुत सेटिङहरू" "पावर संवाद" - "विभाजित स्क्रिन टगल गर्नुहोस्" "लक स्क्रिन" "स्क्रिनसट" - "पहुँचसम्बन्धी मेनु" + + + + "%1$s को क्याप्सन बार।" "%1$s लाई प्रतिबन्धित बाल्टीमा राखियो" "%1$s:" + + "वार्तालाप" "सामूहिक वार्तालाप" "%1$d+" diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 17989bff6ebc..684af964cd7f 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1631,6 +1631,10 @@ "Als je beide volumetoetsen een paar seconden ingedrukt houdt, wordt de toegankelijkheidsfunctie %1$s ingeschakeld. Hierdoor kan de manier veranderen waarop je apparaat werkt.\n\nJe kunt deze sneltoets op een andere functie instellen via Instellingen > Toegankelijkheid." "Inschakelen" "Niet inschakelen" + + + + "Toestaan dat %1$s volledige controle over je apparaat heeft?" "Als je %1$s inschakelt, maakt je apparaat geen gebruik van schermvergrendeling om de gegevensversleuteling te verbeteren." "Volledige controle is gepast voor apps die je helpen met toegankelijkheid, maar voor de meeste apps is het ongepast." @@ -2038,13 +2042,17 @@ "Meldingen" "Snelle instellingen" "Voedingsdialoogvenster" - "Gesplitst scherm schakelen" "Scherm vergrendelen" "Screenshot" - "Toegankelijkheidsmenu" + + + + "Ondertitelingsbalk van %1$s." "%1$s is in de bucket RESTRICTED geplaatst" "%1$s:" + + "Gesprek" "Groepsgesprek" "%1$d+" diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index a77845f10e93..f0417192c5e4 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1108,7 +1108,7 @@ "ଇନପୁଟ୍ ପଦ୍ଧତି" "ଟେକ୍ସଟ୍‌ କାର୍ଯ୍ୟ" "ଷ୍ଟୋରେଜ୍‌ ସ୍ପେସ୍‌ ଶେଷ ହେବାରେ ଲାଗିଛି" - "କିଛି ସିଷ୍ଟମ ଫଙ୍କଶନ୍‍ କାମ କରିନପାରେ" + "କିଛି ସିଷ୍ଟମ ପ୍ରକାର୍ଯ୍ୟ କାମ କରିନପାରେ" "ସିଷ୍ଟମ୍ ପାଇଁ ପ୍ରର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍‌ ନାହିଁ। ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ଆପଣଙ୍କ ପାଖରେ 250MB ଖାଲି ଜାଗା ଅଛି ଏବଂ ପୁନଃ ଆରମ୍ଭ କରନ୍ତୁ।" "%1$s ଚାଲୁଛି" "ଅଧିକ ସୂଚନା ପାଇଁ କିମ୍ବା ଆପ୍‍ ବନ୍ଦ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।" @@ -1631,6 +1631,10 @@ "କିଛି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ’କୁ ଧରି ରଖିବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍ %1$s ଚାଲୁ ହୁଏ। ଏହା ଆପଣଙ୍କ ଡିଭାଇସ୍ କିପରି କାମ କରେ ତାହା ପରିବର୍ତ୍ତନ କରିପାରେ।\n\nଆପଣ ସେଟିଂସ୍ &gt ଆକ୍ସେସିବିଲିଟୀରେ ଏହି ସର୍ଚକଟକୁ ଅନ୍ୟ ଏକ ଫିଚରରେ ପରିବର୍ତ୍ତନ କରିପାରିବେ।" "ଚାଲୁ କରନ୍ତୁ" "ଚାଲୁ କରନ୍ତୁ ନାହିଁ" + + + + "%1$sକୁ ଆପଣଙ୍କ ଡିଭାଇସର ସମ୍ପୂର୍ଣ୍ଣ ନିୟନ୍ତ୍ରଣର ଅନୁମତି ଦେବେ?" "ଯଦି ଆପଣ %1$s ଚାଲୁ କରନ୍ତି, ତେବେ ଆପଣଙ୍କ ଡିଭାଇସ୍ ଡାଟା ଏନକ୍ରିପ୍ସନ୍ ବୃଦ୍ଧି କରିବାକୁ ଆପଣଙ୍କର ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରିବ ନାହିଁ।" "ଯେଉଁ ଆପ୍ସ ଆପଣଙ୍କୁ ଆକ୍ସେସିବିଲିଟୀ ଆବଶ୍ୟକତାରେ ସହାୟତା କରେ, ସେହି ଆପ୍ସ ପାଇଁ ସମ୍ପୂର୍ଣ୍ଣ ନିୟନ୍ତ୍ରଣ ଉପଯୁକ୍ତ ଅଟେ, କିନ୍ତୁ ଅଧିକାଂଶ ଆପ୍ସ ପାଇଁ ଉପଯୁକ୍ତ ନୁହେଁ।" @@ -2038,13 +2042,17 @@ "ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଖୋଲନ୍ତୁ" "କ୍ୱିକ୍ ସେଟିଂସ୍ ଖୋଲନ୍ତୁ" "ପାୱାର ଡାୟଲଗ୍ ଖୋଲନ୍ତୁ" - "ଦୁଇଟି ସ୍କ୍ରିନ୍ ମଧ୍ୟରେ ଟୋଗଲ୍ କରନ୍ତୁ" "ସ୍କ୍ରିନ୍ ଲକ୍ କରନ୍ତୁ" "ସ୍କ୍ରି‍ନ୍‍ସଟ୍ ନିଅନ୍ତୁ" - "ଆକ୍ସେସିବିଲିଟୀ ମେନୁ" + + + + "%1$sର କ୍ୟାପ୍ସନ୍ ବାର୍।" "%1$sକୁ ପ୍ରତିବନ୍ଧିତ ବକେଟରେ ରଖାଯାଇଛି" "%1$s:" + + "ବାର୍ତ୍ତାଳାପ" "ଗୋଷ୍ଠୀ ବାର୍ତ୍ତାଳାପ" "%1$d+" diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index d2ae05212956..bdd9b0a9e3c1 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1631,6 +1631,10 @@ "ਕੁਝ ਸਕਿੰਟਾਂ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਨੂੰ ਦਬਾਈ ਰੱਖਣਾ %1$s, ਇੱਕ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਚਾਲੂ ਕਰ ਦਿੰਦਾ ਹੈ। ਇਹ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲ ਸਕਦਾ ਹੈ।\n\nਸੈਟਿੰਗਾਂ ਅਤੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਤੁਸੀਂ ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਕਿਸੇ ਹੋਰ ਵਿਸ਼ੇਸ਼ਤਾ ਵਿੱਚ ਬਦਲ ਸਕਦੇ ਹੋ।" "ਚਾਲੂ ਕਰੋ" "ਚਾਲੂ ਨਾ ਕਰੋ" + + + + "ਕੀ %1$s ਨੂੰ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦਾ ਪੂਰਾ ਕੰਟਰੋਲ ਦੇਣਾ ਹੈ?" "ਜੇਕਰ ਤੁਸੀਂ %1$s ਚਾਲੂ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਇਨਕ੍ਰਿਪਸ਼ਨ ਦਾ ਵਿਸਤਾਰ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰੇਗਾ।" "ਪੂਰਾ ਕੰਟਰੋਲ ਉਹਨਾਂ ਐਪਾਂ ਲਈ ਢੁਕਵਾਂ ਹੈ ਜੋ ਪਹੁੰਚਯੋਗਤਾ ਸੰਬੰਧੀ ਲੋੜਾਂ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਕਰਦੀਆਂ ਹਨ, ਪਰ ਜ਼ਿਆਦਾਤਰ ਐਪਾਂ ਲਈ ਢੁਕਵਾਂ ਨਹੀਂ ਹੁੰਦਾ।" @@ -2038,13 +2042,17 @@ "ਸੂਚਨਾਵਾਂ" "ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ" "ਪਾਵਰ ਵਿੰਡੋ" - "ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਨੂੰ ਟੌਗਲ ਕਰੋ" "ਲਾਕ ਸਕ੍ਰੀਨ" "ਸਕ੍ਰੀਨਸ਼ਾਟ" - "ਪਹੁੰਚਯੋਗਤਾ ਮੀਨੂ" + + + + "%1$s ਦੀ ਸੁਰਖੀ ਪੱਟੀ।" "%1$s ਨੂੰ ਪ੍ਰਤਿਬੰਧਿਤ ਖਾਨੇ ਵਿੱਚ ਪਾਇਆ ਗਿਆ ਹੈ" "%1$s:" + + "ਗੱਲਬਾਤ" "ਗੁਰੱਪ ਗੱਲਬਾਤ" "%1$d+" diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index f219bd3ac143..15ec13371059 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1675,6 +1675,10 @@ "Przytrzymanie obu klawiszy głośności przez kilka sekund włącza usługę %1$s, stanowiącą ułatwienie dostępu. Może to zmienić sposób działania urządzenia.\n\nAby zmienić ten skrót i wskazać inną funkcję, kliknij Ustawienia > Ułatwienia dostępu." "Włącz" "Nie włączaj" + + + + "Pozwolić usłudze %1$s na pełną kontrolę nad urządzeniem?" "Jeśli włączysz usługę %1$s, Twoje urządzenie nie będzie korzystać z blokady ekranu, by usprawnić szyfrowanie danych." "Pełna kontrola jest odpowiednia dla aplikacji, które pomagają Ci radzić sobie z niepełnosprawnością, ale nie należy jej przyznawać wszystkim aplikacjom." @@ -2106,13 +2110,17 @@ "Powiadomienia" "Szybkie ustawienia" "Okno opcji zasilania" - "Przełącz podzielony ekran" "Ekran blokady" "Zrzut ekranu" - "Menu ułatwień dostępu" + + + + "Pasek napisów w aplikacji %1$s" "Umieszczono pakiet %1$s w zasobniku danych RESTRICTED" "%1$s:" + + "Rozmowa" "Rozmowa grupowa" "%1$d+" diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 624560ec822c..ae2b5216f742 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1631,6 +1631,10 @@ "Manter as duas teclas de volume pressionadas por alguns segundos ativa o serviço %1$s, um recurso de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nÉ possível trocar o uso desse atalho para outro recurso em \"Config. > Acessibilidade\"." "Ativar" "Não ativar" + + + + "Permitir que o %1$s tenha controle total do seu dispositivo?" "Se o %1$s for ativado, o dispositivo não usará o bloqueio de tela para melhorar a criptografia de dados." "O controle total é adequado para apps que ajudam você com as necessidades de acessibilidade, mas não para a maioria dos apps." @@ -2038,13 +2042,17 @@ "Notificações" "Configurações rápidas" "Caixa de diálogo de liga/desliga" - "Ativar tela dividida" "Bloquear tela" "Capturar tela" - "Menu de acessibilidade" + + + + "Barra de legendas do app %1$s." "%1$s foi colocado no intervalo \"RESTRITO\"" "%1$s:" + + "Conversa" "Conversa em grupo" "+ de %1$d" diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index b0ab7264ec7c..0ae75ec4cd5e 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1631,6 +1631,10 @@ "Manter premidas ambas as teclas de volume durante alguns segundos ativa o serviço %1$s, uma funcionalidade de acessibilidade. Esta pode alterar a forma como o seu dispositivo funciona.\n\nPode alterar este atalho para outra funcionalidade em Definições > Acessibilidade." "Ativar" "Não ativar" + + + + "Permitir que o serviço %1$s tenha controlo total sobre o seu dispositivo?" "Se ativar o serviço %1$s, o dispositivo não utilizará o bloqueio de ecrã para otimizar a encriptação de dados." "O controlo total é adequado para aplicações que ajudam nas necessidades de acessibilidade, mas não para a maioria das aplicações." @@ -2038,13 +2042,17 @@ "Notificações" "Definições rápidas" "Caixa de diálogo de energia" - "Ativar/desativar o ecrã dividido" "Ecrã de bloqueio" "Captura de ecrã" - "Menu Acessibilidade" + + + + "Barra de legendas da aplicação %1$s." "%1$s foi colocado no contentor RESTRITO." "%1$s:" + + "Conversa" "Conversa de grupo" "> %1$d" diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 624560ec822c..ae2b5216f742 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1631,6 +1631,10 @@ "Manter as duas teclas de volume pressionadas por alguns segundos ativa o serviço %1$s, um recurso de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nÉ possível trocar o uso desse atalho para outro recurso em \"Config. > Acessibilidade\"." "Ativar" "Não ativar" + + + + "Permitir que o %1$s tenha controle total do seu dispositivo?" "Se o %1$s for ativado, o dispositivo não usará o bloqueio de tela para melhorar a criptografia de dados." "O controle total é adequado para apps que ajudam você com as necessidades de acessibilidade, mas não para a maioria dos apps." @@ -2038,13 +2042,17 @@ "Notificações" "Configurações rápidas" "Caixa de diálogo de liga/desliga" - "Ativar tela dividida" "Bloquear tela" "Capturar tela" - "Menu de acessibilidade" + + + + "Barra de legendas do app %1$s." "%1$s foi colocado no intervalo \"RESTRITO\"" "%1$s:" + + "Conversa" "Conversa em grupo" "+ de %1$d" diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 73c3e42696dc..7455e9eeba21 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1653,6 +1653,10 @@ "Dacă apăsați ambele taste de volum câteva secunde, activați funcția de accesibilitate %1$s. Acest lucru poate schimba funcționarea dispozitivului.\n\nPuteți alege altă funcție pentru această comandă în Setări > Accesibilitate." "Activați" "Nu activați" + + + + "Permiteți serviciului %1$s să aibă control total asupra dispozitivului dvs.?" "Dacă activați %1$s, dispozitivul nu va folosi blocarea ecranului pentru a îmbunătăți criptarea datelor." "Controlul total este adecvat pentru aplicații care vă ajută cu accesibilitatea, însă nu pentru majoritatea aplicaților." @@ -2072,13 +2076,17 @@ "Notificări" "Setări rapide" "Power Dialog" - "Activați ecranul împărțit" "Ecran de blocare" "Captură de ecran" - "Meniul Accesibilitate" + + + + "Bară cu legenda pentru %1$s." "%1$s a fost adăugat la grupul RESTRICȚIONATE" "%1$s:" + + "Conversație" "Conversație de grup" "%1$d+" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index a6d8f2504d5a..dd32364ecf04 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1675,6 +1675,10 @@ "Чтобы включить функцию \"%1$s\", нажмите обе кнопки регулировки громкости на несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nЧтобы назначить это сочетание клавиш другой функции, перейдите в настройки и выберите \"Специальные возможности\"." "Включить" "Не включать" + + + + "Предоставить сервису \"%1$s\" полный контроль над устройством?" "Если включить сервис \"%1$s\", устройство не будет использовать блокировку экрана для защиты данных." "Полный контроль нужен приложениям для реализации специальных возможностей и не нужен большинству остальных." @@ -2106,13 +2110,17 @@ "Уведомления" "Быстрые настройки" "Диалоговое окно питания" - "Включить или выключить разделение экрана" "Заблокированный экран" "Скриншот" - "Меню специальных возможностей" + + + + "Строка субтитров в приложении \"%1$s\"." "Приложение \"%1$s\" помещено в категорию с ограниченным доступом." "%1$s:" + + "Чат" "Групповой чат" "%1$d+" diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 3a8639e1e2f1..7a912fcc8803 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -1633,6 +1633,10 @@ "හඬ පරිමා යතුරු දෙකම තත්පර කීපයකට පහළට අල්ලාගෙන සිටීම ප්‍රවේශ්‍යතා විශේෂාංගයක් වන %1$s ක්‍රියාත්මක කරයි. මෙය ඔබේ උපාංගය ක්‍රියා කරන ආකාරය වෙනස් කළ හැකිය.\n\nඔබට මෙම කෙටිමග සැකසීම් > ප්‍රවේශ්‍යතාව හි තවත් විශේෂාංගයකට වෙනස් කළ හැකිය." "ක්‍රියාත්මක කරන්න" "ක්‍රියාත්මක නොකරන්න" + + + + "%1$s හට ඔබේ උපාංගයේ සම්පූර්ණ පාලනය තබා ගැනීමට ඉඩ දෙන්නද?" "ඔබ %1$s ක්‍රියාත්මක කරන්නේ නම්, දත්ත සංකේතනය වැඩිදියුණු කිරීමට ඔබේ උපාංගය ඔබේ තිර අගුල භාවිත නොකරනු ඇත." "පූර්ණ පාලනය ඔබට ප්‍රවේශ්‍යතා අවශ්‍යතා සමඟ උදවු කරන යෙදුම් සඳහා සුදුසු වන නමුත් බොහෝ යෙදුම් සඳහා සුදුසු නොවේ." @@ -2040,13 +2044,17 @@ "දැනුම්දීම්" "ඉක්මන් සැකසීම්" "බල සංවාදය" - "බෙදුම් තිරය ටොගල කරන්න" "අගුලු තිරය" "තිර රුව" - "ප්‍රවේශ්‍යතා මෙනුව" + + + + "%1$s හි සිරස්තල තීරුව." "%1$s අවහිර කළ බාල්දියට දමා ඇත" "%1$s:" + + "සංවාදය" "සමූහ සංවාදය" "%1$d+" diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index dee51d552503..0c0e2f50a042 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1675,6 +1675,10 @@ "Pridržaním oboch klávesov hlasitosti na niekoľko sekúnd zapnete funkciu dostupnosti %1$s. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nTúto skratku môžete zmeniť na inú funkciu v časti Nastavenia > Dostupnosť." "Zapnúť" "Nezapínať" + + + + "Chcete povoliť službe %1$s úplnú kontrolu nad zariadením?" "Ak zapnete službu %1$s, vaše zariadenie nezvýši úroveň šifrovania údajov zámkou obrazovky." "Úplná kontrola je vhodná pre aplikácie, ktoré vám pomáhajú s dostupnosťou, ale nie pre väčšinu aplikácií." @@ -2106,13 +2110,17 @@ "Upozornenia" "Rýchle nastavenia" "Dialógové okno napájania" - "Prepnúť rozdelenú obrazovku" "Uzamknúť obrazovku" "Snímka obrazovky" - "Ponuka Dostupnosť" + + + + "Popis aplikácie %1$s." "Balík %1$s bol vložený do kontajnera OBMEDZENÉ" "%1$s:" + + "Konverzácia" "Skupinová konverzácia" "%1$d+" diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 3ac05cf352ea..b37bece93e83 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1675,6 +1675,10 @@ "Če za nekaj sekund pridržite obe tipki za glasnost, boste vklopili storitev %1$s, ki je funkcija za ljudi s posebnimi potrebami. To lahko spremeni način delovanja naprave.\n\nTo bližnjico lahko v meniju »Nastavitve« > »Funkcije za ljudi s posebnimi potrebami« spremenite, da bo uporabljena za drugo funkcijo." "Vklopi" "Ne vklopi" + + + + "Ali storitvi %1$s dovolite popoln nadzor nad svojo napravo?" "Če vklopite storitev %1$s, vaša naprava ne bo uporabljala zaklepanja zaslona za izboljšanje šifriranja podatkov." "Popoln nadzor je ustrezen za aplikacije, ki vam pomagajo pri funkcijah za ljudi s posebnimi potrebami, vendar ne za večino aplikacij." @@ -2106,13 +2110,17 @@ "Obvestila" "Hitre nastavitve" "Pogovorno okno o porabi energije" - "Preklop razdeljenega zaslona" "Zaklenjen zaslon" "Posnetek zaslona" - "Meni s funkcijami za ljudi s posebnimi potrebami" + + + + "Vrstica s podnapisi aplikacije %1$s." "Paket %1$s je bil dodan v segment OMEJENO" "%1$s:" + + "Pogovor" "Skupinski pogovor" "%1$d+" diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 6bf4306ed2d3..f19d6e4bb065 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1631,6 +1631,10 @@ "Mbajtja shtypur e dy tasteve të volumit për pak sekonda aktivizon %1$s, një veçori të qasshmërisë. Kjo mund të ndryshojë mënyrën se si funksionon pajisja jote.\n\nMund të ndryshosh këtë shkurtore te një veçori tjetër te Cilësimet > Qasshmëria." "Aktivizo" "Mos e aktivizo" + + + + "Do të lejosh që %1$s të ketë kontroll të plotë të pajisjes sate?" "Nëse aktivizon %1$s, pajisja nuk do të përdorë kyçjen e ekranit për të përmirësuar enkriptimin e të dhënave." "Kontrolli i plotë është i përshtatshëm për aplikacionet që të ndihmojnë me nevojat e qasshmërisë, por jo për shumicën e aplikacioneve." @@ -2038,13 +2042,17 @@ "Njoftimet" "Cilësimet e shpejta" "Dialogu i energjisë" - "Kalo tek ekrani i ndarë" "Ekrani i kyçjes" "Pamja e ekranit" - "Menyja e qasshmërisë" + + + + "Shiriti i nëntitullit të %1$s." "%1$s është vendosur në grupin E KUFIZUAR" "%1$s:" + + "Biseda" "Bisedë në grup" "%1$d+" diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index e1e62b5c0656..1599cf57c755 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1653,6 +1653,10 @@ "Ако задржите оба тастера за јачину звука пар секунди, укључује се %1$s, функција приступачности. То може да промени начин рада уређаја.\n\nМожете да промените функцију на коју се односи ова пречица у одељку Подешавања > Приступачност." "Укључи" "Не укључуј" + + + + "Желите ли да дозволите да услуга %1$s има потпуну контролу над уређајем?" "Ако укључите услугу %1$s, уређај неће користити закључавање екрана да би побољшао шифровање података." "Потпуна контрола је примерена за апликације које вам помажу код услуга приступачности, али не и за већину апликација." @@ -2072,13 +2076,17 @@ "Обавештења" "Брза подешавања" "Дијалог напајања" - "Укључите/искључите подељени екран" "Закључани екран" "Снимак екрана" - "Мени Приступачност" + + + + "Трака са насловима апликације %1$s." "Пакет %1$s је додат у сегмент ОГРАНИЧЕНО" "%1$s:" + + "Конверзација" "Групна конверзација" "%1$d+" diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 2e6a848be5cc..d5458216d9ab 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1631,6 +1631,10 @@ "Om du trycker ned båda volymknapparna i ett par sekunder aktiveras %1$s, en tillgänglighetsfunktion. Det kan leda till att enheten fungerar annorlunda.\n\nDu kan ändra vilken funktion som ska aktiveras med genvägen under Inställningar > Tillgänglighet." "Aktivera" "Aktivera inte" + + + + "Vill du tillåta att %1$s får fullständig kontroll över enheten?" "Om du aktiverar %1$s används inte skärmlåset för att förbättra datakryptering på enheten." "Fullständig kontroll lämpar sig för appar med tillgänglighetsfunktioner, men är inte lämplig för de flesta appar." @@ -2038,13 +2042,17 @@ "Aviseringar" "Snabbinställningar" "Dialogruta för ström" - "Aktivera och inaktivera delad skärm" "Låsskärm" "Skärmdump" - "Tillgänglighetsmenyn" + + + + "Textningsfält för %1$s." "%1$s har placerats i hinken RESTRICTED" "%1$s:" + + "Konversation" "Gruppkonversation" "%1$d+" diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 355ad13cf0a5..c42aad000b0d 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1631,6 +1631,10 @@ "Hatua ya kushikilia chini vitufe vyote viwili vya sauti kwa sekunde chache huwasha %1$s, kipengele cha ufikivu. Huenda hatua hii ikabadilisha jinsi kifaa chako kinavyofanya kazi.\n\nUnaweza kubadilisha njia hii ya mkato iwe kipengele kingine katika Mipangilio > Ufikivu." "Washa" "Usiwashe" + + + + "Ungependa kuruhusu %1$s idhibiti kifaa chako kikamilifu?" "Ukiwasha %1$s, kifaa chako hakitatumia kipengele cha kufunga skrini yako ili kuboresha usimbaji wa data kwa njia fiche." "Udhibiti kamili unafaa kwa programu zinazokusaidia kwa mahitaji ya ufikivu, ila si kwa programu nyingi." @@ -2038,13 +2042,17 @@ "Arifa" "Mipangilio ya Haraka" "Kidirisha cha Nishati" - "Geuza Skrini Iliyogawanywa" "Skrini Iliyofungwa" "Picha ya skrini" - "Menyu ya Ufikivu" + + + + "Upau wa manukuu wa %1$s." "%1$s kimewekwa katika kikundi KILICHODHIBITIWA" "%1$s:" + + "Mazungumzo" "Mazungumzo ya Kikundi" "%1$d+" diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 2a03f365e9d7..87fa6c6b4e02 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1631,6 +1631,10 @@ "இரண்டு ஒலியளவு விசைகளையும் சில விநாடிகள் பிடித்திருப்பதால் அணுகல்தன்மை அம்சமான %1$s ஆன் ஆகும். இதனால் உங்கள் சாதனம் வேலை செய்யும் முறை மாறக்கூடும்.\n\nஅமைப்புகள் > அணுகல்தன்மைக்குச் சென்று இந்த ஷார்ட்கட்டை வேறு அம்சத்திற்கு மாற்ற முடியும்." "ஆன் செய்" "ஆன் செய்யாதே" + + + + "உங்கள் சாதனத்தை முழுமையாகக் கட்டுப்படுத்த %1$s சேவையை அனுமதிக்க வேண்டுமா?" "%1$s சேவையை ஆன் செய்தால் தரவு என்க்ரிப்ஷனை மேம்படுத்த சாதனம் திரைப் பூட்டைப் பயன்படுத்தாது." "உங்களுக்கு உதவக்கூடிய ஆப்ஸுக்குக் தேவையான அணுகல்தன்மையை அளித்து முழுக் கட்டுப்பாட்டையும் அளிக்கலாம், ஆனால் பெரும்பாலான ஆப்ஸுக்கு இது பொருந்தாது." @@ -2038,13 +2042,17 @@ "அறிவிப்புகள்" "விரைவு அமைப்புகள்" "பவர் உரையாடல்" - "திரைப் பிரிப்பை நிலைமாற்று" "பூட்டுத் திரை" "ஸ்கிரீன்ஷாட்" - "அணுகல்தன்மை மெனு" + + + + "%1$s ஆப்ஸின் தலைப்புப் பட்டி." "%1$s என்பதை வரம்பிடப்பட்ட பக்கெட்திற்குள் சேர்க்கப்பட்டது" "%1$s:" + + "உரையாடல்" "குழு உரையாடல்" "%1$d+" diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index da5b77cfce6c..0773bd990e78 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1631,6 +1631,10 @@ "రెండు వాల్యూమ్ కీలను కొన్ని సెకన్ల పాటు నొక్కి పట్టుకోవడం ద్వారా యాక్సెసిబిలిటీ అయిన %1$s ఆన్ అవుతుంది. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nసెట్టింగ్‌లు > యాక్సెసిబిలిటీలో, వేరొక ఫీచర్‌ను ప్రారంభించేలా ఈ షార్ట్ కట్‌ను మీరు మార్చవచ్చు." "ఆన్ చేయి" "ఆన్ చేయకండి" + + + + "%1$sకి మీ పరికరంపై పూర్తి నియంత్రణను ఇవ్వాలనుకుంటున్నారా?" "మీరు %1$sని ఆన్ చేస్తే, డేటా ఎన్‌క్రిప్షన్‌ను మెరుగుపరచడానికి మీ పరికరం మీ స్క్రీన్ లాక్‌ను ఉపయోగించదు." "అవసరమైన యాక్సెస్ సామర్ధ్యం కోసం యాప్‌లకు పూర్తి నియంత్రణ ఇవ్వడం తగిన పనే అయినా, అన్ని యాప్‌లకు అలా ఇవ్వడం సరికాదు." @@ -2038,13 +2042,17 @@ "నోటిఫికేషన్‌లు" "శీఘ్ర సెట్టింగ్‌లు" "పవర్ డైలాగ్‌ను తెరువు" - "స్క్రీన్ విభజనను టోగుల్ చేయి" "స్క్రీన్‌ను లాక్ చేయి" "స్క్రీన్‌షాట్" - "యాక్సెసిబిలిటీ మెను" + + + + "%1$s క్యాప్షన్ బార్." "%1$s పరిమితం చేయబడిన బకెట్‌లో ఉంచబడింది" "%1$s:" + + "సంభాషణ" "గ్రూప్ సంభాషణ" "%1$d+" diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index ff5557de5e91..a700e7540c4e 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1631,6 +1631,10 @@ "การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 2-3 วินาทีจะเปิด %1$s ซึ่งเป็นฟีเจอร์การช่วยเหลือพิเศษ การดำเนินการนี้อาจเปลี่ยนแปลงลักษณะการทำงานของอุปกรณ์\n\nคุณแก้ไขทางลัดนี้ให้เปิดฟีเจอร์อื่นได้ในการตั้งค่า > การช่วยเหลือพิเศษ" "เปิด" "ไม่ต้องเปิด" + + + + "อนุญาตให้ %1$s ควบคุมอุปกรณ์อย่างเต็มที่ไหม" "หากคุณเปิด %1$s อุปกรณ์ของคุณจะไม่ใช้ล็อกหน้าจอเพื่อปรับปรุงการเข้ารหัสข้อมูล" "การควบคุมอย่างเต็มที่เหมาะสำหรับแอปที่ช่วยคุณในเรื่องความต้องการความช่วยเหลือพิเศษแต่ไม่เหมาะสำหรับแอปส่วนใหญ่" @@ -2038,13 +2042,17 @@ "การแจ้งเตือน" "การตั้งค่าด่วน" "กล่องโต้ตอบพลังงาน" - "เปิด/ปิดการแบ่งหน้าจอ" "หน้าจอล็อก" "ภาพหน้าจอ" - "เมนูการช่วยเหลือพิเศษ" + + + + "แถบคำบรรยาย %1$s" "ใส่ %1$s ในที่เก็บข้อมูลที่ถูกจำกัดแล้ว" "%1$s:" + + "การสนทนา" "บทสนทนากลุ่ม" "%1$d+" diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 5de452546922..d85ceb0ed9a9 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1631,6 +1631,10 @@ "Mao-on ang feature ng pagiging accessible na %1$s kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nPuwede mong palitan ng ibang feature ang shortcut na ito sa Mga Setting > Pagiging Accessible." "I-on" "Huwag i-on" + + + + "Bigyan ang %1$s ng ganap na kontrol sa iyong device?" "Kung io-on mo ang %1$s, hindi gagamitin ng iyong device ang lock ng screen mo para pahusayin ang pag-encrypt ng data." "Naaangkop ang ganap na kontrol sa mga app na tumutulong sa mga pangangailangan mo sa accessibility, pero hindi sa karamihan ng mga app." @@ -2038,13 +2042,17 @@ "Mga Notification" "Mga Mabilisang Setting" "Dialog ng Power" - "I-toggle ang Split Screen" "Lock Screen" "Screenshot" - "Menu ng Accessibility" + + + + "Caption bar ng %1$s." "Inilagay ang %1$s sa PINAGHIHIGPITANG bucket" "%1$s:" + + "Pag-uusap" "Panggrupong Pag-uusap" "%1$d+" diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index b10d19323066..d87a8541cf69 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1631,6 +1631,10 @@ "Ses tuşlarının ikisini birden birkaç saniyeliğine basılı tutmak %1$s erişilebilirlik özelliğini etkinleştirir. Bu, cihazınızın çalışma şeklini değiştirebilir.\n\nBu kısayolu, Ayarlar > Erişilebilirlik\'te başka bir özellikle değiştirebilirsiniz." "Etkinleştir" "Etkinleştirme" + + + + "%1$s hizmetinin cihazınızı tamamen kontrol etmesine izin veriyor musunuz?" "%1$s hizmetini açarsanız cihazınız veri şifrelemeyi geliştirmek için ekran kilidinizi kullanmaz." "Erişebilirlik ihtiyaçlarınıza yardımcı olan uygulamalara tam kontrol verilmesi uygundur ancak diğer pek çok uygulama için uygun değildir." @@ -2038,13 +2042,17 @@ "Bildirimler" "Hızlı Ayarlar" "Güç İletişim Kutusu" - "Bölünmüş Ekranı aç/kapat" "Kilit Ekranı" "Ekran görüntüsü" - "Erişilebilirlik Menüsü" + + + + "%1$s uygulamasının başlık çubuğu." "%1$s KISITLANMIŞ gruba yerleştirildi" "%1$s:" + + "Görüşme" "Grup Görüşmesi" "%1$d+" diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 55d6eca30d89..ff8f1ecf45a1 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1675,6 +1675,10 @@ "Якщо втримувати обидві клавіші гучності впродовж кількох секунд, буде ввімкнено спеціальні можливості – %1$s. Це може вплинути на роботу пристрою.\n\nДля цієї комбінації клавіш можна вибрати іншу функцію в меню \"Налаштування > Спеціальні можливості\"." "Увімкнути" "Не вмикати" + + + + "Надати сервісу %1$s повний доступ до вашого пристрою?" "Якщо ви ввімкнете сервіс %1$s, дані на пристрої не захищатимуться екраном блокування." "Повний доступ доречний для додатків, які надають спеціальні можливості, але його не варто відкривати для більшості інших додатків." @@ -2106,13 +2110,17 @@ "Сповіщення" "Швидкі налаштування" "Відкрити вікно" - "Розділити екран" "Заблокувати екран" "Знімок екрана" - "Меню спеціальних можливостей" + + + + "Смуга із субтитрами для додатка %1$s." "Пакет \"%1$s\" додано в сегмент з обмеженнями" "%1$s:" + + "Чат" "Груповий чат" "%1$d+" diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index d4728a107446..14de695b9351 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -1631,6 +1631,10 @@ "والیوم کی دونوں کلیدوں کو کچھ سیکنڈز تک دبائے رکھنے سے %1$s ایکسیسبیلٹی خصوصیت آن ہو جاتی ہے۔ اس سے آپ کے آلے کے کام کرنے کا طریقہ تبدیل ہو سکتا ہے۔\n\nآپ ترتیبات اور ایکسیسبیلٹی میں دیگر خصوصیت کے لیے اس شارٹ کٹ کو تبدیل کر سکتے ہیں۔" "آن کریں" "آن نہ کریں" + + + + "%1$s کو آپ کے آلے کا مکمل کنٹرول حاصل کرنے کی اجازت دیں؟" "اگر آپ %1$s کو آن کرتے ہیں تو آپ کا آلہ ڈیٹا کی مرموزکاری کو بڑھانے کیلئے آپ کی اسکرین کا قفل استعمال نہیں کرے گا۔" "مکمل کنٹرول ان ایپس کے لیے مناسب ہے جو ایکسیسبیلٹی کی ضروریات میں آپ کی مدد کرتی ہیں، لیکن زیادہ تر ایپس کیلئے مناسب نہیں۔" @@ -2038,13 +2042,17 @@ "اطلاعات" "فوری ترتیبات" "پاور ڈائیلاگ" - "اسپلٹ اسکرین ٹوگل کریں" "مقفل اسکرین" "اسکرین شاٹ" - "ایکسیسبیلٹی مینو" + + + + "%1$s کی کیپشن بار۔" "%1$s کو پابند کردہ بکٹ میں رکھ دیا گیا ہے" "%1$s:" + + "گفتگو" "گروپ گفتگو" "+%1$d" diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index c212627a3206..5fc642e987ce 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1631,6 +1631,10 @@ "%1$s funksiyasini yoqish uchun ikkala tovush tugmalarini bir necha soniya bosib turing. Qurilmangiz ishlashida oʻzgarish yuz berishi mumkin.\n\nBu tezkor tugmalarni boshqa funksiyaga Sozlamalar ichidagi Maxsus imkoniyatlar orqali tayinlash mumkin." "Yoqilsin" "Yoqilmasin" + + + + "%1$s xizmatiga qurilmangizni boshqarish uchun toʻliq ruxsat berilsinmi?" "Agar %1$s xizmatini yoqsangiz, qurilmangiz maʼlumotlarni shifrlashni kuchaytirish uchun ekran qulfidan foydalanmaydi." "Toʻliq nazorat maxsus imkoniyatlar bilan ishlovchi ilovalar uchun mos, lekin barcha ilovalar uchun emas." @@ -2038,13 +2042,17 @@ "Bildirishnomalar" "Tezkor sozlamalar" "Quvvat muloqot oynasi" - "Ekranni ikkiga ajratish tugmasi" "Ekran qulfi" "Skrinshot" - "Maxsus imkoniyatlar menyusi" + + + + "%1$s taglavhalar paneli." "%1$s cheklangan turkumga joylandi" "%1$s:" + + "Suhbat" "Guruh suhbati" "%1$d+" diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 3b2de1c5e691..b6e1f2484105 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1631,6 +1631,10 @@ "Thao tác nhấn và giữ cả hai phím âm lượng trong vài giây sẽ bật %1$s, một tính năng hỗ trợ tiếp cận. Việc bật tính năng này có thể thay đổi cách thiết bị của bạn hoạt động.\n\nBạn có thể chuyển phím tắt này thành một tính năng khác trong phần Cài đặt > Hỗ trợ tiếp cận." "Bật" "Không bật" + + + + "Cho phép %1$s toàn quyền kiểm soát thiết bị của bạn?" "Nếu bạn bật %1$s, thiết bị của bạn sẽ không dùng phương thức khóa màn hình để tăng cường mã hóa dữ liệu." "Bạn chỉ nên cấp toàn quyền kiểm soát cho những ứng dụng trợ giúp mình khi cần hỗ trợ tiếp cận, chứ không nên cấp cho hầu hết các ứng dụng." @@ -2038,13 +2042,17 @@ "Thông báo" "Cài đặt nhanh" "Hộp thoại thao tác với nguồn" - "Bật/tắt chế độ chia đôi màn hình" "Khóa màn hình" "Chụp ảnh màn hình" - "Trình đơn Hỗ trợ tiếp cận" + + + + "Thanh phụ đề của %1$s." "Đã đưa %1$s vào bộ chứa BỊ HẠN CHẾ" "%1$s:" + + "Cuộc trò chuyện" "Cuộc trò chuyện nhóm" "%1$d+" diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index dd8875944a10..790ac9c5f138 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1631,6 +1631,10 @@ "同时按住两个音量键几秒钟,即可开启%1$s无障碍功能。这样做可能会改变您设备的工作方式。\n\n您可以在“设置”>“无障碍”中将此快捷方式更改为开启另一项功能。" "开启" "不开启" + + + + "要允许%1$s完全控制您的设备吗?" "如果您开启%1$s,您的设备将无法使用屏幕锁定功能来增强数据加密效果。" "对于能满足您的无障碍功能需求的应用,可授予其完全控制权限;但对大部分应用来说,都不适合授予此权限。" @@ -2038,13 +2042,17 @@ "通知" "快捷设置" "电源对话框" - "开启/关闭分屏" "锁定屏幕" "屏幕截图" - "无障碍功能菜单" + + + + "%1$s的标题栏。" "%1$s 已被放入受限存储分区" "%1$s:" + + "对话" "群组对话" "%1$d+" diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 4d790c465314..57c1c66a53a6 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1631,6 +1631,10 @@ "同時按下兩個音量鍵幾秒,以開啟 %1$s 無障礙功能。這可能會變更裝置的運作。\n\n您可在「設定」>「無障礙功能」中變更此快速鍵。" "開啟" "不要開啟" + + + + "要允許 %1$s 完全控制您的裝置嗎?" "如果您開啟 %1$s,裝置將無法使用螢幕鎖定功能加強資料加密。" "為您提供所需無障礙功能的應用程式有權完全控制您的裝置,但大部分應用程式均沒有此權限。" @@ -2038,13 +2042,17 @@ "通知" "快速設定" "電源對話框" - "切換分割螢幕" "將畫面上鎖" "螢幕截圖" - "無障礙功能選單" + + + + "「%1$s」的說明列。" "%1$s 已納入受限制的儲存區" "%1$s:" + + "對話" "群組對話" "%1$d+" diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index e18b51e91a2a..406b94fbc0f6 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1631,6 +1631,10 @@ "同時按住音量調高鍵和調低鍵數秒,即可開啟「%1$s」無障礙功能。這麼做可能會改變裝置的運作方式。\n\n你可以在 [設定] > [無障礙設定] 中變更這個快速鍵觸發的功能。" "開啟" "不要開啟" + + + + "要將裝置的完整控制權授予「%1$s」嗎?" "如果你開啟「%1$s」,裝置將無法使用螢幕鎖定功能強化資料加密。" "如果你有無障礙服務需求,可以將完整控制權授予具有相關功能的應用程式,但請勿將完整控制權授予大多數的應用程式。" @@ -2038,13 +2042,17 @@ "通知" "快速設定" "開啟電源對話方塊" - "切換分割畫面模式" "螢幕鎖定" "擷取螢幕畫面" - "無障礙選單" + + + + "「%1$s」的說明文字列。" "已將「%1$s」移入受限制的值區" "%1$s:" + + "對話" "群組對話" "%1$d+" diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index a9af563ee1e5..d7b3ee72d4f4 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1631,6 +1631,10 @@ "Ukubambela phansi bobabili okhiye bevolumu amasekhondi ambalwa kuvula i-%1$s, eyisici sokufinyelela Lokhu kungashintsha indlela idivayisi yakho esebenza ngayo.\n\nUngashintshela lesi sinqamuleli kwesinye isici Kuzilungiselelo > Ukufinyeleleka." "Vula" "Ungavuli" + + + + "Vumela i-%1$s ukuthola ukulawula okuphelele kwedivayisi yakho?" "Uma uvula i-%1$s, idivayisi yakho ngeke isebenzise ukukhiya kwakho kwesikrini sakho ukuthuthukisa ukubethelwa kwedatha." "Ukulawula okugcwele kulungele izinhlelo zokusebenza ezikusiza ngezidingo zokufinyelela, kodwa hhayi izinhlelo zokusebenza eziningi." @@ -2038,13 +2042,17 @@ "Izaziso" "Izilungiselelo ezisheshayo" "Ibhokisi lamandla" - "Guqula ukuhlukanisa isikrini" "Khiya isikrini" "Isithombe-skrini" - "Imenyu yokufinyeleleka" + + + + "Ibha yamazwibela we-%1$s." "I-%1$s ifakwe kubhakede LOKUKHAWULELWE" "%1$s:" + + "Ingxoxo" "Ingxoxo Yeqembu" "%1$d+" -- GitLab From e067ddfc05c495eebf7e07664774a0776ec7813d Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Fri, 24 Apr 2020 18:28:25 -0700 Subject: [PATCH 035/166] Shift Primary stack as well as secondary during ime adjustment Previously, was only 'adjusting' the secondary split for ime. So the secondary had functional insets and drag/drop. However, the primary wasn't being adjusted (accorcding to WM). This caused its insets to be calculated against its originla position (which allowed ime insets to potentially overlap it) and it meant that WM didn't use its visual position when calculating drag/drop coordinates. So, this does the same with the primary stack as what was done with secondary. However, doing this required some other fixes in WM. Basically, if appbounds is explicitly overridden, just use them instead of intersecting with parent. Also, WSA doesn't apply crop for splits (maybe it shouldn't apply crop to anything anymore). Additionally, insets calculation was applying top insets to the bottom if the inset frame's top wasn't exactly equal to the window's top -- because of a catch-all else condition. So this adds checks for matching the bottoms as well. Bug: 154056542 Bug: 151862790 Test: Open two apps in split screen and open IME in secondary. Drag from secondary to primary. Change-Id: I2391783e726e4a1c8ed3150628af2f398218fe90 --- core/java/android/view/InsetsSource.java | 13 +++++-- .../systemui/stackdivider/Divider.java | 14 +++++++ .../core/java/com/android/server/wm/Task.java | 38 ++++++++++--------- .../server/wm/WindowStateAnimator.java | 4 +- .../server/wm/InsetsSourceProviderTest.java | 21 ++++++++++ 5 files changed, 68 insertions(+), 22 deletions(-) diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 033ccef3666d..a501779637bc 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -136,20 +136,25 @@ public class InsetsSource implements Parcelable { if (mTmpFrame.width() == relativeFrame.width()) { if (mTmpFrame.top == relativeFrame.top) { return Insets.of(0, mTmpFrame.height(), 0, 0); - } else { + } else if (mTmpFrame.bottom == relativeFrame.bottom) { return Insets.of(0, 0, 0, mTmpFrame.height()); } + // TODO: remove when insets are shell-customizable. + // This is a hack that says "if this is a top-inset (eg statusbar), always apply it + // to the top". It is used when adjusting primary split for IME. + if (mTmpFrame.top == 0) { + return Insets.of(0, mTmpFrame.height(), 0, 0); + } } // Intersecting at left/right else if (mTmpFrame.height() == relativeFrame.height()) { if (mTmpFrame.left == relativeFrame.left) { return Insets.of(mTmpFrame.width(), 0, 0, 0); - } else { + } else if (mTmpFrame.right == relativeFrame.right) { return Insets.of(0, 0, mTmpFrame.width(), 0); } - } else { - return Insets.NONE; } + return Insets.NONE; } /** diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index f36f8c131848..f08fbf1171a1 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -263,11 +263,25 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, wct.setScreenSizeDp(mSplits.mSecondary.token, mSplits.mSecondary.configuration.screenWidthDp, mSplits.mSecondary.configuration.screenHeightDp); + + wct.setBounds(mSplits.mPrimary.token, mSplitLayout.mAdjustedPrimary); + adjustAppBounds = new Rect(mSplits.mPrimary.configuration + .windowConfiguration.getAppBounds()); + adjustAppBounds.offset(0, mSplitLayout.mAdjustedPrimary.top + - mSplitLayout.mPrimary.top); + wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds); + wct.setScreenSizeDp(mSplits.mPrimary.token, + mSplits.mPrimary.configuration.screenWidthDp, + mSplits.mPrimary.configuration.screenHeightDp); } else { wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary); wct.setAppBounds(mSplits.mSecondary.token, null); wct.setScreenSizeDp(mSplits.mSecondary.token, SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); + wct.setBounds(mSplits.mPrimary.token, mSplitLayout.mPrimary); + wct.setAppBounds(mSplits.mPrimary.token, null); + wct.setScreenSizeDp(mSplits.mPrimary.token, + SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); } WindowOrganizer.applyTransaction(wct); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 44049b84de40..3268fc28481c 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2299,27 +2299,31 @@ class Task extends WindowContainer { insideParentBounds = parentBounds.contains(resolvedBounds); } + // Non-null compatibility insets means the activity prefers to keep its original size, so + // out bounds doesn't need to be restricted by the parent or current display + final boolean customContainerPolicy = compatInsets != null; + Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); if (outAppBounds == null || outAppBounds.isEmpty()) { + // App-bounds hasn't been overridden, so calculate a value for it. inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds); outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); - } - // Non-null compatibility insets means the activity prefers to keep its original size, so - // the out bounds doesn't need to be restricted by the parent or current display. - final boolean customContainerPolicy = compatInsets != null; - if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) { - final Rect containingAppBounds; - if (insideParentBounds) { - containingAppBounds = parentConfig.windowConfiguration.getAppBounds(); - } else { - // Restrict appBounds to display non-decor rather than parent because the override - // bounds are beyond the parent. Otherwise, it won't match the overridden bounds. - final TaskDisplayArea displayArea = getDisplayArea(); - containingAppBounds = displayArea != null - ? displayArea.getWindowConfiguration().getAppBounds() : null; - } - if (containingAppBounds != null && !containingAppBounds.isEmpty()) { - outAppBounds.intersect(containingAppBounds); + + if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) { + final Rect containingAppBounds; + if (insideParentBounds) { + containingAppBounds = parentConfig.windowConfiguration.getAppBounds(); + } else { + // Restrict appBounds to display non-decor rather than parent because the + // override bounds are beyond the parent. Otherwise, it won't match the + // overridden bounds. + final TaskDisplayArea displayArea = getDisplayArea(); + containingAppBounds = displayArea != null + ? displayArea.getWindowConfiguration().getAppBounds() : null; + } + if (containingAppBounds != null && !containingAppBounds.isEmpty()) { + outAppBounds.intersect(containingAppBounds); + } } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index bfe3b2890bc1..0e83beed6b90 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -57,6 +57,7 @@ import static com.android.server.wm.WindowStateAnimatorProto.SURFACE; import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; +import android.app.WindowConfiguration; import android.content.Context; import android.graphics.Matrix; import android.graphics.PixelFormat; @@ -787,7 +788,8 @@ class WindowStateAnimator { return false; } - if (w.getWindowConfiguration().tasksAreFloating()) { + if (w.getWindowConfiguration().tasksAreFloating() + || WindowConfiguration.isSplitScreenWindowingMode(w.getWindowingMode())) { return false; } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index f8117573efdd..ca016761438b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -189,4 +189,25 @@ public class InsetsSourceProviderTest extends WindowTestsBase { mProvider.onInsetsModified(target, state.getSource(ITYPE_STATUS_BAR)); assertTrue(mSource.isVisible()); } + + @Test + public void testInsetGeometries() { + final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); + statusBar.getFrameLw().set(0, 0, 500, 100); + statusBar.mHasSurface = true; + mProvider.setWindow(statusBar, null, null); + mProvider.onPostLayout(); + assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame()); + // Still apply top insets if window overlaps even if it's top doesn't exactly match + // the inset-window's top. + assertEquals(Insets.of(0, 100, 0, 0), + mProvider.getSource().calculateInsets(new Rect(0, -100, 500, 400), + false /* ignoreVisibility */)); + + // Don't apply left insets if window is left-of inset-window but still overlaps + statusBar.getFrameLw().set(100, 0, 0, 0); + assertEquals(Insets.of(0, 0, 0, 0), + mProvider.getSource().calculateInsets(new Rect(-100, 0, 400, 500), + false /* ignoreVisibility */)); + } } -- GitLab From a39cf78c63a1225490a74147c9a4dbb23b3c2865 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Tue, 28 Apr 2020 12:14:01 -0700 Subject: [PATCH 036/166] Add RoleControllerManagerTest. Fixes: 155119713 Test: atest RoleControllerManagerTest Change-Id: If90ba60cccbe1589365142a06d917a87b7a092ef --- api/test-current.txt | 5 ++++ .../app/role/RoleControllerManager.java | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/api/test-current.txt b/api/test-current.txt index 5d91adf6c060..a2fe6e3e83f3 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -732,6 +732,11 @@ package android.app.role { method public void onRoleHoldersChanged(@NonNull String, @NonNull android.os.UserHandle); } + public class RoleControllerManager { + method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer); + method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer); + } + public final class RoleManager { method @RequiresPermission("android.permission.OBSERVE_ROLE_HOLDERS") public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer); diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java index 16ddbc147d82..96a4debd3d6a 100644 --- a/core/java/android/app/role/RoleControllerManager.java +++ b/core/java/android/app/role/RoleControllerManager.java @@ -21,6 +21,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.annotation.TestApi; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; @@ -48,6 +49,7 @@ import java.util.function.Consumer; * @hide */ @SystemService(Context.ROLE_CONTROLLER_SERVICE) +@TestApi public class RoleControllerManager { private static final String LOG_TAG = RoleControllerManager.class.getSimpleName(); @@ -73,6 +75,8 @@ public class RoleControllerManager { * PackageManagerService lock in constructor. * * @see #createWithInitializedRemoteServiceComponentName(Handler, Context) + * + * @hide */ public static void initializeRemoteServiceComponentName(@NonNull Context context) { sRemoteServiceComponentName = getRemoteServiceComponentName(context); @@ -83,6 +87,8 @@ public class RoleControllerManager { * name so that we can avoid acquiring the PackageManagerService lock in constructor. * * @see #initializeRemoteServiceComponentName(Context) + * + * @hide */ @NonNull public static RoleControllerManager createWithInitializedRemoteServiceComponentName( @@ -113,6 +119,9 @@ public class RoleControllerManager { } } + /** + * @hide + */ public RoleControllerManager(@NonNull Context context) { this(getRemoteServiceComponentName(context), context.getMainThreadHandler(), context); } @@ -128,6 +137,8 @@ public class RoleControllerManager { /** * @see RoleControllerService#onGrantDefaultRoles() + * + * @hide */ public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer callback) { @@ -141,6 +152,8 @@ public class RoleControllerManager { /** * @see RoleControllerService#onAddRoleHolder(String, String, int) + * + * @hide */ public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { @@ -155,6 +168,8 @@ public class RoleControllerManager { /** * @see RoleControllerService#onRemoveRoleHolder(String, String, int) + * + * @hide */ public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { @@ -169,6 +184,8 @@ public class RoleControllerManager { /** * @see RoleControllerService#onClearRoleHolders(String, int) + * + * @hide */ public void onClearRoleHolders(@NonNull String roleName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { @@ -186,6 +203,8 @@ public class RoleControllerManager { * * @deprecated Use {@link #isApplicationVisibleForRole(String, String, Executor, Consumer)} * instead. + * + * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName, @@ -201,8 +220,11 @@ public class RoleControllerManager { /** * @see RoleControllerService#onIsApplicationVisibleForRole(String, String) + * + * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @TestApi public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer callback) { AndroidFuture operation = mRemoteService.postAsync(service -> { @@ -216,8 +238,11 @@ public class RoleControllerManager { /** * @see RoleControllerService#onIsRoleVisible(String) + * + * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @TestApi public void isRoleVisible(@NonNull String roleName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer callback) { AndroidFuture operation = mRemoteService.postAsync(service -> { -- GitLab From 0f390fb0fbdefdb3885061dd9538ccb5dd6ae8d1 Mon Sep 17 00:00:00 2001 From: Joshua Tsuji Date: Tue, 28 Apr 2020 15:20:10 -0400 Subject: [PATCH 037/166] Fix a11y issues. Test: use talkback w bubbles Fixes: 129522932 Fixes: 140367439 Fixes: 149539297 Change-Id: I8ca58df861a23365fdd3a2b7155161538f194d5c --- packages/SystemUI/res/values/strings.xml | 3 ++ .../android/systemui/bubbles/BubbleData.java | 3 +- .../systemui/bubbles/BubbleStackView.java | 45 +++++++++---------- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d639ed074240..61e8bfbad8d7 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -644,6 +644,9 @@ Notification dismissed. + + Bubble dismissed. + Notification shade. diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 35a4811110a8..b773bdbcc692 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -15,6 +15,7 @@ */ package com.android.systemui.bubbles; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; @@ -772,7 +773,7 @@ public class BubbleData { /** * The set of bubbles in row. */ - @VisibleForTesting(visibility = PRIVATE) + @VisibleForTesting(visibility = PACKAGE) public List getBubbles() { return Collections.unmodifiableList(mBubbles); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index d870c1191512..366d4a7345af 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -1106,6 +1106,8 @@ public class BubbleStackView extends FrameLayout { // R constants are not final so we cannot use switch-case here. if (action == AccessibilityNodeInfo.ACTION_DISMISS) { mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION); + announceForAccessibility( + getResources().getString(R.string.accessibility_bubble_dismissed)); return true; } else if (action == AccessibilityNodeInfo.ACTION_COLLAPSE) { mBubbleData.setExpanded(false); @@ -1136,32 +1138,29 @@ public class BubbleStackView extends FrameLayout { if (mBubbleData.getBubbles().isEmpty()) { return; } - Bubble topBubble = mBubbleData.getBubbles().get(0); - String appName = topBubble.getAppName(); - Notification notification = topBubble.getEntry().getSbn().getNotification(); - CharSequence titleCharSeq = notification.extras.getCharSequence(Notification.EXTRA_TITLE); - String titleStr = getResources().getString(R.string.notification_bubble_title); - if (titleCharSeq != null) { - titleStr = titleCharSeq.toString(); - } - int moreCount = mBubbleContainer.getChildCount() - 1; - // Example: Title from app name. - String singleDescription = getResources().getString( - R.string.bubble_content_description_single, titleStr, appName); + for (int i = 0; i < mBubbleData.getBubbles().size(); i++) { + final Bubble bubble = mBubbleData.getBubbles().get(i); + final String appName = bubble.getAppName(); + final Notification notification = bubble.getEntry().getSbn().getNotification(); + final CharSequence titleCharSeq = + notification.extras.getCharSequence(Notification.EXTRA_TITLE); - // Example: Title from app name and 4 more. - String stackDescription = getResources().getString( - R.string.bubble_content_description_stack, titleStr, appName, moreCount); + String titleStr = getResources().getString(R.string.notification_bubble_title); + if (titleCharSeq != null) { + titleStr = titleCharSeq.toString(); + } - if (mIsExpanded) { - // TODO(b/129522932) - update content description for each bubble in expanded view. - } else { - // Collapsed stack. - if (moreCount > 0) { - mBubbleContainer.setContentDescription(stackDescription); - } else { - mBubbleContainer.setContentDescription(singleDescription); + if (bubble.getIconView() != null) { + if (mIsExpanded || i > 0) { + bubble.getIconView().setContentDescription(getResources().getString( + R.string.bubble_content_description_single, titleStr, appName)); + } else { + final int moreCount = mBubbleContainer.getChildCount() - 1; + bubble.getIconView().setContentDescription(getResources().getString( + R.string.bubble_content_description_stack, + titleStr, appName, moreCount)); + } } } } -- GitLab From aae4bbf652713bc94e739f541548074137b91eed Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 28 Apr 2020 12:54:41 -0700 Subject: [PATCH 038/166] Import translations. DO NOT MERGE Change-Id: I79b3d6f94e71ff5a7636befd886f3892d4938a86 Auto-generated-cl: translation import --- .../SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml | 2 +- .../SettingsLib/RestrictedLockUtils/res/values-es/strings.xml | 4 ++-- .../SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml | 4 ++-- .../SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml | 2 +- .../RestrictedLockUtils/res/values-pt-rPT/strings.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml index e26af95395a0..42cf76173a72 100644 --- a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml +++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml @@ -17,6 +17,6 @@ - "فعَّل المشرف هذا الإعداد." + "يفعِّل المشرف هذا الإعداد." "أوقف المشرف هذا الإعداد." diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml index 4a6f73f0fcef..351f16cb1a24 100644 --- a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml +++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml @@ -17,6 +17,6 @@ - "Habilitada por el administrador" - "Inhabilitada por el administrador" + "Habilitado por el administrador" + "Inhabilitado por el administrador" diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml index 7fcdbdfa7dab..490efd099569 100644 --- a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml +++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml @@ -17,6 +17,6 @@ - "管理者が有効にしました" - "管理者が無効にしました" + "管理者によって有効にされています" + "管理者により無効にされています" diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml index d4bac70ac4e7..9c225f92f3bb 100644 --- a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml +++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml @@ -17,6 +17,6 @@ - "प्रशासकाने सुरू केलेले" + "अ‍ॅडमिनने सुरू केलेले" "प्रशासकाने बंद केलेले" diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml index 5cf0b29e14ff..e57d1cc11a20 100644 --- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml @@ -17,6 +17,6 @@ - "Ativada pelo administrador" + "Ativado pelo administrador" "Desativada pelo administrador" -- GitLab From d06e83b791266e12099e8bd0fcd56de20258d262 Mon Sep 17 00:00:00 2001 From: Santiago Seifert Date: Tue, 28 Apr 2020 21:08:05 +0100 Subject: [PATCH 039/166] Replace some javadoc symbols with HTML encoding Bug: 154120292 Test: N/A. Change-Id: I5d1b59ae714db30b2c39a3919a01cc2c15fc0f64 --- apex/media/framework/java/android/media/MediaParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 2746cba76887..d22e998f6cab 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -120,7 +120,7 @@ import java.util.UUID; * @Override * public void onTrackDataFound(int i, @NonNull TrackData trackData) { * MediaFormat mediaFormat = trackData.mediaFormat; - * if (videoTrackIndex == -1 && + * if (videoTrackIndex == -1 && * mediaFormat * .getString(MediaFormat.KEY_MIME, /* defaultValue= */ "") * .startsWith("video/")) { @@ -178,7 +178,7 @@ import java.util.UUID; * * private void ensureSpaceInBuffer(int numberOfBytesToRead) { * int requiredLength = bytesWrittenCount + numberOfBytesToRead; - * if (requiredLength > sampleDataBuffer.length) { + * if (requiredLength > sampleDataBuffer.length) { * sampleDataBuffer = Arrays.copyOf(sampleDataBuffer, requiredLength); * } * } -- GitLab From 80d5ed1473a1c0b3ed4be6be99564f215df58dd4 Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Fri, 10 Apr 2020 21:17:30 -0700 Subject: [PATCH 040/166] Enable flagging shortcuts to launch properly in a bubble For things to work correctly in a bubble they must be flagged to behave as documentLaunchMode=always Test: atest NotificationManagerTest Bug: 150719933 Change-Id: Ic5680c40fd2cbf0e9d0809a0a87a166f1e328c74 --- core/java/android/app/ActivityOptions.java | 24 +++++++++++++++++++ .../systemui/bubbles/BubbleExpandedView.java | 1 + .../server/pm/LauncherAppsService.java | 12 ++++++++++ 3 files changed, 37 insertions(+) diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 0129aabd49f8..80d2e6c60f69 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -265,6 +265,14 @@ public class ActivityOptions { private static final String KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING = "android:activity.disallowEnterPictureInPictureWhileLaunching"; + /** + * Indicates flags should be applied to the launching activity such that it will behave + * correctly in a bubble. + * @hide + */ + private static final String KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES = + "android:activity.applyActivityFlagsForBubbles"; + /** * For Activity transitions, the calling Activity's TransitionListener used to * notify the called Activity when the shared element and the exit transitions @@ -354,6 +362,7 @@ public class ActivityOptions { private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; private boolean mLockTaskMode = false; private boolean mDisallowEnterPictureInPictureWhileLaunching; + private boolean mApplyActivityFlagsForBubbles; private boolean mTaskAlwaysOnTop; private boolean mTaskOverlay; private boolean mTaskOverlayCanResume; @@ -1033,6 +1042,8 @@ public class ActivityOptions { SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT); mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean( KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, false); + mApplyActivityFlagsForBubbles = opts.getBoolean( + KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES, false); if (opts.containsKey(KEY_ANIM_SPECS)) { Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS); mAnimSpecs = new AppTransitionAnimationSpec[specs.length]; @@ -1465,6 +1476,16 @@ public class ActivityOptions { return mDisallowEnterPictureInPictureWhileLaunching; } + /** @hide */ + public void setApplyActivityFlagsForBubbles(boolean apply) { + mApplyActivityFlagsForBubbles = apply; + } + + /** @hide */ + public boolean isApplyActivityFlagsForBubbles() { + return mApplyActivityFlagsForBubbles; + } + /** * Update the current values in this ActivityOptions from those supplied * in otherOptions. Any values @@ -1663,6 +1684,9 @@ public class ActivityOptions { b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, mDisallowEnterPictureInPictureWhileLaunching); } + if (mApplyActivityFlagsForBubbles) { + b.putBoolean(KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES, mApplyActivityFlagsForBubbles); + } if (mAnimSpecs != null) { b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index bb2365559f74..e367927621f3 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -131,6 +131,7 @@ public class BubbleExpandedView extends LinearLayout { } try { if (!mIsOverflow && mBubble.usingShortcutInfo()) { + options.setApplyActivityFlagsForBubbles(true); mActivityView.startShortcutActivity(mBubble.getShortcutInfo(), options, null /* sourceBounds */); } else { diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 385ace8a511b..c6d08c36631a 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -16,11 +16,15 @@ package com.android.server.pm; +import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IApplicationThread; import android.app.PendingIntent; @@ -864,6 +868,14 @@ public class LauncherAppsService extends SystemService { } // Note the target activity doesn't have to be exported. + // Flag for bubble + ActivityOptions options = ActivityOptions.fromBundle(startActivityOptions); + if (options != null && options.isApplyActivityFlagsForBubbles()) { + // Flag for bubble to make behaviour match documentLaunchMode=always. + intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT); + intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + } + intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intents[0].setSourceBounds(sourceBounds); -- GitLab From 11524e3284bf6b8cb00a2813345c7f3fecfb54e4 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 28 Apr 2020 13:24:36 -0700 Subject: [PATCH 041/166] Import translations. DO NOT MERGE Change-Id: Id0c5f1fd9dfe07b1a5761c410a9dadef5d00f387 Auto-generated-cl: translation import --- packages/SettingsLib/SearchWidget/res/values-fr-rCA/strings.xml | 2 +- packages/SettingsLib/SearchWidget/res/values-hi/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/SettingsLib/SearchWidget/res/values-fr-rCA/strings.xml b/packages/SettingsLib/SearchWidget/res/values-fr-rCA/strings.xml index 6b2364d04cc6..9977138f2079 100644 --- a/packages/SettingsLib/SearchWidget/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/SearchWidget/res/values-fr-rCA/strings.xml @@ -17,5 +17,5 @@ - "Paramètres de recherche" + "Rechercher dans les paramètres" diff --git a/packages/SettingsLib/SearchWidget/res/values-hi/strings.xml b/packages/SettingsLib/SearchWidget/res/values-hi/strings.xml index 975c320f9dba..88c38316948a 100644 --- a/packages/SettingsLib/SearchWidget/res/values-hi/strings.xml +++ b/packages/SettingsLib/SearchWidget/res/values-hi/strings.xml @@ -17,5 +17,5 @@ - "खोज की सेटिंग" + "सेटिंग में खोजें" -- GitLab From 78d4e6dc7be59fcbf2be83ebd6a87dd371cb4fb6 Mon Sep 17 00:00:00 2001 From: Darryl L Johnson Date: Fri, 24 Apr 2020 16:37:28 -0700 Subject: [PATCH 042/166] Add test to ensure virtual display orientation doesn't change when device rotates. Bug: 148639826 Bug: 149213586 Test: ActivityThreadTest#testOrientationChangeDoesntOverrideVirtualDisplayOrientation Change-Id: I74d5d9ad6d051e1059ca03102716319ac1b674a7 --- core/java/android/app/ActivityThread.java | 5 ++ .../app/activity/ActivityThreadTest.java | 72 +++++++++++++++++-- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index bea85a9974d9..361149c2f017 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3085,6 +3085,11 @@ public final class ActivityThread extends ClientTransactionHandler { return mActivities.get(token); } + @VisibleForTesting(visibility = PACKAGE) + public Configuration getConfiguration() { + return mConfiguration; + } + @Override public void updatePendingConfiguration(Configuration config) { synchronized (mResourcesManager) { diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index c328d720426d..34417e68f11c 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -18,6 +18,8 @@ package android.app.activity; import static android.content.Intent.ACTION_EDIT; import static android.content.Intent.ACTION_VIEW; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.google.common.truth.Truth.assertThat; @@ -31,6 +33,7 @@ import android.app.Activity; import android.app.ActivityThread; import android.app.IApplicationThread; import android.app.PictureInPictureParams; +import android.app.ResourcesManager; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ClientTransaction; @@ -38,8 +41,10 @@ import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.NewIntentItem; import android.app.servertransaction.ResumeActivityItem; import android.app.servertransaction.StopActivityItem; +import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.hardware.display.DisplayManager; import android.os.IBinder; import android.util.MergedConfiguration; import android.view.Display; @@ -230,9 +235,9 @@ public class ActivityThreadTest { final ActivityThread activityThread = activity.getActivityThread(); final Configuration pendingConfig = new Configuration(); - pendingConfig.orientation = orientation == Configuration.ORIENTATION_LANDSCAPE - ? Configuration.ORIENTATION_PORTRAIT - : Configuration.ORIENTATION_LANDSCAPE; + pendingConfig.orientation = orientation == ORIENTATION_LANDSCAPE + ? ORIENTATION_PORTRAIT + : ORIENTATION_LANDSCAPE; pendingConfig.seq = seq + 2; activityThread.updatePendingActivityConfiguration(activity.getActivityToken(), pendingConfig); @@ -257,7 +262,7 @@ public class ActivityThreadTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { final Configuration config = new Configuration(); config.seq = BASE_SEQ; - config.orientation = Configuration.ORIENTATION_PORTRAIT; + config.orientation = ORIENTATION_PORTRAIT; activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config, Display.INVALID_DISPLAY); @@ -306,6 +311,61 @@ public class ActivityThreadTest { assertEquals(400, activity.mConfig.smallestScreenWidthDp); } + @Test + public void testOrientationChanged_DoesntOverrideVirtualDisplayOrientation() { + final TestActivity activity = mActivityTestRule.launchActivity(new Intent()); + final ActivityThread activityThread = activity.getActivityThread(); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + Context appContext = activity.getApplication(); + Configuration originalAppConfig = + new Configuration(appContext.getResources().getConfiguration()); + DisplayManager dm = appContext.getSystemService(DisplayManager.class); + + int virtualDisplayWidth; + int virtualDisplayHeight; + if (originalAppConfig.orientation == ORIENTATION_PORTRAIT) { + virtualDisplayWidth = 100; + virtualDisplayHeight = 200; + } else { + virtualDisplayWidth = 200; + virtualDisplayHeight = 100; + } + Display virtualDisplay = dm.createVirtualDisplay("virtual-display", + virtualDisplayWidth, virtualDisplayHeight, 200, null, 0).getDisplay(); + Context virtualDisplayContext = appContext.createDisplayContext(virtualDisplay); + int originalVirtualDisplayOrientation = virtualDisplayContext.getResources() + .getConfiguration().orientation; + + Configuration newAppConfig = new Configuration(originalAppConfig); + newAppConfig.seq++; + newAppConfig.orientation = newAppConfig.orientation == ORIENTATION_PORTRAIT + ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + + activityThread.updatePendingConfiguration(newAppConfig); + activityThread.handleConfigurationChanged(newAppConfig); + + try { + assertEquals("Virtual display orientation should not change when process" + + " configuration orientation changes.", + originalVirtualDisplayOrientation, + virtualDisplayContext.getResources().getConfiguration().orientation); + } finally { + // Make sure to reset the process config to prevent side effects to other + // tests. + Configuration activityThreadConfig = activityThread.getConfiguration(); + activityThreadConfig.seq = originalAppConfig.seq - 1; + + Configuration resourceManagerConfig = ResourcesManager.getInstance() + .getConfiguration(); + resourceManagerConfig.seq = originalAppConfig.seq - 1; + + activityThread.updatePendingConfiguration(originalAppConfig); + activityThread.handleConfigurationChanged(originalAppConfig); + } + }); + } + @Test public void testResumeAfterNewIntent() { final Activity activity = mActivityTestRule.launchActivity(new Intent()); @@ -386,7 +446,7 @@ public class ActivityThreadTest { final int numOfConfig = activity.mNumOfConfigChanges; Configuration config = new Configuration(); - config.orientation = Configuration.ORIENTATION_PORTRAIT; + config.orientation = ORIENTATION_PORTRAIT; config.seq = seq; activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config, Display.INVALID_DISPLAY); @@ -396,7 +456,7 @@ public class ActivityThreadTest { } config = new Configuration(); - config.orientation = Configuration.ORIENTATION_LANDSCAPE; + config.orientation = ORIENTATION_LANDSCAPE; config.seq = seq + 1; activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config, Display.INVALID_DISPLAY); -- GitLab From 39f3cf42f759ff90c10d9eb95addd265f0b5fc85 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 28 Apr 2020 14:24:32 -0700 Subject: [PATCH 043/166] Import translations. DO NOT MERGE Change-Id: I4962c75d29516ebdf5c65863144efcd97eb9bfb0 Auto-generated-cl: translation import --- packages/SettingsLib/res/values-ar/strings.xml | 2 +- packages/SettingsLib/res/values-as/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-bn/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-de/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-kn/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-mk/arrays.xml | 2 +- packages/SettingsLib/res/values-ml/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-mn/arrays.xml | 2 +- packages/SettingsLib/res/values-mr/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-ne/arrays.xml | 2 +- packages/SettingsLib/res/values-ne/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-or/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-pa/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-pt-rBR/arrays.xml | 2 +- packages/SettingsLib/res/values-pt/arrays.xml | 2 +- packages/SettingsLib/res/values-sl/arrays.xml | 2 +- packages/SettingsLib/res/values-ta/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-te/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-ur/strings.xml | 12 ++++-------- packages/SettingsLib/res/values-uz/arrays.xml | 2 +- packages/SettingsLib/res/values-vi/arrays.xml | 2 +- packages/SettingsLib/res/values-zh-rTW/strings.xml | 4 ++-- 22 files changed, 59 insertions(+), 107 deletions(-) diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 4f1024bd4f4b..51eae81b8aa3 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -511,7 +511,7 @@ "الساعة %1$s" "يوم %1$s" "المدة" - "الطلب في كل مرة" + "السؤال في كل مرة" "إلى أن يتم إيقاف الوضع" "للتو" "مكبر صوت الهاتف" diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index bf0831983fc8..587151fdccea 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -436,14 +436,10 @@ "বেটাৰি আনুমানিকভাৱে %1$s লৈকে চলিব" "%1$s পৰ্যন্ত" "%1$sৰ ভিতৰত বেটাৰী শেষ হ\'ব পাৰে" - - - - - - - - + "%1$sতকৈ কম বাকী আছে" + "%1$sতকৈ কম বাকী আছে (%2$s)" + "%1$sতকৈ বেছি বাকী আছে (%2$s)" + "%1$sতকৈ বেছি বাকী আছে" "ফ’নটো সোনকালে বন্ধ হৈ যাব পাৰে" "টেবলেটটো সোনকালে বন্ধ হৈ যাব পাৰে" "ডিভাইচটো সোনকালে বন্ধ হৈ যাব পাৰে" diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index decb3da257c1..bdc93380f31b 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -436,14 +436,10 @@ "আনুমানিক %1$s পর্যন্ত চলবে" "%1$s পর্যন্ত" "ব্যাটারির চার্জ %1$s-এ শেষ হয়ে যেতে পারে" - - - - - - - - + "%1$s-এরও কম সময় চলবে" + "%1$s-এরও কম সময় চলবে (%2$s)" + "%1$s-এরও বেশি সময় চলবে (%2$s)" + "%1$s-এরও বেশি সময় চলবে" "ফোন শীঘ্রই বন্ধ হয়ে যেতে পারে" "ট্যাবলেটটি শীঘ্রই বন্ধ হয়ে যেতে পারে" "ডিভাইসটি শীঘ্রই বন্ধ হয়ে যেতে পারে" diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 7838a0c810db..59c5b1d96eb3 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -436,14 +436,10 @@ "Sollte etwa bis %1$s reichen" "Bis %1$s" "Der Akku ist voraussichtlich um %1$s leer" - - - - - - - - + "Weniger als %1$s verbleibend" + "Weniger als %1$s verbleibend (%2$s)" + "Mehr als %1$s verbleibend (%2$s)" + "Mehr als %1$s verbleibend" "Smartphone wird eventuell bald ausgeschaltet" "Tablet wird eventuell bald ausgeschaltet" "Gerät wird eventuell bald ausgeschaltet" diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index aece31fb1ef6..5e60928c9290 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -436,14 +436,10 @@ "%1$s ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು" "%1$s ರವರೆಗೆ" "%1$s ಗಳಲ್ಲಿ ಬ್ಯಾಟರಿ ಮುಕ್ತಾಯವಾಗಬಹುದು" - - - - - - - - + "%1$s ಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ" + "%1$s ಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ (%2$s)" + "%1$s ಕ್ಕಿಂತ ಹೆಚ್ಚು ಸಮಯ ಉಳಿದಿದೆ (%2$s)" + "%1$s ಕ್ಕಿಂತ ಹೆಚ್ಚು ಸಮಯ ಉಳಿದಿದೆ" "ಫೋನ್ ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು" "ಟ್ಯಾಬ್ಲೆಟ್‌‌ ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು" "ಸಾಧನವು ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು" diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml index 90956ad99e9e..85ca0cb0077c 100644 --- a/packages/SettingsLib/res/values-mk/arrays.xml +++ b/packages/SettingsLib/res/values-mk/arrays.xml @@ -26,7 +26,7 @@ "Се поврзува..." "Автентицирање..." "Добивање ИП адреса..." - "Поврзана" + "Поврзано" "Суспендирана" "Се исклучува..." "Исклучено" diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index f61c44cd4440..156d941a7de7 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -436,14 +436,10 @@ "ഏകദേശം %1$s വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്" "%1$s വരെ" "%1$s ആവുമ്പോഴേക്ക് ബാറ്ററി തീർന്നേക്കാം" - - - - - - - - + "%1$s എന്നതിൽ കുറവ് സമയം ശേഷിക്കുന്നു" + "%1$s എന്നതിൽ കുറവ് സമയം ശേഷിക്കുന്നു (%2$s)" + "%1$s-ൽ കൂടുതൽ ശേഷിക്കുന്നു (%2$s)" + "%1$s-ൽ കൂടുതൽ ശേഷിക്കുന്നു" "ഫോൺ ഉടൻ ഷട്ട് ഡൗൺ ആയേക്കാം" "ടാബ്‌ലെറ്റ് ഉടൻ ഷട്ട് ഡൗൺ ആയേക്കാം" "ഉപകരണം ഉടൻ ഷട്ട് ഡൗൺ ആയേക്കാം" diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml index ce868af26bc9..c5e87bcf3945 100644 --- a/packages/SettingsLib/res/values-mn/arrays.xml +++ b/packages/SettingsLib/res/values-mn/arrays.xml @@ -40,7 +40,7 @@ "%1$s руу холбогдож байна…" "%1$s-тай гэрчилж байна…" "%1$s-с IP хаягийг авч байна…" - "%1$s руу холбогдсон" + "%1$s-д холбогдсон" "Түр хаасан" "%1$s-с салгагдаж байна…" "Салгагдсан" diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index b382f75ac5ae..b2e4c0a77d52 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -436,14 +436,10 @@ "सुमारे %1$s पर्यंत टिकावी" "%1$s पर्यंत" "%1$s वाजेपर्यंत बॅटरी संपू शकते" - - - - - - - - + "%1$s पेक्षा कमी शिल्लक आहे" + "%1$s पेक्षा कमी (%2$s) शिल्लक आहे" + "%1$s पेक्षा जास्त (%2$s) शिल्लक आहे" + "%1$s पेक्षा जास्त शिल्लक आहे" "फोन लवकरच बंद होऊ शकतो" "टॅबलेट लवकरच बंद होऊ शकतो" "डिव्हाइस लवकरच बंद होऊ शकते" diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index 5d79e80608d0..2895a02cbae4 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -40,7 +40,7 @@ "%1$sसँग जडान हुँदै..." "%1$sको साथ प्रमाणित गर्दै…" "%1$sबाट IP ठेगाना प्राप्त गर्दै…" - "%1$sसँग जडित" + "%1$s मा जोडिएको छ" "निलम्बित" "%1$sबाट विच्छेदन गर्दै..." "विच्छेदन भएको" diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index d6856a762825..870a6d169f11 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -436,14 +436,10 @@ "लगभग %1$s सम्म टिक्नु पर्छ" "%1$s सम्म" "ब्याट्री %1$s बजेसम्ममा सकिन सक्छ" - - - - - - - - + "%1$s भन्दा कम समय बाँकी छ" + "%1$s भन्दा कम समय बाँकी छ (%2$s)" + "%1$s भन्दा बढी समय बाँकी छ (%2$s)" + "%1$s भन्दा बढी समय बाँकी छ" "फोन चाँडै बन्द हुन सक्छ" "ट्याब्लेट चाँडै बन्द हुन सक्छ" "यन्त्र चाँडै बन्द हुन सक्छ" diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index cedf0bfef509..4341845bb75a 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -436,14 +436,10 @@ "ବ୍ୟାଟେରୀ %1$s ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ" "%1$s ପର୍ଯ୍ୟନ୍ତ" "%1$s ସୁଦ୍ଧା ବ୍ୟାଟେରୀର ଚାର୍ଜ ଶେଷ ହୋଇ ଯାଇପାରେ" - - - - - - - - + "%1$sରୁ କମ୍ ବ୍ୟାଟେରୀ ବାକି ଅଛି" + "%1$sରୁ କମ୍ ବ୍ୟାଟେରୀ ବାକି ଅଛି (%2$s)" + "%1$sରୁ ଅଧିକ ବ୍ୟାଟେରୀ ବାକି ଅଛି (%2$s)" + "%1$sରୁ ଅଧିକ ବ୍ୟାଟେରୀ ବାକି ଅଛି" "ଫୋନ୍ ଶୀଘ୍ର ବନ୍ଦ ହୋଇଯାଇପାରେ" "ଟାବଲେଟ୍ ଶୀଘ୍ର ବନ୍ଦ ହୋଇଯାଇପାରେ" "ଡିଭାଇସ୍ ଶୀଘ୍ର ବନ୍ଦ ହୋଇଯାଇପାରେ" diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 4ba370b3a030..b47b5fb2e05e 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -436,14 +436,10 @@ "ਲਗਭਗ %1$s ਤੱਕ ਚੱਲੇਗੀ" "%1$s ਤੱਕ" "ਬੈਟਰੀ %1$s ਤੱਕ ਖਤਮ ਹੋ ਸਕਦੀ ਹੈ" - - - - - - - - + "%1$s ਤੋਂ ਘੱਟ ਬਾਕੀ" + "%1$s ਤੋਂ ਘੱਟ ਬਾਕੀ (%2$s)" + "%1$s ਤੋਂ ਵੱਧ ਬਾਕੀ (%2$s)" + "%1$s ਤੋਂ ਵੱਧ ਬਾਕੀ" "ਫ਼ੋਨ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ" "ਟੈਬਲੈੱਟ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ" "ਡੀਵਾਈਸ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ" diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml index 4658ffdd859a..ca154e523f98 100644 --- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml @@ -61,7 +61,7 @@ "Desativado" "Filtro ativado" - "Ativada" + "Ativado" "AVRCP 1.4 (padrão)" diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml index 4658ffdd859a..ca154e523f98 100644 --- a/packages/SettingsLib/res/values-pt/arrays.xml +++ b/packages/SettingsLib/res/values-pt/arrays.xml @@ -61,7 +61,7 @@ "Desativado" "Filtro ativado" - "Ativada" + "Ativado" "AVRCP 1.4 (padrão)" diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml index eb860743b796..80042a60fb81 100644 --- a/packages/SettingsLib/res/values-sl/arrays.xml +++ b/packages/SettingsLib/res/values-sl/arrays.xml @@ -40,7 +40,7 @@ "Vzpostavljanje povezave z omrežjem %1$s …" "Preverjanje pristnosti v omrežju %1$s …" "Pridobivanje naslova IP od %1$s …" - "Povezava z omrežjem %1$s je vzpostavljena" + "Povezava z: %1$s" "Začasno ustavljeno" "Prekinjanje povezave z omrežjem %1$s …" "Prekinjena povezava" diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 4494d1a7ec68..a57fd0e87e1f 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -436,14 +436,10 @@ "%1$s வரை பயன்படுத்த முடியும்" "%1$s வரை" "%1$sக்கு பேட்டரி காலியாகிவிடக்கூடும்" - - - - - - - - + "%1$s ஐ விடக் குறைவாக உள்ளது" + "%1$s ஐ விடக் குறைவாக உள்ளது (%2$s)" + "%1$sக்கு மேல் உள்ளது (%2$s)" + "%1$sக்கு மேல் உள்ளது" "மொபைல் விரைவில் ஆஃப் ஆகக்கூடும்" "டேப்லெட் விரைவில் ஆஃப் ஆகக்கூடும்" "சாதனம் விரைவில் ஆஃப் ஆகக்கூடும்" diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index ff958ea1159a..b6186106cfa0 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -436,14 +436,10 @@ "దాదాపు %1$s వరకు ఉండాలి" "%1$s వరకు" "బ్యాటరీ %1$s సమయానికి ఖాళీ అవ్వచ్చు" - - - - - - - - + "%1$s కంటే తక్కువ సమయం మిగిలి ఉంది" + "%1$s కంటే తక్కువ సమయం మిగిలి ఉంది (%2$s)" + "%1$s కంటే ఎక్కువ సమయం మిగిలి ఉంది (%2$s)" + "%1$s కంటే ఎక్కువ సమయం మిగిలి ఉంది" "ఫోన్ త్వరలో షట్‌డౌన్ కావచ్చు" "టాబ్లెట్ త్వరలో షట్‌డౌన్ కావచ్చు" "పరికరం త్వరలో షట్‌డౌన్ కావచ్చు" diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 32515ace5b4a..a425805963f0 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -436,14 +436,10 @@ "تقریباً %1$s تک بیٹری چلے گی" "%1$s تک" "%1$s تک بیٹری ختم ہو سکتی ہے" - - - - - - - - + "%1$s سے کم باقی ہے" + "%1$s سے کم باقی ہے (%2$s)" + "%1$s سے زیادہ باقی ہے (%2$s)" + "%1$s سے زیادہ باقی ہے" "فون جلد ہی بند ہو سکتا ہے" "ٹیبلیٹ جلد ہی بند ہو سکتا ہے" "آلہ جلد ہی بند ہو سکتا ہے" diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml index 892ebe0a6e9e..26153adfef2a 100644 --- a/packages/SettingsLib/res/values-uz/arrays.xml +++ b/packages/SettingsLib/res/values-uz/arrays.xml @@ -40,7 +40,7 @@ "%1$s tarmog‘iga ulanilmoqda…" "%1$s bilan aloqa o‘rnatilyapti…" "%1$s IP manzil beryapti…" - "%1$s tarmog‘iga ulanildi" + "Bunga ulangan: %1$s" "Muzlatildi" "%1$s tarmog‘idan uzilmoqda…" "Uzildi" diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml index db29bc86e8a8..3a7e55afb124 100644 --- a/packages/SettingsLib/res/values-vi/arrays.xml +++ b/packages/SettingsLib/res/values-vi/arrays.xml @@ -152,7 +152,7 @@ ", đang hoạt động" - ", đang hoạt động (nội dung phương tiện)" + ", đang hoạt động (nội dung nghe nhìn)" ", đang hoạt động (điện thoại)" diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 43caf5ae6cce..6fefa31b0fb7 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -253,7 +253,7 @@ "Wi-Fi 掃描調節" "Wi‑Fi 加強型 MAC 隨機化" "行動數據連線一律保持啟用狀態" - "數據連線硬體加速" + "網路共用硬體加速" "顯示沒有名稱的藍牙裝置" "停用絕對音量功能" "啟用 Gabeldorsche" @@ -299,7 +299,7 @@ "允許模擬位置" "啟用檢視屬性檢查" "即使 Wi‑Fi 連線已啟用,一律將行動數據連線保持啟用狀態 (以便快速切換網路)。" - "使用數據連線硬體加速功能 (如果可用的話)" + "使用網路共用硬體加速功能 (如果可用的話)" "允許 USB 偵錯嗎?" "USB 偵錯是針對應用程式開發而設計的功能,可讓你複製電腦和裝置中的資料、不需經由通知即可在裝置上安裝應用程式,以及讀取記錄資料。" "要啟用無線偵錯嗎?" -- GitLab From e73034929e20d0ff6fa2c2211e23959a927609ac Mon Sep 17 00:00:00 2001 From: Beverly Date: Tue, 28 Apr 2020 14:58:37 -0400 Subject: [PATCH 044/166] Fix mocked HighPriorityProvider calls in test Test: atest KeyguardCoordinatorTest Fixes: 155204585 Change-Id: If928eee2a52e1e47d3f31ed801872ce66c6c5ef9 --- .../coordinator/KeyguardCoordinatorTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java index 4f481081855f..f54252effdd3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java @@ -176,6 +176,7 @@ public class KeyguardCoordinatorTest extends SysuiTestCase { .setKey(mEntry.getKey()) .setImportance(IMPORTANCE_MIN) .build()); + when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); // THEN filter out the entry assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0)); @@ -197,7 +198,8 @@ public class KeyguardCoordinatorTest extends SysuiTestCase { .setImportance(IMPORTANCE_MIN) .build()); - // WHEN its parent has a summary that exceeds threshold to show on lockscreen + // WHEN its parent does exceed threshold tot show on the lockscreen + when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(true); parent.setSummary(new NotificationEntryBuilder() .setImportance(IMPORTANCE_HIGH) .build()); @@ -205,7 +207,8 @@ public class KeyguardCoordinatorTest extends SysuiTestCase { // THEN don't filter out the entry assertFalse(mKeyguardFilter.shouldFilterOut(entryWithParent, 0)); - // WHEN its parent has a summary that doesn't exceed threshold to show on lockscreen + // WHEN its parent doesn't exceed threshold to show on lockscreen + when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(false); parent.setSummary(new NotificationEntryBuilder() .setImportance(IMPORTANCE_MIN) .build()); @@ -248,5 +251,8 @@ public class KeyguardCoordinatorTest extends SysuiTestCase { .thenReturn(true); // notification doesn't have a summary + + // notification is high priority, so it shouldn't be filtered + when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true); } } -- GitLab From 524d0f5c2874ebfd006a26bbf2fa0341332d6ddc Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Mon, 27 Apr 2020 13:30:01 -0700 Subject: [PATCH 045/166] Update cleanupAppliedPayload comment. ServiceSpecificException might be thrown, but it is not actionable by the caller. Hence hide the comment. Fixes: 151156841 Test: none Change-Id: I07836fe2c5cc361fa81f6fc24851ad2b0721591b (cherry picked from commit d3abb6d3851d23f4c15ed424ffde8771391acf14) Merged-In: I07836fe2c5cc361fa81f6fc24851ad2b0721591b --- core/java/android/os/UpdateEngine.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index de274c082c80..e907e2204b7b 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -614,9 +614,6 @@ public class UpdateEngine { * encountered. Device is corrupted, and future updates must not be applied. * The device cannot recover without flashing and factory resets. * - * - * @throws ServiceSpecificException if other transient errors has occurred. - * A reboot may or may not help resolving the issue. */ @WorkerThread @ErrorCode -- GitLab From ac7bc339b2e63cd25594536481cd78d52da9141a Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Fri, 24 Apr 2020 16:01:41 +0100 Subject: [PATCH 046/166] Block rollback commit if it may violate min extension constraint. This CL adds a check when a rollback is committed, if the rollback contains an apex and an extension version has increased since the rollback was created. The check looks for any installed app with a minExtensionVersion greater than was present when the rollback was created. If such an app exists, the rollback commit is not allowed to occur. See go/sdk-extensions-and-rollback for more details. Bug: 152737927 Test: atest RollbackUnitTest Test: atest RollbackTest Test: atest RollbackManagerHostTest Merged-In: I7fd7d98a03ecb1edb1b79475fadb944bc4f2fd6f Change-Id: I7fd7d98a03ecb1edb1b79475fadb944bc4f2fd6f --- .../com/android/server/rollback/Rollback.java | 80 +++++++++++++++- .../server/rollback/RollbackStore.java | 9 +- .../server/rollback/RollbackStoreTest.java | 7 +- .../server/rollback/RollbackUnitTest.java | 93 +++++++++++++++++++ 4 files changed, 175 insertions(+), 14 deletions(-) diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 9171501270d0..a0f40708627e 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; @@ -36,6 +37,7 @@ import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.os.UserManager; +import android.os.ext.SdkExtensions; import android.text.TextUtils; import android.util.IntArray; import android.util.Slog; @@ -43,8 +45,11 @@ import android.util.SparseIntArray; import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.LocalServices; +import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; import java.io.IOException; @@ -175,10 +180,9 @@ class Rollback { private int mNumPackageSessionsWithSuccess; /** - * The extension versions supported at the time of rollback creation. May be null if not set - * at creation time. + * The extension versions supported at the time of rollback creation. */ - @Nullable private final SparseIntArray mExtensionVersions; + private final SparseIntArray mExtensionVersions; /** * Constructs a new, empty Rollback instance. @@ -211,7 +215,8 @@ class Rollback { Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId, String installerPackageName) { - this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null, null); + this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null, + new SparseIntArray(0)); } /** @@ -297,7 +302,7 @@ class Rollback { * Returns the extension versions that were supported at the time that the rollback was created, * as a mapping from SdkVersion to ExtensionVersion. */ - @Nullable SparseIntArray getExtensionVersions() { + SparseIntArray getExtensionVersions() { return mExtensionVersions; } @@ -470,6 +475,15 @@ class Rollback { return; } + if (containsApex() && wasCreatedAtLowerExtensionVersion()) { + PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); + if (extensionVersionReductionWouldViolateConstraint(mExtensionVersions, pmi)) { + sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, + "Rollback may violate a minExtensionVersion constraint"); + return; + } + } + // Get a context to use to install the downgraded version of the package. Context pkgContext; try { @@ -845,6 +859,56 @@ class Rollback { } } + /** + * Returns true if there is an app installed that specifies a minExtensionVersion greater + * than what was present at the time this Rollback was created. + */ + @VisibleForTesting + static boolean extensionVersionReductionWouldViolateConstraint( + SparseIntArray rollbackExtVers, PackageManagerInternal pmi) { + if (rollbackExtVers.size() == 0) { + return false; + } + List packages = pmi.getPackageList().getPackageNames(); + for (int i = 0; i < packages.size(); i++) { + AndroidPackage pkg = pmi.getPackage(packages.get(i)); + SparseIntArray minExtVers = pkg.getMinExtensionVersions(); + if (minExtVers == null) { + continue; + } + for (int j = 0; j < rollbackExtVers.size(); j++) { + int minExt = minExtVers.get(rollbackExtVers.keyAt(j), -1); + if (rollbackExtVers.valueAt(j) < minExt) { + return true; + } + } + } + return false; + } + + /** + * Returns true if for any SDK version, the extension version recorded at the time of rollback + * creation is lower than the current extension version. + */ + private boolean wasCreatedAtLowerExtensionVersion() { + for (int i = 0; i < mExtensionVersions.size(); i++) { + if (SdkExtensions.getExtensionVersion(mExtensionVersions.keyAt(i)) + > mExtensionVersions.valueAt(i)) { + return true; + } + } + return false; + } + + private boolean containsApex() { + for (PackageRollbackInfo pkgInfo : info.getPackages()) { + if (pkgInfo.isApex()) { + return true; + } + } + return false; + } + void dump(IndentingPrintWriter ipw) { synchronized (mLock) { ipw.println(info.getRollbackId() + ":"); @@ -871,6 +935,12 @@ class Rollback { ipw.decreaseIndent(); ipw.println("-committedSessionId: " + info.getCommittedSessionId()); } + if (mExtensionVersions.size() > 0) { + ipw.println("-extensionVersions:"); + ipw.increaseIndent(); + ipw.println(mExtensionVersions.toString()); + ipw.decreaseIndent(); + } ipw.decreaseIndent(); } } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index f186d65baaa1..792cc45ee180 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -179,10 +179,7 @@ class RollbackStore { } private static @Nullable JSONArray extensionVersionsToJson( - @Nullable SparseIntArray extensionVersions) throws JSONException { - if (extensionVersions == null) { - return null; - } + SparseIntArray extensionVersions) throws JSONException { JSONArray array = new JSONArray(); for (int i = 0; i < extensionVersions.size(); i++) { JSONObject entryJson = new JSONObject(); @@ -193,10 +190,10 @@ class RollbackStore { return array; } - private static @Nullable SparseIntArray extensionVersionsFromJson(@Nullable JSONArray json) + private static @Nullable SparseIntArray extensionVersionsFromJson(JSONArray json) throws JSONException { if (json == null) { - return null; + return new SparseIntArray(0); } SparseIntArray extensionVersions = new SparseIntArray(json.length()); for (int i = 0; i < json.length(); i++) { diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java index 6afdfba70f58..102d5bb373c8 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java @@ -234,8 +234,8 @@ public class RollbackStoreTest { @Test public void loadFromJsonNoExtensionVersions() throws Exception { - Rollback expectedRb = - mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null); + Rollback expectedRb = mRollbackStore.createNonStagedRollback( + ID, USER, INSTALLER, null, new SparseIntArray(0)); expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z")); expectedRb.setRestoreUserDataInProgress(true); @@ -337,7 +337,8 @@ public class RollbackStoreTest { @Test public void saveAndDelete() { - Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null); + Rollback rollback = mRollbackStore.createNonStagedRollback( + ID, USER, INSTALLER, null, new SparseIntArray(0)); RollbackStore.saveRollback(rollback); diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index e74891c7b3f8..57eae1d26f7c 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -24,12 +24,18 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.content.pm.PackageManagerInternal; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.util.IntArray; +import android.util.SparseIntArray; import android.util.SparseLongArray; +import com.android.server.pm.PackageList; +import com.android.server.pm.parsing.pkg.PackageImpl; + import com.google.common.collect.Range; import org.junit.Before; @@ -44,6 +50,7 @@ import java.io.File; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; @RunWith(JUnit4.class) public class RollbackUnitTest { @@ -56,10 +63,17 @@ public class RollbackUnitTest { private static final String INSTALLER = "some.installer"; @Mock private AppDataRollbackHelper mMockDataHelper; + @Mock private PackageManagerInternal mMockPmi; + + private List mPackages; + private PackageList mPackageList; @Before public void setUp() { MockitoAnnotations.initMocks(this); + mPackages = new ArrayList<>(); + mPackageList = new PackageList(mPackages, null); + when(mMockPmi.getPackageList()).thenReturn(mPackageList); } @Test @@ -340,6 +354,85 @@ public class RollbackUnitTest { assertThat(rollback.allPackagesEnabled()).isTrue(); } + @Test + public void minExtVerConstraintNotViolated() { + addPkgWithMinExtVersions("pkg0", new int[][] {{30, 4}}); + addPkgWithMinExtVersions("pkg1", new int[][] {}); + addPkgWithMinExtVersions("pkg2", new int[][] {{30, 5}, {31, 1}}); + addPkgWithMinExtVersions("pkg3", new int[][] {{31, 7}, {32, 15}}); + + assertThat(Rollback.extensionVersionReductionWouldViolateConstraint( + sparseArrayFrom(new int[][] {{30, 5}}), mMockPmi)).isFalse(); + } + + @Test + public void minExtVerConstraintExists() { + addPkgWithMinExtVersions("pkg0", null); + addPkgWithMinExtVersions("pkg1", new int[][] {{30, 5}, {31, 1}}); + + assertThat(Rollback.extensionVersionReductionWouldViolateConstraint( + sparseArrayFrom(new int[][] {{30, 4}}), mMockPmi)).isTrue(); + } + + @Test + public void minExtVerConstraintExistsOnOnePackage() { + addPkgWithMinExtVersions("pkg0", new int[][] {{30, 4}}); + addPkgWithMinExtVersions("pkg1", new int[][] {}); + addPkgWithMinExtVersions("pkg2", new int[][] {{30, 5}, {31, 1}}); + addPkgWithMinExtVersions("pkg3", new int[][] {{31, 7}, {32, 15}}); + + assertThat(Rollback.extensionVersionReductionWouldViolateConstraint( + sparseArrayFrom(new int[][] {{30, 4}}), mMockPmi)).isTrue(); + } + + @Test + public void minExtVerConstraintDifferentSdk() { + addPkgWithMinExtVersions("pkg0", null); + addPkgWithMinExtVersions("pkg1", new int[][] {{30, 5}, {31, 1}}); + + assertThat(Rollback.extensionVersionReductionWouldViolateConstraint( + sparseArrayFrom(new int[][] {{32, 4}}), mMockPmi)).isFalse(); + } + + @Test + public void minExtVerConstraintNoneRecordedOnRollback() { + addPkgWithMinExtVersions("pkg0", new int[][] {{30, 4}}); + addPkgWithMinExtVersions("pkg1", new int[][] {}); + addPkgWithMinExtVersions("pkg2", new int[][] {{30, 5}, {31, 1}}); + addPkgWithMinExtVersions("pkg3", new int[][] {{31, 7}, {32, 15}}); + + assertThat(Rollback.extensionVersionReductionWouldViolateConstraint( + new SparseIntArray(0), mMockPmi)).isFalse(); + } + + @Test + public void minExtVerConstraintNoMinsRecorded() { + addPkgWithMinExtVersions("pkg0", null); + addPkgWithMinExtVersions("pkg1", null); + + assertThat(Rollback.extensionVersionReductionWouldViolateConstraint( + sparseArrayFrom(new int[][] {{32, 4}}), mMockPmi)).isFalse(); + } + + private void addPkgWithMinExtVersions(String pkg, int[][] minExtVersions) { + mPackages.add(pkg); + PackageImpl pkgImpl = new PackageImpl(pkg, "baseCodePath", "codePath", null, false); + pkgImpl.setMinExtensionVersions(sparseArrayFrom(minExtVersions)); + + when(mMockPmi.getPackage(pkg)).thenReturn(pkgImpl); + } + + private static SparseIntArray sparseArrayFrom(int[][] arr) { + if (arr == null) { + return null; + } + SparseIntArray result = new SparseIntArray(arr.length); + for (int[] pair : arr) { + result.put(pair[0], pair[1]); + } + return result; + } + private static PackageRollbackInfo newPkgInfoFor( String packageName, long fromVersion, long toVersion, boolean isApex) { return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion), -- GitLab From 09c24c3c4f8c29344708c31bf56f05d6648ffb1e Mon Sep 17 00:00:00 2001 From: Michael Groover Date: Tue, 28 Apr 2020 15:08:20 -0700 Subject: [PATCH 047/166] Resolve IndexOOBException in SigningDetails#hasAncestor When a package that is part of a shareduid is installed / updated its lineage is compared against the current signature / lineage for the shareduid. If the new package has more than one signature in the lineage an IndexOutOfBoundsException is thrown by SigningDetails#hasAncestor crashing the system server. This commit ensures the ancestor check is only performed against the current signer for the shareduid. Fixes: 155134046 Test: atest SigningDetailsTest Change-Id: Icef9f21c6901e255e5276085259d2f773f41e858 --- .../android/content/pm/PackageParser.java | 2 +- .../content/pm/SigningDetailsTest.java | 147 ++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 core/tests/coretests/src/android/content/pm/SigningDetailsTest.java diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 8a57f826ad2e..addac9853f92 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -6016,7 +6016,7 @@ public class PackageParser { // the last entry in pastSigningCertificates is the current signer, ignore it for (int i = 0; i < pastSigningCertificates.length - 1; i++) { - if (pastSigningCertificates[i].equals(oldDetails.signatures[i])) { + if (pastSigningCertificates[i].equals(oldDetails.signatures[0])) { return true; } } diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java new file mode 100644 index 000000000000..51af048bcae8 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.pm; + +import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.pm.PackageParser.SigningDetails; +import android.util.ArraySet; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.security.PublicKey; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class SigningDetailsTest { + private static final String FIRST_SIGNATURE = "1234"; + private static final String SECOND_SIGNATURE = "5678"; + private static final String THIRD_SIGNATURE = "9abc"; + + @Test + public void hasAncestor_multipleSignersInLineageWithAncestor_returnsTrue() throws Exception { + SigningDetails twoSignersInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + SigningDetails oneSignerInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE); + + boolean result = twoSignersInLineageDetails.hasAncestor(oneSignerInLineageDetails); + + assertTrue(result); + } + + @Test + public void hasAncestor_oneSignerInLineageAgainstMultipleSignersInLineage_returnsFalse() + throws Exception { + SigningDetails twoSignersInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + SigningDetails oneSignerInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE); + + boolean result = oneSignerInLineageDetails.hasAncestor(twoSignersInLineageDetails); + + assertFalse(result); + } + + @Test + public void hasAncestor_multipleSignersInLineageAgainstSelf_returnsFalse() throws Exception { + SigningDetails twoSignersInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + + boolean result = twoSignersInLineageDetails.hasAncestor(twoSignersInLineageDetails); + + assertFalse(result); + } + + @Test + public void hasAncestor_oneSignerInLineageWithAncestor_returnsTrue() throws Exception { + SigningDetails twoSignersInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + SigningDetails oneSignerDetails = createSigningDetails(FIRST_SIGNATURE); + + boolean result = twoSignersInLineageDetails.hasAncestor(oneSignerDetails); + + assertTrue(result); + } + + @Test + public void hasAncestor_singleSignerAgainstLineage_returnsFalse() throws Exception { + SigningDetails oneSignerDetails = createSigningDetails(FIRST_SIGNATURE); + SigningDetails twoSignersInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + + boolean result = oneSignerDetails.hasAncestor(twoSignersInLineageDetails); + + assertFalse(result); + } + + @Test + public void hasAncestor_multipleSigners_returnsFalse() throws Exception { + SigningDetails twoSignersDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); + SigningDetails twoSignersInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + + boolean result1 = twoSignersInLineageDetails.hasAncestor(twoSignersDetails); + boolean result2 = twoSignersDetails.hasAncestor(twoSignersInLineageDetails); + + assertFalse(result1); + assertFalse(result2); + } + + @Test + public void hasAncestor_unknownDetails_returnsFalse() throws Exception { + SigningDetails unknownDetails = SigningDetails.UNKNOWN; + SigningDetails twoSignersInLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + + boolean result1 = twoSignersInLineageDetails.hasAncestor(unknownDetails); + boolean result2 = unknownDetails.hasAncestor(twoSignersInLineageDetails); + + assertFalse(result1); + assertFalse(result2); + } + + private SigningDetails createSigningDetailsWithLineage(String... signers) { + Signature[] signingHistory = new Signature[signers.length]; + for (int i = 0; i < signers.length; i++) { + signingHistory[i] = new Signature(signers[i]); + } + Signature[] currentSignature = new Signature[]{signingHistory[signers.length - 1]}; + // TODO: Since the PublicKey ArraySet is not used by any of the tests a generic empty Set + // works for now, but if this is needed in the future consider creating mock PublicKeys that + // can respond as required for the method under test. + ArraySet publicKeys = new ArraySet<>(); + return new SigningDetails(currentSignature, SIGNING_BLOCK_V3, publicKeys, signingHistory); + } + + private SigningDetails createSigningDetails(String... signers) { + Signature[] currentSignatures = new Signature[signers.length]; + for (int i = 0; i < signers.length; i++) { + currentSignatures[i] = new Signature(signers[i]); + } + // TODO: Similar to above when tests are added that require this it should be updated to use + // mocked PublicKeys. + ArraySet publicKeys = new ArraySet<>(); + return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, publicKeys, null); + } +} -- GitLab From 7d40780e398a38a69a2325c3f091a61b04dc6206 Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Sun, 26 Apr 2020 20:04:55 -0700 Subject: [PATCH 048/166] Don't remove OnAlarmListener mappings in the client Removing the mapping is prone to races in keeping the state consistent with the server. An inconsistent state can lead to multiple alarms on the server for the same listener. Test: atest CtsJobSchedulerTestCases doesn't fill the alarm queue with more than one alarm. atest CtsAlarmManagerTestCases Bug: 154444435 Change-Id: Iaf11d5decb17fbf2366b49d9865231bf65dbdc41 --- core/java/android/app/AlarmManager.java | 42 ++++++++++++------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 00627ef381ab..ae8a2cbf5120 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -31,7 +31,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.WorkSource; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.Log; import android.util.proto.ProtoOutputStream; @@ -40,6 +39,8 @@ import libcore.timezone.ZoneInfoDb; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.WeakHashMap; /** * This class provides access to the system alarm services. These allow you @@ -222,26 +223,12 @@ public class AlarmManager { } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } - - synchronized (AlarmManager.class) { - if (sWrappers != null) { - sWrappers.remove(mListener); - } - } } @Override public void doAlarm(IAlarmCompleteListener alarmManager) { mCompletion = alarmManager; - // Remove this listener from the wrapper cache first; the server side - // already considers it gone - synchronized (AlarmManager.class) { - if (sWrappers != null) { - sWrappers.remove(mListener); - } - } - mHandler.post(this); } @@ -263,9 +250,14 @@ public class AlarmManager { } } - // Tracking of the OnAlarmListener -> wrapper mapping, for cancel() support. - // Access is synchronized on the AlarmManager class object. - private static ArrayMap sWrappers; + /** + * Tracking of the OnAlarmListener -> ListenerWrapper mapping, for cancel() support. + * An entry is guaranteed to stay in this map as long as its ListenerWrapper is held by the + * server. + * + *

Access is synchronized on the AlarmManager class object. + */ + private static WeakHashMap> sWrappers; /** * package private on purpose @@ -682,14 +674,17 @@ public class AlarmManager { if (listener != null) { synchronized (AlarmManager.class) { if (sWrappers == null) { - sWrappers = new ArrayMap(); + sWrappers = new WeakHashMap<>(); } - recipientWrapper = sWrappers.get(listener); + final WeakReference weakRef = sWrappers.get(listener); + if (weakRef != null) { + recipientWrapper = weakRef.get(); + } // no existing wrapper => build a new one if (recipientWrapper == null) { recipientWrapper = new ListenerWrapper(listener); - sWrappers.put(listener, recipientWrapper); + sWrappers.put(listener, new WeakReference<>(recipientWrapper)); } } @@ -948,7 +943,10 @@ public class AlarmManager { ListenerWrapper wrapper = null; synchronized (AlarmManager.class) { if (sWrappers != null) { - wrapper = sWrappers.get(listener); + final WeakReference weakRef = sWrappers.get(listener); + if (weakRef != null) { + wrapper = weakRef.get(); + } } } -- GitLab From 9993fd6bff7ac3253c84de0b3e0335ed660d2c2b Mon Sep 17 00:00:00 2001 From: Malcolm Chen Date: Tue, 28 Apr 2020 16:14:01 -0700 Subject: [PATCH 049/166] Add two ServiceState APIs back to @Unsupported list. It was removed from boot-image-profile when we tried to expose them for mainlining. Now it's @hide again, so we should add it back into boot-image-profile otherwise it will break appss. Bug: 153825725 Test: build Change-Id: I7dcce69d3d2b3b2601f98817a4ee4467f7efb47a --- config/boot-image-profile.txt | 2 ++ telephony/java/android/telephony/ServiceState.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index 5f45d6932293..95778b5b3648 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -19208,6 +19208,8 @@ HSPLandroid/telephony/PreciseDataConnectionState;->toString()Ljava/lang/String; HSPLandroid/telephony/PreciseDataConnectionState;->writeToParcel(Landroid/os/Parcel;I)V HSPLandroid/telephony/Rlog;->d(Ljava/lang/String;Ljava/lang/String;)I HSPLandroid/telephony/ServiceState;->createLocationInfoSanitizedCopy(Z)Landroid/telephony/ServiceState; +HSPLandroid/telephony/ServiceState;->fillInNotifierBundle(Landroid/os/Bundle;)V +HSPLandroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState; PLandroid/telephony/SignalStrength;->(Landroid/telephony/SignalStrength;)V HPLandroid/telephony/SignalStrength;->copyFrom(Landroid/telephony/SignalStrength;)V HSPLandroid/telephony/SignalStrength;->isGsm()Z diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 0e61efac77ec..07d71d0ad7f9 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -398,6 +398,7 @@ public class ServiceState implements Parcelable { * @hide */ @NonNull + @UnsupportedAppUsage public static ServiceState newFromBundle(@NonNull Bundle m) { ServiceState ret; ret = new ServiceState(); @@ -1315,6 +1316,7 @@ public class ServiceState implements Parcelable { * @hide * */ + @UnsupportedAppUsage public void fillInNotifierBundle(@NonNull Bundle m) { m.putParcelable(EXTRA_SERVICE_STATE, this); // serviceState already consists of below entries. -- GitLab From 937bf9be71a43dbd91946ec52c1f97b1e6696733 Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Tue, 28 Apr 2020 16:38:58 -0700 Subject: [PATCH 050/166] Check for null HAT and add logging Bug: 154364967 Test: atest com.android.server.biometrics Change-Id: I1212715828a0588adc2771fbac0b1fb68fd9e13d Merged-In: I7dfe5dd8e740eef0fc74cd2e68a19dc35eb2c49e --- .../com/android/server/biometrics/BiometricService.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index d49b590b8a4d..70fbca5b4798 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -1504,11 +1504,17 @@ public class BiometricService extends SystemService { try { switch (reason) { case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED: - mKeyStore.addAuthToken(credentialAttestation); + if (credentialAttestation != null) { + mKeyStore.addAuthToken(credentialAttestation); + } else { + Slog.e(TAG, "Credential confirmed but attestation is null"); + } case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED: case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED: if (mCurrentAuthSession.mTokenEscrow != null) { mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow); + } else { + Slog.e(TAG, "mTokenEscrow is null"); } mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded( Utils.getAuthenticationTypeForResult(reason)); -- GitLab From 045c020636b20033bb83c1eb06fbbfee432e7515 Mon Sep 17 00:00:00 2001 From: Adam He Date: Fri, 17 Apr 2020 15:20:01 -0700 Subject: [PATCH 051/166] Address leftover TODOs from inline suggestions. Fixes: 146524826 Test: atest android.autofillservice.cts.inline Change-Id: I50666e9fa012b18f74c20982068a452fdc9592f8 --- .../InputMethodService.java | 15 ++++++-- .../android/service/autofill/FillRequest.java | 36 ++++++++++++++----- .../MultiClientInputMethodManagerService.java | 1 - 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 93ce88b767bc..d3464fde4b75 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -784,10 +784,19 @@ public class InputMethodService extends AbstractInputMethodService { } } - // TODO(b/137800469): Add detailed docs explaining the inline suggestions process. /** - * This method should be implemented by subclass which supports displaying autofill inline - * suggestion. + * Called when Autofill is requesting an {@link InlineSuggestionsRequest} from the IME. + * + *

The Autofill Framework will first request the IME to create and send an + * {@link InlineSuggestionsRequest} back. Once Autofill Framework receives a valid request and + * also receives valid inline suggestions, they will be returned via + * {@link #onInlineSuggestionsResponse(InlineSuggestionsResponse)}.

+ * + *

IME Lifecycle - The request will wait to be created after inputStarted

+ * + *

If the IME wants to support displaying inline suggestions, they must set + * supportsInlineSuggestions in its XML and implement this method to return a valid + * {@link InlineSuggestionsRequest}.

* * @param uiExtras the extras that contain the UI renderer related information * @return an {@link InlineSuggestionsRequest} to be sent to Autofill. diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 8f858d547b1f..d94160c2b40c 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -127,10 +127,16 @@ public final class FillRequest implements Parcelable { private final @RequestFlags int mFlags; /** - * Gets the {@link android.view.inputmethod.InlineSuggestionsRequest} associated + * Gets the {@link InlineSuggestionsRequest} associated * with this request. * - * TODO(b/137800469): Add more doc describing how to handle the inline suggestions request. + *

Autofill Framework will send a {@code @non-null} {@link InlineSuggestionsRequest} if + * currently inline suggestions are supported and can be displayed. If the Autofill service + * wants to show inline suggestions, they may return {@link Dataset} with valid + * {@link InlinePresentation}.

+ * + *

The Autofill Service must set supportsInlineSuggestions in its XML to enable support + * for inline suggestions.

* * @return the suggestionspec */ @@ -142,7 +148,7 @@ public final class FillRequest implements Parcelable { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -212,10 +218,16 @@ public final class FillRequest implements Parcelable { * @return any combination of {@link #FLAG_MANUAL_REQUEST} and * {@link #FLAG_COMPATIBILITY_MODE_REQUEST}. * @param inlineSuggestionsRequest - * Gets the {@link android.view.inputmethod.InlineSuggestionsRequest} associated + * Gets the {@link InlineSuggestionsRequest} associated * with this request. * - * TODO(b/137800469): Add more doc describing how to handle the inline suggestions request. + *

Autofill Framework will send a {@code @non-null} {@link InlineSuggestionsRequest} if + * currently inline suggestions are supported and can be displayed. If the Autofill service + * wants to show inline suggestions, they may return {@link Dataset} with valid + * {@link InlinePresentation}.

+ * + *

The Autofill Service must set supportsInlineSuggestions in its XML to enable support + * for inline suggestions.

* @hide */ @DataClass.Generated.Member @@ -292,10 +304,16 @@ public final class FillRequest implements Parcelable { } /** - * Gets the {@link android.view.inputmethod.InlineSuggestionsRequest} associated + * Gets the {@link InlineSuggestionsRequest} associated * with this request. * - * TODO(b/137800469): Add more doc describing how to handle the inline suggestions request. + *

Autofill Framework will send a {@code @non-null} {@link InlineSuggestionsRequest} if + * currently inline suggestions are supported and can be displayed. If the Autofill service + * wants to show inline suggestions, they may return {@link Dataset} with valid + * {@link InlinePresentation}.

+ * + *

The Autofill Service must set supportsInlineSuggestions in its XML to enable support + * for inline suggestions.

* * @return the suggestionspec */ @@ -387,8 +405,8 @@ public final class FillRequest implements Parcelable { }; @DataClass.Generated( - time = 1583196707026L, - codegenVersion = "1.0.14", + time = 1588119440090L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") @Deprecated diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 0d16ee0e1d5a..0b73e4f0e9b4 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -192,7 +192,6 @@ public final class MultiClientInputMethodManagerService { InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) { try { - //TODO(b/137800469): support multi client IMEs. cb.onInlineSuggestionsUnsupported(); } catch (RemoteException e) { Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e); -- GitLab From d3cfe6204ec27a6a9c7d5e1fd401a2ae7c2633f4 Mon Sep 17 00:00:00 2001 From: Sean Pont Date: Fri, 17 Apr 2020 12:17:43 -0700 Subject: [PATCH 052/166] Use POWER_MENU_LOCKED_SHOW_CONTENT in wallet Use POWER_MENU_LOCKED_SHOW_CONTENT to control lock screen behavior for the Quick Access Wallet. Do not provide Intents that will fail to start an Activity. Update comments. Bug:155232731 Bug:155186709 Test: manual Test: atest CtsQuickAccessWalletTestCases Change-Id: I78e5355a69d666aee6dd122389edabed170f07b2 --- core/java/android/provider/Settings.java | 3 +- .../QuickAccessWalletClientImpl.java | 72 +++++++++++++++---- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index fbd6cbae47d9..0d29a04cc5f5 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2020,8 +2020,7 @@ public final class Settings { * In some cases, a matching Activity may not exist, so ensure you * safeguard against this. *

- * Input: The Intent's data URI specifies the application package name - * to be shown, with the "package" scheme. That is "package:com.my.app". + * Input: Nothing. *

* Output: Nothing. */ diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java index 31a085d15a34..9d0b582dddc4 100644 --- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java @@ -16,6 +16,8 @@ package android.service.quickaccesswallet; +import static android.service.quickaccesswallet.QuickAccessWalletService.ACTION_VIEW_WALLET; +import static android.service.quickaccesswallet.QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS; import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE_INTERFACE; import android.annotation.CallbackExecutor; @@ -26,6 +28,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.IBinder; @@ -97,8 +102,7 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser @Override public boolean isWalletFeatureAvailableWhenDeviceLocked() { - return checkSecureSetting(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS) - && checkSecureSetting(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + return checkSecureSetting(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT); } @Override @@ -234,27 +238,67 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser @Override @Nullable public Intent createWalletIntent() { - if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) { + if (mServiceInfo == null) { return null; } - return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET) - .setComponent( - new ComponentName( - mServiceInfo.getComponentName().getPackageName(), - mServiceInfo.getWalletActivity())); + String packageName = mServiceInfo.getComponentName().getPackageName(); + String walletActivity = mServiceInfo.getWalletActivity(); + return createIntent(walletActivity, packageName, ACTION_VIEW_WALLET); } @Override @Nullable public Intent createWalletSettingsIntent() { - if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) { + if (mServiceInfo == null) { return null; } - return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS) - .setComponent( - new ComponentName( - mServiceInfo.getComponentName().getPackageName(), - mServiceInfo.getSettingsActivity())); + String packageName = mServiceInfo.getComponentName().getPackageName(); + String settingsActivity = mServiceInfo.getSettingsActivity(); + return createIntent(settingsActivity, packageName, ACTION_VIEW_WALLET_SETTINGS); + } + + @Nullable + private Intent createIntent(@Nullable String activityName, String packageName, String action) { + PackageManager pm = mContext.getPackageManager(); + if (TextUtils.isEmpty(activityName)) { + activityName = queryActivityForAction(pm, packageName, action); + } + if (TextUtils.isEmpty(activityName)) { + return null; + } + ComponentName component = new ComponentName(packageName, activityName); + if (!isActivityEnabled(pm, component)) { + return null; + } + return new Intent(action).setComponent(component); + } + + @Nullable + private static String queryActivityForAction(PackageManager pm, String packageName, + String action) { + Intent intent = new Intent(action).setPackage(packageName); + ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); + if (resolveInfo == null + || resolveInfo.activityInfo == null + || !resolveInfo.activityInfo.exported) { + return null; + } + return resolveInfo.activityInfo.name; + } + + private static boolean isActivityEnabled(PackageManager pm, ComponentName component) { + int setting = pm.getComponentEnabledSetting(component); + if (setting == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + return true; + } + if (setting != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { + return false; + } + try { + return pm.getActivityInfo(component, 0).isEnabled(); + } catch (NameNotFoundException e) { + return false; + } } @Override -- GitLab From 338ce5db92a3385dff37d03a70c037a76b06f5cd Mon Sep 17 00:00:00 2001 From: Danning Chen Date: Tue, 28 Apr 2020 17:40:21 -0700 Subject: [PATCH 053/166] Query with the flag FLAG_MATCH_PINNED_BY_ANY_LAUNCHER for notification's shortcuts This change is to make sure the pinned-only shortcuts are cached when the associated notifications are posted. Change-Id: I30b104bd07a8c54e0efca236b855b9f7df2c51f8 Test: Manual test Bug: 154663170 --- .../java/com/android/server/notification/ShortcutHelper.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java index 96da649350b0..2e4cfaeb0fb6 100644 --- a/services/core/java/com/android/server/notification/ShortcutHelper.java +++ b/services/core/java/com/android/server/notification/ShortcutHelper.java @@ -18,7 +18,7 @@ package com.android.server.notification; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; +import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER; import android.annotation.NonNull; import android.content.IntentFilter; @@ -171,7 +171,8 @@ public class ShortcutHelper { LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); query.setPackage(packageName); query.setShortcutIds(Arrays.asList(shortcutId)); - query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED); + query.setQueryFlags( + FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER | FLAG_MATCH_CACHED); List shortcuts = mLauncherAppsService.getShortcuts(query, user); ShortcutInfo info = shortcuts != null && shortcuts.size() > 0 ? shortcuts.get(0) -- GitLab From 412bd81933bbf49b888d33109f21e3c8223270e3 Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Tue, 28 Apr 2020 15:10:13 -0700 Subject: [PATCH 054/166] Move ImeAwareTextEdit from Settings to android.widget Also use it for AuthCredentialPasswordView Bug: 154161590 Test: BiometricPromptDemo, authenticate with password Change-Id: I17601848dd9be3d0580988e3ff613c3793dfed51 --- .../java/android/widget/ImeAwareEditText.java | 96 +++++++++++++++++++ .../layout/auth_credential_password_view.xml | 2 +- .../AuthCredentialPasswordView.java | 11 +-- 3 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 core/java/android/widget/ImeAwareEditText.java diff --git a/core/java/android/widget/ImeAwareEditText.java b/core/java/android/widget/ImeAwareEditText.java new file mode 100644 index 000000000000..9cd458558de3 --- /dev/null +++ b/core/java/android/widget/ImeAwareEditText.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; + +/** + * EditText that keeps track of the IME state, specifically its input connection. This is useful + * for clients who request the IME before the system has established a connection. + * @hide + */ +public class ImeAwareEditText extends EditText { + private boolean mHasPendingShowSoftInputRequest; + final Runnable mRunShowSoftInputIfNecessary = () -> showSoftInputIfNecessary(); + + public ImeAwareEditText(Context context) { + super(context, null); + } + + public ImeAwareEditText(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ImeAwareEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public ImeAwareEditText(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + /** + * This method is called back by the system when the system is about to establish a connection + * to the current input method. + * + *

This is a good and reliable signal to schedule a pending task to call + * {@link InputMethodManager#showSoftInput(View, int)}.

+ * + * @param editorInfo context about the text input field. + * @return {@link InputConnection} to be passed to the input method. + */ + @Override + public InputConnection onCreateInputConnection(EditorInfo editorInfo) { + final InputConnection ic = super.onCreateInputConnection(editorInfo); + if (mHasPendingShowSoftInputRequest) { + removeCallbacks(mRunShowSoftInputIfNecessary); + post(mRunShowSoftInputIfNecessary); + } + return ic; + } + + private void showSoftInputIfNecessary() { + if (mHasPendingShowSoftInputRequest) { + final InputMethodManager imm = + getContext().getSystemService(InputMethodManager.class); + imm.showSoftInput(this, 0); + mHasPendingShowSoftInputRequest = false; + } + } + + public void scheduleShowSoftInput() { + final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class); + if (imm.isActive(this)) { + // This means that ImeAwareEditText is already connected to the IME. + // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check. + mHasPendingShowSoftInputRequest = false; + removeCallbacks(mRunShowSoftInputIfNecessary); + imm.showSoftInput(this, 0); + return; + } + + // Otherwise, InputMethodManager#showSoftInput() should be deferred after + // onCreateInputConnection(). + mHasPendingShowSoftInputRequest = true; + } +} diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml index 45638ce5e744..1e0ce0026d8e 100644 --- a/packages/SystemUI/res/layout/auth_credential_password_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml @@ -55,7 +55,7 @@ android:layout_height="0dp" android:layout_weight="1"/> - { - mPasswordField.requestFocus(); - mImm.showSoftInput(mPasswordField, InputMethodManager.SHOW_IMPLICIT); - }, 100); + mPasswordField.requestFocus(); + mPasswordField.scheduleShowSoftInput(); } @Override -- GitLab From ff0555d5f11a5790b8374cf632864126c9df6cc8 Mon Sep 17 00:00:00 2001 From: xshu Date: Tue, 28 Apr 2020 19:01:57 -0700 Subject: [PATCH 055/166] Shorten description for mac randomization dev option Bug: 152446209 Bug: 155189263 Test: Manual verify Change-Id: I7799abec35cba09d7318d6bf2e634580c74b602b --- packages/SettingsLib/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 7baaf494771b..c557e8559490 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -719,7 +719,7 @@ Reduces battery drain & improves network performance - This toggle affects MAC randomization behavior for client mode only.\nWhen this mode is activated, any networks that have MAC randomization enabled may have their MAC addresses re\u2011randomized during association, depending on when the client last disconnected from the network. Re\u2011randomization does not occur if the device reconnects in 4 hours or less. + When this mode is enabled, this device\u2019s MAC address may change each time it connects to a network that has MAC randomization enabled. Metered -- GitLab From 165e592e14dbadd67d3f50b609105c09cf2667e5 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Tue, 28 Apr 2020 19:04:53 -0700 Subject: [PATCH 056/166] Update icon shape for Sam And rename it to Vessel Fixes: 153277903 Test: Manual Change-Id: I63ffffce5162ba7ad59c1beb7a89e924b40e4454 --- packages/overlays/Android.mk | 1 + .../Android.mk | 4 ++-- .../AndroidManifest.xml | 4 ++-- .../res/values/config.xml | 2 +- .../res/values/strings.xml | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) rename packages/overlays/{IconShapeFlowerOverlay => IconShapeVesselOverlay}/Android.mk (90%) rename packages/overlays/{IconShapeFlowerOverlay => IconShapeVesselOverlay}/AndroidManifest.xml (89%) rename packages/overlays/{IconShapeFlowerOverlay => IconShapeVesselOverlay}/res/values/config.xml (67%) rename packages/overlays/{IconShapeFlowerOverlay => IconShapeVesselOverlay}/res/values/strings.xml (86%) diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index dcdb80b497d0..97b0eee3f3c4 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -48,6 +48,7 @@ LOCAL_REQUIRED_MODULES := \ IconShapeRoundedRectOverlay \ IconShapeSquircleOverlay \ IconShapeTeardropOverlay \ + IconShapeVesselOverlay \ NavigationBarMode3ButtonOverlay \ NavigationBarModeGesturalOverlay \ NavigationBarModeGesturalOverlayNarrowBack \ diff --git a/packages/overlays/IconShapeFlowerOverlay/Android.mk b/packages/overlays/IconShapeVesselOverlay/Android.mk similarity index 90% rename from packages/overlays/IconShapeFlowerOverlay/Android.mk rename to packages/overlays/IconShapeVesselOverlay/Android.mk index d410bb72b723..0816e6f7800d 100644 --- a/packages/overlays/IconShapeFlowerOverlay/Android.mk +++ b/packages/overlays/IconShapeVesselOverlay/Android.mk @@ -17,13 +17,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_RRO_THEME := IconShapeFlower +LOCAL_RRO_THEME := IconShapeVessel LOCAL_PRODUCT_MODULE := true LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_PACKAGE_NAME := IconShapeFlowerOverlay +LOCAL_PACKAGE_NAME := IconShapeVesselOverlay LOCAL_SDK_VERSION := current include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/IconShapeFlowerOverlay/AndroidManifest.xml b/packages/overlays/IconShapeVesselOverlay/AndroidManifest.xml similarity index 89% rename from packages/overlays/IconShapeFlowerOverlay/AndroidManifest.xml rename to packages/overlays/IconShapeVesselOverlay/AndroidManifest.xml index 9d20c6b6f82c..025ac6951f0b 100644 --- a/packages/overlays/IconShapeFlowerOverlay/AndroidManifest.xml +++ b/packages/overlays/IconShapeVesselOverlay/AndroidManifest.xml @@ -14,7 +14,7 @@ ~ limitations under the License. --> - + diff --git a/packages/overlays/IconShapeFlowerOverlay/res/values/config.xml b/packages/overlays/IconShapeVesselOverlay/res/values/config.xml similarity index 67% rename from packages/overlays/IconShapeFlowerOverlay/res/values/config.xml rename to packages/overlays/IconShapeVesselOverlay/res/values/config.xml index 73f4f2175a4b..86d31f6450bc 100644 --- a/packages/overlays/IconShapeFlowerOverlay/res/values/config.xml +++ b/packages/overlays/IconShapeVesselOverlay/res/values/config.xml @@ -16,7 +16,7 @@ --> - "M50,0 C60.6,0 69.9,5.3 75.6,13.5 78.5,17.8 82.3,21.5 86.6,24.5 94.7,30.1 100,39.4 100,50 100,60.6 94.7,69.9 86.5,75.6 82.2,78.5 78.5,82.3 75.5,86.6 69.9,94.7 60.6,100 50,100 39.4,100 30.1,94.7 24.4,86.5 21.5,82.2 17.7,78.5 13.4,75.5 5.3,69.9 0,60.6 0,50 0,39.4 5.3,30.1 13.5,24.4 17.8,21.5 21.5,17.7 24.5,13.4 30.1,5.3 39.4,0 50,0 Z" + "M12.97,0 C8.41,0 4.14,2.55 2.21,6.68 -1.03,13.61 -0.71,21.78 3.16,28.46 4.89,31.46 4.89,35.2 3.16,38.2 -1.05,45.48 -1.05,54.52 3.16,61.8 4.89,64.8 4.89,68.54 3.16,71.54 -0.71,78.22 -1.03,86.39 2.21,93.32 4.14,97.45 8.41,100 12.97,100 21.38,100 78.62,100 87.03,100 91.59,100 95.85,97.45 97.79,93.32 101.02,86.39 100.71,78.22 96.84,71.54 95.1,68.54 95.1,64.8 96.84,61.8 101.05,54.52 101.05,45.48 96.84,38.2 95.1,35.2 95.1,31.46 96.84,28.46 100.71,21.78 101.02,13.61 97.79,6.68 95.85,2.55 91.59,0 87.03,0 78.62,0 21.38,0 12.97,0 Z" false diff --git a/packages/overlays/IconShapeFlowerOverlay/res/values/strings.xml b/packages/overlays/IconShapeVesselOverlay/res/values/strings.xml similarity index 86% rename from packages/overlays/IconShapeFlowerOverlay/res/values/strings.xml rename to packages/overlays/IconShapeVesselOverlay/res/values/strings.xml index 47c1479e5a40..a50e7e9a9ab2 100644 --- a/packages/overlays/IconShapeFlowerOverlay/res/values/strings.xml +++ b/packages/overlays/IconShapeVesselOverlay/res/values/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - - Flower + + Vessel -- GitLab From 8966662d4b5ad1521ce402b327b6cda957617382 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 28 Apr 2020 10:43:47 -0700 Subject: [PATCH 057/166] RELAND: "media: lazy MediaCodec.release()" Bug: 129008570 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Test: manual app test Change-Id: I991d9dbf4acc00b0d205da5ea516c5866423e2e6 --- media/jni/android_media_MediaCodec.cpp | 14 ++++++++++++-- media/jni/android_media_MediaCodec.h | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index b2b707b00829..98e68b8b53f2 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -232,6 +232,13 @@ void JMediaCodec::release() { }); } +void JMediaCodec::releaseAsync() { + if (mCodec != NULL) { + mCodec->releaseAsync(); + } + mInitStatus = NO_INIT; +} + JMediaCodec::~JMediaCodec() { if (mLooper != NULL) { /* MediaCodec and looper should have been released explicitly already @@ -1114,7 +1121,10 @@ static sp getMediaCodec(JNIEnv *env, jobject thiz) { } static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { - setMediaCodec(env, thiz, NULL); + sp codec = getMediaCodec(env, thiz); + if (codec != NULL) { + codec->releaseAsync(); + } } static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, const char *msg) { @@ -2889,7 +2899,7 @@ static void android_media_MediaCodec_native_setup( static void android_media_MediaCodec_native_finalize( JNIEnv *env, jobject thiz) { - android_media_MediaCodec_release(env, thiz); + setMediaCodec(env, thiz, NULL); } // MediaCodec.LinearBlock diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 8899fee7a73d..400ce1bc98e7 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -61,6 +61,7 @@ struct JMediaCodec : public AHandler { void registerSelf(); void release(); + void releaseAsync(); status_t enableOnFrameRenderedListener(jboolean enable); -- GitLab From a114b793923b94fc352a00d8dc2ece442f549890 Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Tue, 28 Apr 2020 15:22:01 -0700 Subject: [PATCH 058/166] Fix CTS * fix BubbleExtractor tests to work with this & better test things now that the channel is a tristate Test: atest NotificationManagerTest BubbleExtractorTest Bug: 155025024 Change-Id: I8b75c1b0087ca79cea29741e32c7c524cb056d04 --- .../java/android/app/NotificationChannel.java | 19 ++- .../server/notification/BubbleExtractor.java | 7 +- .../notification/BubbleExtractorTest.java | 108 ++++++++++++------ 3 files changed, 89 insertions(+), 45 deletions(-) diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 9f8d3c4090d6..cf2f7690bc2c 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -161,6 +161,19 @@ public final class NotificationChannel implements Parcelable { USER_LOCKED_ALLOW_BUBBLE }; + /** + * @hide + */ + public static final int DEFAULT_ALLOW_BUBBLE = -1; + /** + * @hide + */ + public static final int ALLOW_BUBBLE_ON = 1; + /** + * @hide + */ + public static final int ALLOW_BUBBLE_OFF = 0; + private static final int DEFAULT_LIGHT_COLOR = 0; private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; @@ -168,12 +181,6 @@ public final class NotificationChannel implements Parcelable { NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_DELETED = false; private static final boolean DEFAULT_SHOW_BADGE = true; - /** - * @hide - */ - public static final int DEFAULT_ALLOW_BUBBLE = -1; - private static final int ALLOW_BUBBLE_ON = 1; - private static final int ALLOW_BUBBLE_OFF = 0; @UnsupportedAppUsage private String mId; diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java index b1a09c1c0d37..d7d413c2ffb0 100644 --- a/services/core/java/com/android/server/notification/BubbleExtractor.java +++ b/services/core/java/com/android/server/notification/BubbleExtractor.java @@ -17,7 +17,7 @@ package com.android.server.notification; import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; -import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE; +import static android.app.NotificationChannel.ALLOW_BUBBLE_OFF; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; @@ -82,10 +82,7 @@ public class BubbleExtractor implements NotificationSignalExtractor { // the app is allowed but there's no channel to check record.setAllowBubble(true); } else if (bubblePreference == BUBBLE_PREFERENCE_ALL) { - // by default the channel is not allowed, only don't bubble if the user specified - boolean userLockedNoBubbles = !recordChannel.canBubble() - && (recordChannel.getUserLockedFields() & USER_LOCKED_ALLOW_BUBBLE) != 0; - record.setAllowBubble(!userLockedNoBubbles); + record.setAllowBubble(recordChannel.getAllowBubbles() != ALLOW_BUBBLE_OFF); } else if (bubblePreference == BUBBLE_PREFERENCE_SELECTED) { record.setAllowBubble(recordChannel.canBubble()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java index 229bd3f35cac..38b71b707196 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java @@ -15,10 +15,13 @@ */ package com.android.server.notification; -import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE; +import static android.app.NotificationChannel.ALLOW_BUBBLE_OFF; +import static android.app.NotificationChannel.ALLOW_BUBBLE_ON; +import static android.app.NotificationChannel.DEFAULT_ALLOW_BUBBLE; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; @@ -59,6 +62,7 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class BubbleExtractorTest extends UiServiceTestCase { + private static final String CHANNEL_ID = "bubbleExtractorChannelId"; private static final String SHORTCUT_ID = "shortcut"; private static final String PKG = "com.android.server.notification"; private static final String TAG = null; @@ -68,12 +72,11 @@ public class BubbleExtractorTest extends UiServiceTestCase { UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); BubbleExtractor mBubbleExtractor; + NotificationChannel mChannel; @Mock RankingConfig mConfig; @Mock - NotificationChannel mChannel; - @Mock Notification.BubbleMetadata mBubbleMetadata; @Mock PendingIntent mPendingIntent; @@ -95,7 +98,8 @@ public class BubbleExtractorTest extends UiServiceTestCase { mBubbleExtractor.setShortcutHelper(mShortcutHelper); mBubbleExtractor.setActivityManager(mActivityManager); - when(mConfig.getNotificationChannel(PKG, UID, "a", false)).thenReturn(mChannel); + mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, IMPORTANCE_DEFAULT); + when(mConfig.getNotificationChannel(PKG, UID, CHANNEL_ID, false)).thenReturn(mChannel); when(mShortcutInfo.getId()).thenReturn(SHORTCUT_ID); } @@ -147,10 +151,10 @@ public class BubbleExtractorTest extends UiServiceTestCase { when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, mUser)).thenReturn(answer); } - void setUpBubblesEnabled(boolean feature, int app, boolean channel) { + void setUpBubblesEnabled(boolean feature, int app, int channel) { when(mConfig.bubblesEnabled()).thenReturn(feature); when(mConfig.getBubblePreference(anyString(), anyInt())).thenReturn(app); - when(mChannel.canBubble()).thenReturn(channel); + mChannel.setAllowBubbles(channel); } // @@ -161,9 +165,8 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testAppYesChannelNo() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - false /* channel */); + ALLOW_BUBBLE_OFF /* channel */); NotificationRecord r = getNotificationRecord(true /* bubble */); - when(mChannel.getUserLockedFields()).thenReturn(USER_LOCKED_ALLOW_BUBBLE); mBubbleExtractor.process(r); assertFalse(r.canBubble()); @@ -171,23 +174,22 @@ public class BubbleExtractorTest extends UiServiceTestCase { } @Test - public void testAppNoChannelYes() throws Exception { + public void testAppYesChannelDefault() { setUpBubblesEnabled(true /* feature */, - BUBBLE_PREFERENCE_NONE /* app */, - true /* channel */); + BUBBLE_PREFERENCE_ALL /* app */, + DEFAULT_ALLOW_BUBBLE /* channel */); NotificationRecord r = getNotificationRecord(true /* bubble */); mBubbleExtractor.process(r); - assertFalse(r.canBubble()); - assertFalse(r.getNotification().isBubbleNotification()); + assertTrue(r.canBubble()); } @Test public void testAppYesChannelYes() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + ALLOW_BUBBLE_ON /* channel */); NotificationRecord r = getNotificationRecord(true /* bubble */); mBubbleExtractor.process(r); @@ -196,10 +198,23 @@ public class BubbleExtractorTest extends UiServiceTestCase { } @Test - public void testAppNoChannelNo() { + public void testAppYesChannelYesFeatureNo() { + setUpBubblesEnabled(false /* feature */, + BUBBLE_PREFERENCE_ALL /* app */, + ALLOW_BUBBLE_ON /* channel */); + NotificationRecord r = getNotificationRecord(true /* bubble */); + + mBubbleExtractor.process(r); + + assertFalse(r.canBubble()); + assertFalse(r.getNotification().isBubbleNotification()); + } + + @Test + public void testAppNoChannelYes() throws Exception { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_NONE /* app */, - false /* channel */); + ALLOW_BUBBLE_ON /* channel */); NotificationRecord r = getNotificationRecord(true /* bubble */); mBubbleExtractor.process(r); @@ -209,10 +224,23 @@ public class BubbleExtractorTest extends UiServiceTestCase { } @Test - public void testAppYesChannelYesUserNo() { - setUpBubblesEnabled(false /* feature */, - BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + public void testAppNoChannelDefault() { + setUpBubblesEnabled(true /* feature */, + BUBBLE_PREFERENCE_NONE /* app */, + DEFAULT_ALLOW_BUBBLE /* channel */); + NotificationRecord r = getNotificationRecord(true /* bubble */); + + mBubbleExtractor.process(r); + + assertFalse(r.canBubble()); + assertFalse(r.getNotification().isBubbleNotification()); + } + + @Test + public void testAppSelectedChannelDefault() { + setUpBubblesEnabled(true /* feature */, + BUBBLE_PREFERENCE_SELECTED /* app */, + DEFAULT_ALLOW_BUBBLE /* channel */); NotificationRecord r = getNotificationRecord(true /* bubble */); mBubbleExtractor.process(r); @@ -225,7 +253,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testAppSelectedChannelNo() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_SELECTED /* app */, - false /* channel */); + ALLOW_BUBBLE_OFF /* channel */); NotificationRecord r = getNotificationRecord(true /* bubble */); mBubbleExtractor.process(r); @@ -238,15 +266,27 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testAppSeletedChannelYes() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_SELECTED /* app */, - true /* channel */); + ALLOW_BUBBLE_ON /* channel */); NotificationRecord r = getNotificationRecord(true /* bubble */); - when(mChannel.getUserLockedFields()).thenReturn(USER_LOCKED_ALLOW_BUBBLE); mBubbleExtractor.process(r); assertTrue(r.canBubble()); } + @Test + public void testAppSeletedChannelYesFeatureNo() { + setUpBubblesEnabled(false /* feature */, + BUBBLE_PREFERENCE_SELECTED /* app */, + ALLOW_BUBBLE_ON /* channel */); + NotificationRecord r = getNotificationRecord(true /* bubble */); + + mBubbleExtractor.process(r); + + assertFalse(r.canBubble()); + assertFalse(r.getNotification().isBubbleNotification()); + } + // // Tests for flagging it as a bubble. // @@ -255,7 +295,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_previouslyRemoved() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(false); setUpShortcutBubble(true /* isValid */); @@ -272,7 +312,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_true_shortcutBubble() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(false); setUpShortcutBubble(true /* isValid */); @@ -287,7 +327,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_true_intentBubble() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(false); setUpIntentBubble(true /* isValid */); @@ -302,7 +342,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_noIntentInvalidShortcut() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(false); setUpShortcutBubble(false /* isValid */); @@ -318,7 +358,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_invalidIntentNoShortcut() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(false); setUpIntentBubble(false /* isValid */); @@ -334,7 +374,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_noIntentNoShortcut() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(false); // Shortcut here is for the notification not the bubble @@ -349,7 +389,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_noMetadata() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(false); NotificationRecord r = getNotificationRecord(false /* bubble */); @@ -363,7 +403,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_notConversation() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(false); setUpIntentBubble(true /* isValid */); @@ -382,7 +422,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_lowRamDevice() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(true); setUpIntentBubble(true /* isValid */); @@ -397,7 +437,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_noIntent() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(true); setUpIntentBubble(true /* isValid */); when(mPendingIntent.getIntent()).thenReturn(null); @@ -413,7 +453,7 @@ public class BubbleExtractorTest extends UiServiceTestCase { public void testFlagBubble_false_noActivityInfo() { setUpBubblesEnabled(true /* feature */, BUBBLE_PREFERENCE_ALL /* app */, - true /* channel */); + DEFAULT_ALLOW_BUBBLE /* channel */); when(mActivityManager.isLowRamDevice()).thenReturn(true); setUpIntentBubble(true /* isValid */); when(mPendingIntent.getIntent()).thenReturn(mIntent); -- GitLab From d64e9452e4283c27128caf34141a7a5b453327ee Mon Sep 17 00:00:00 2001 From: govenliu Date: Wed, 29 Apr 2020 11:04:44 +0800 Subject: [PATCH 059/166] [Wi-Fi] Add proto fields for Openroaming network. Add two new proto fields for Openroaming network use. In settings -> Network and Internet -> Wi-Fi, user may click on an Openroaming Wi-Fi AP for Wi-Fi service, so need to add two metrics to record this behavior: 1. OPENROAMING_TAP: records when user tap on the Openroaming Wi-Fi. 2. OPENROAMING_TAP_ON_WIFI_CONNECTION: records when user alreay connected to any wi-Fi network but still tap on the Openroaming Wi-Fi to change to Openroaming. Bug: 146669261 Test: No, just add proto fields. Change-Id: If203761052646e3a9df6e9a76fd5268c3db965fd --- core/proto/android/app/settings_enums.proto | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 997829eacf96..bfa5d70663ab 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2678,4 +2678,15 @@ enum PageId { // CATEGORY: SETTINGS // OS: R DEVICE_CONTROLS_SETTINGS = 1844; + + // ACTION: Settings > Wi-Fi > Tap on Openroaming Wi-Fi + // CATEGORY: SETTINGS + // OS: R + OPENROAMING_TAP = 1845; + + // When device already using any Wi-Fi service, to track if user still want to use Openroaming + // ACTION: Settings > Wi-Fi > Tap on Openroaming Wi-Fi + // CATEGORY: SETTINGS + // OS: R + OPENROAMING_TAP_ON_WIFI_CONNECTION = 1846; } -- GitLab From d664de2cd5eba8d95092b4ea68540e7f91c2081f Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Tue, 28 Apr 2020 16:29:36 -0400 Subject: [PATCH 060/166] Update when media controls get cleared Some apps include an action to dismiss a media notification, so we should listen for that happening and clear controls in that case. Also, remove STATE_CONNECTING as a condition to clear controls - This was originally added in ag/11056932 as a workaround for an issue with YouTube cast sessions. However this caused issues with other apps like Spotify which set STATE_CONNECTING while still active. YT was using that as a workaround for legacy behavior and will update to use STATE_NONE for R+ builds (b/155213698). In the meantime, listening for when the notification is removed will also work to clear YT's controls as expected. Fixes: 154953276 Fixes: 155029855 Test: manual Change-Id: Ie9320e1406c1f457a39f67705ec1ffcb3a983488 --- .../systemui/media/MediaControlPanel.java | 6 +-- .../android/systemui/qs/QSMediaBrowser.java | 2 +- .../src/com/android/systemui/qs/QSPanel.java | 41 ++++++++++++++++++- .../com/android/systemui/qs/QuickQSPanel.java | 7 +++- .../com/android/systemui/qs/QSPanelTest.java | 5 ++- 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index ddc9c9d7c314..5ccfd8c516ad 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -124,11 +124,7 @@ public class MediaControlPanel { @Override public void onPlaybackStateChanged(PlaybackState state) { final int s = state != null ? state.getState() : PlaybackState.STATE_NONE; - // When the playback state is NONE or CONNECTING, transition the player to the - // resumption state. State CONNECTING needs to be considered for Cast sessions. Ending - // a cast session in YT results in the CONNECTING state, which makes sense if you - // thinking of the session as waiting to connect to another cast device. - if (s == PlaybackState.STATE_NONE || s == PlaybackState.STATE_CONNECTING) { + if (s == PlaybackState.STATE_NONE) { Log.d(TAG, "playback state change will trigger resumption, state=" + state); clearControls(); makeInactive(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java index 302b84203641..9e532868427f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java @@ -82,7 +82,7 @@ public class QSMediaBrowser { public void onChildrenLoaded(String parentId, List children) { if (children.size() == 0) { - Log.e(TAG, "No children found"); + Log.e(TAG, "No children found for " + mComponentName); return; } // We ask apps to return a playable item as the first child when sending diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index cb3d5116a9cd..121e2aa94954 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -53,6 +53,7 @@ import android.widget.LinearLayout; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.NotificationVisibility; import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.media.InfoMediaManager; @@ -75,6 +76,9 @@ import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSliderView; +import com.android.systemui.statusbar.notification.NotificationEntryListener; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; import com.android.systemui.tuner.TunerService; @@ -116,6 +120,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private final DelayableExecutor mBackgroundExecutor; private boolean mUpdateCarousel = false; private ActivityStarter mActivityStarter; + private NotificationEntryManager mNotificationEntryManager; protected boolean mExpanded; protected boolean mListening; @@ -151,6 +156,15 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } }; + private final NotificationEntryListener mNotificationEntryListener = + new NotificationEntryListener() { + @Override + public void onEntryRemoved(NotificationEntry entry, NotificationVisibility visibility, + boolean removedByUser, int reason) { + checkToRemoveMediaNotification(entry); + } + }; + @Inject public QSPanel( @Named(VIEW_CONTEXT) Context context, @@ -161,7 +175,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne @Main Executor foregroundExecutor, @Background DelayableExecutor backgroundExecutor, @Nullable LocalBluetoothManager localBluetoothManager, - ActivityStarter activityStarter + ActivityStarter activityStarter, + NotificationEntryManager entryManager ) { super(context, attrs); mContext = context; @@ -172,6 +187,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mLocalBluetoothManager = localBluetoothManager; mBroadcastDispatcher = broadcastDispatcher; mActivityStarter = activityStarter; + mNotificationEntryManager = entryManager; setOrientation(VERTICAL); @@ -407,6 +423,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mHasLoadedMediaControls = true; } + private void checkToRemoveMediaNotification(NotificationEntry entry) { + if (!useQsMediaPlayer(mContext)) { + return; + } + + if (!entry.isMediaNotification()) { + return; + } + + // If this entry corresponds to an existing set of controls, clear the controls + // This will handle apps that use an action to clear their notification + for (QSMediaPlayer p : mMediaPlayers) { + if (p.getKey() != null && p.getKey().equals(entry.getKey())) { + Log.d(TAG, "Clearing controls since notification removed " + entry.getKey()); + p.clearControls(); + return; + } + } + Log.d(TAG, "Media notification removed but no player found " + entry.getKey()); + } + protected void addDivider() { mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false); mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(), @@ -473,6 +510,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne loadMediaResumptionControls(); } } + mNotificationEntryManager.addNotificationEntryListener(mNotificationEntryListener); } @Override @@ -489,6 +527,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } mDumpManager.unregisterDumpable(getDumpableTag()); mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver); + mNotificationEntryManager.removeNotificationEntryListener(mNotificationEntryListener); super.onDetachedFromWindow(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index becf9da800b3..38dea657242d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -40,6 +40,7 @@ import com.android.systemui.plugins.qs.QSTile.SignalState; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.Utils; @@ -86,10 +87,12 @@ public class QuickQSPanel extends QSPanel { @Main Executor foregroundExecutor, @Background DelayableExecutor backgroundExecutor, @Nullable LocalBluetoothManager localBluetoothManager, - ActivityStarter activityStarter + ActivityStarter activityStarter, + NotificationEntryManager entryManager ) { super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, - foregroundExecutor, backgroundExecutor, localBluetoothManager, activityStarter); + foregroundExecutor, backgroundExecutor, localBluetoothManager, activityStarter, + entryManager); if (mFooter != null) { removeView(mFooter.getView()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java index 9a32b1db2ff3..392adf99e511 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java @@ -44,6 +44,7 @@ import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.util.concurrency.DelayableExecutor; import org.junit.Before; @@ -91,6 +92,8 @@ public class QSPanelTest extends SysuiTestCase { private LocalBluetoothManager mLocalBluetoothManager; @Mock private ActivityStarter mActivityStarter; + @Mock + private NotificationEntryManager mEntryManager; @Before public void setup() throws Exception { @@ -101,7 +104,7 @@ public class QSPanelTest extends SysuiTestCase { mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher, mQSLogger, mForegroundExecutor, mBackgroundExecutor, - mLocalBluetoothManager, mActivityStarter); + mLocalBluetoothManager, mActivityStarter, mEntryManager); // Provides a parent with non-zero size for QSPanel mParentView = new FrameLayout(mContext); mParentView.addView(mQsPanel); -- GitLab From 7e0a1a8ec7ba90ef42ff33673f03af02650b3d7b Mon Sep 17 00:00:00 2001 From: Alex Buynytskyy Date: Mon, 27 Apr 2020 17:06:10 -0700 Subject: [PATCH 061/166] Unavailable DataLoader status. This is a temporary failure, does not fail the session, but requires caller to re-commit. E.g. there are connectivity issues which can be fixed later. Bug: b/153874006 Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest Change-Id: I02791a2963130dbecb510c4a7cafcf04f6245761 --- .../content/pm/IDataLoaderStatusListener.aidl | 6 +++++- .../server/pm/PackageInstallerSession.java | 16 +++++++++++----- services/incremental/IncrementalService.cpp | 18 ++++++++++++++++-- services/incremental/IncrementalService.h | 1 + 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl index 24a62c5638ec..efb00a016da5 100644 --- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl +++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl @@ -40,11 +40,15 @@ oneway interface IDataLoaderStatusListener { /** Installation can't continue as DataLoader failed to stream necessary data. */ const int DATA_LOADER_IMAGE_NOT_READY = 6; + /** DataLoader instance can't run at the moment, but might recover later. + * It's up to system to decide if the app is still usable. */ + const int DATA_LOADER_UNAVAILABLE = 7; + /** DataLoader reports that this instance is invalid and can never be restored. * Warning: this is a terminal status that data loader should use carefully and * the system should almost never use - e.g. only if all recovery attempts * fail and all retry limits are exceeded. */ - const int DATA_LOADER_UNRECOVERABLE = 7; + const int DATA_LOADER_UNRECOVERABLE = 8; /** Data loader status callback */ void onStatusChanged(in int dataLoaderId, in int status); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index d9275f59cbda..9c0ac1884bd5 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2691,17 +2691,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } break; } + case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: { + // Don't fail or commit the session. Allow caller to commit again. + sendPendingStreaming(mContext, mRemoteStatusReceiver, sessionId, + "DataLoader unavailable"); + break; + } case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE: mDataLoaderFinished = true; dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "DataLoader reported unrecoverable failure."); - return; + break; } } catch (RemoteException e) { // In case of streaming failure we don't want to fail or commit the session. // Just return from this method and allow caller to commit again. sendPendingStreaming(mContext, mRemoteStatusReceiver, sessionId, - new StreamingException(e)); + e.getMessage()); } } }; @@ -3059,13 +3065,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private static void sendPendingStreaming(Context context, IntentSender target, int sessionId, - Throwable cause) { + @Nullable String cause) { final Intent intent = new Intent(); intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING); - if (cause != null && !TextUtils.isEmpty(cause.getMessage())) { + if (!TextUtils.isEmpty(cause)) { intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, - "Staging Image Not Ready [" + cause.getMessage() + "]"); + "Staging Image Not Ready [" + cause + "]"); } else { intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready"); } diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 10368586999c..992a4ef76f04 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -1708,15 +1708,19 @@ bool IncrementalService::DataLoaderStub::setTargetStatus(int newStatus) { { std::unique_lock lock(mStatusMutex); oldStatus = mTargetStatus; - mTargetStatus = newStatus; - mTargetStatusTs = Clock::now(); curStatus = mCurrentStatus; + setTargetStatusLocked(newStatus); } LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> " << newStatus << " (current " << curStatus << ")"; return fsmStep(); } +void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) { + mTargetStatus = status; + mTargetStatusTs = Clock::now(); +} + bool IncrementalService::DataLoaderStub::waitForStatus(int status, Clock::duration duration) { auto now = Clock::now(); std::unique_lock lock(mStatusMutex); @@ -1782,6 +1786,9 @@ bool IncrementalService::DataLoaderStub::fsmStep() { } switch (targetStatus) { + case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE: + // Do nothing, this is a reset state. + break; case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: { return destroy(); } @@ -1796,6 +1803,7 @@ bool IncrementalService::DataLoaderStub::fsmStep() { case IDataLoaderStatusListener::DATA_LOADER_CREATED: switch (currentStatus) { case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: + case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE: return bind(); case IDataLoaderStatusListener::DATA_LOADER_BOUND: return create(); @@ -1825,9 +1833,15 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount if (mCurrentStatus == newStatus) { return binder::Status::ok(); } + oldStatus = mCurrentStatus; mCurrentStatus = newStatus; targetStatus = mTargetStatus; + + if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE) { + // For unavailable, reset target status. + setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE); + } } LOG(DEBUG) << "Current status update for DataLoader " << mId << ": " << oldStatus << " -> " diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 81fbe74be606..d5c612daee58 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -187,6 +187,7 @@ private: bool destroy(); bool setTargetStatus(int status); + void setTargetStatusLocked(int status); bool waitForStatus(int status, Clock::duration duration); bool fsmStep(); -- GitLab From a3d358f2772ac0070ce38b758e2277f6c40bca31 Mon Sep 17 00:00:00 2001 From: Heemin Seog Date: Tue, 28 Apr 2020 22:31:03 -0700 Subject: [PATCH 062/166] Lazy load status icon related components PhoneStatusBarPolicy is a complex object with many underlying dependencies. Lazy load until there is a better story around it. Bug: 147455109 Test: manual (systrace) Change-Id: I726a797f37c0816ab189a4c5f4fd5eecb0692e53 --- .../car/navigationbar/CarNavigationBar.java | 16 ++++++++-------- .../car/navigationbar/CarNavigationBarTest.java | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java index 3b643697022d..fcc8c8cddeb1 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java @@ -78,8 +78,8 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks private final IStatusBarService mBarService; private final Lazy mKeyguardStateControllerLazy; private final ButtonSelectionStateController mButtonSelectionStateController; - private final PhoneStatusBarPolicy mIconPolicy; - private final StatusBarIconController mIconController; + private final Lazy mIconPolicyLazy; + private final Lazy mIconControllerLazy; private final int mDisplayId; @@ -124,8 +124,8 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks IStatusBarService barService, Lazy keyguardStateControllerLazy, ButtonSelectionStateController buttonSelectionStateController, - PhoneStatusBarPolicy iconPolicy, - StatusBarIconController iconController + Lazy iconPolicyLazy, + Lazy iconControllerLazy ) { super(context); mResources = resources; @@ -140,8 +140,8 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks mBarService = barService; mKeyguardStateControllerLazy = keyguardStateControllerLazy; mButtonSelectionStateController = buttonSelectionStateController; - mIconPolicy = iconPolicy; - mIconController = iconController; + mIconPolicyLazy = iconPolicyLazy; + mIconControllerLazy = iconControllerLazy; mDisplayId = context.getDisplayId(); } @@ -238,8 +238,8 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks // Must be called on the main thread due to the use of observeForever() in // mIconPolicy.init(). mMainHandler.post(() -> { - mIconPolicy.init(); - mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController); + mIconPolicyLazy.get().init(); + mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconControllerLazy.get()); }); } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java index adf435972e5b..c555f64825d8 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java @@ -91,7 +91,7 @@ public class CarNavigationBarTest extends SysuiTestCase { mCarNavigationBarController, mWindowManager, mDeviceProvisionedController, new CommandQueue(mContext), mAutoHideController, mButtonSelectionStateListener, mHandler, mBackgroundHandler, mBarService, () -> mKeyguardStateController, - mButtonSelectionStateController, mIconPolicy, mIconController); + mButtonSelectionStateController, () -> mIconPolicy, () -> mIconController); } @Test -- GitLab From d617c8c825388993cacb4bc75cfa3704c36b8a91 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Tue, 28 Apr 2020 22:40:29 -0700 Subject: [PATCH 063/166] Add pebble shape to Android.mk Fixes: 155249319 Test: manual Change-Id: If0a291afa8ee97b4109ddd08c27c6708f6bceb0a --- packages/overlays/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index 97b0eee3f3c4..550b8713024a 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -45,6 +45,7 @@ LOCAL_REQUIRED_MODULES := \ IconPackRoundedSettingsOverlay \ IconPackRoundedSystemUIOverlay \ IconPackRoundedThemePickerOverlay \ + IconShapePebbleOverlay \ IconShapeRoundedRectOverlay \ IconShapeSquircleOverlay \ IconShapeTeardropOverlay \ -- GitLab From 16e0cbc048c575ee60f9944127797690b2b2e15e Mon Sep 17 00:00:00 2001 From: kwaky Date: Tue, 28 Apr 2020 22:53:15 -0700 Subject: [PATCH 064/166] Add mBouncer null checks for view-related requests. CarKeytuardView is inflated when CarKeyguardView is first started, and mBouncer is assigned a value onFinishInflated. Since some of the view-related requests that reference mBouncer could be called before that event, we need to add null checks for them. Such a check is redundant for methods that check for mShowing, since if mShowing is true, it is guaranteed that mBouncer is assigned a value. Bug: 154928953 Test: Manual and Existing Unit Tests Change-Id: I796e8bc73459df3b2a0374818556262ebc2ea237 --- .../car/keyguard/CarKeyguardViewController.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java index 6d659f192593..baa6ac945a8a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java @@ -151,7 +151,9 @@ public class CarKeyguardViewController extends OverlayViewController implements @Override public void notifyKeyguardAuthenticated(boolean strongAuth) { - mBouncer.notifyKeyguardAuthenticated(strongAuth); + if (mBouncer != null) { + mBouncer.notifyKeyguardAuthenticated(strongAuth); + } } @Override @@ -204,11 +206,15 @@ public class CarKeyguardViewController extends OverlayViewController implements @Override public void onFinishedGoingToSleep() { - mBouncer.onScreenTurnedOff(); + if (mBouncer != null) { + mBouncer.onScreenTurnedOff(); + } } @Override public void onCancelClicked() { + if (!mShowing) return; + getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false); getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false); @@ -228,6 +234,8 @@ public class CarKeyguardViewController extends OverlayViewController implements @Override public void startPreHideAnimation(Runnable finishRunnable) { + if (!mShowing) return; + mBouncer.startPreHideAnimation(finishRunnable); } @@ -260,12 +268,12 @@ public class CarKeyguardViewController extends OverlayViewController implements @Override public boolean isBouncerShowing() { - return mBouncer.isShowing(); + return mBouncer != null && mBouncer.isShowing(); } @Override public boolean bouncerIsOrWillBeShowing() { - return mBouncer.isShowing() || mBouncer.inTransit(); + return mBouncer != null && (mBouncer.isShowing() || mBouncer.inTransit()); } @Override -- GitLab From e79aa09f873117177354fa61e1ed8ba6180700bb Mon Sep 17 00:00:00 2001 From: TYM Tsai Date: Mon, 27 Apr 2020 21:31:27 +0800 Subject: [PATCH 065/166] Update the documentation for inline suggestion APIs Update the APIs documentation and add test cases Bug: 152316540 Test: atest InlinePresentationStyleUtilsTest Change-Id: Icff5d5b36fecd94ceb7db5446eefa16b4b1aac3b --- .../inputmethod/InlineSuggestionsRequest.java | 11 +++- .../widget/inline/InlinePresentationSpec.java | 11 +++- .../InlinePresentationStyleUtilsTest.java | 54 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index 4d4faa4ba2a3..cce109074d82 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -72,6 +72,9 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The extras state propagated from the IME to pass extra data. + * + *

Note: There should be no remote objects in the bundle, all included remote objects will + * be removed from the bundle before transmission.

*/ private @NonNull Bundle mExtras; @@ -261,6 +264,9 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The extras state propagated from the IME to pass extra data. + * + *

Note: There should be no remote objects in the bundle, all included remote objects will + * be removed from the bundle before transmission.

*/ @DataClass.Generated.Member public @NonNull Bundle getExtras() { @@ -513,6 +519,9 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The extras state propagated from the IME to pass extra data. + * + *

Note: There should be no remote objects in the bundle, all included remote objects will + * be removed from the bundle before transmission.

*/ @DataClass.Generated.Member public @NonNull Builder setExtras(@NonNull Bundle value) { @@ -595,7 +604,7 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1587537617922L, + time = 1588109685838L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") diff --git a/core/java/android/widget/inline/InlinePresentationSpec.java b/core/java/android/widget/inline/InlinePresentationSpec.java index 9f966d84eb34..5f924c6ae194 100644 --- a/core/java/android/widget/inline/InlinePresentationSpec.java +++ b/core/java/android/widget/inline/InlinePresentationSpec.java @@ -44,6 +44,9 @@ public final class InlinePresentationSpec implements Parcelable { /** * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case * the default system UI style will be used. + * + *

Note: There should be no remote objects in the bundle, all included remote objects will + * be removed from the bundle before transmission.

*/ @NonNull private final Bundle mStyle; @@ -122,6 +125,9 @@ public final class InlinePresentationSpec implements Parcelable { /** * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case * the default system UI style will be used. + * + *

Note: There should be no remote objects in the bundle, all included remote objects will + * be removed from the bundle before transmission.

*/ @DataClass.Generated.Member public @NonNull Bundle getStyle() { @@ -260,6 +266,9 @@ public final class InlinePresentationSpec implements Parcelable { /** * The extras encoding the UI style information. Defaults to {@code Bundle.Empty} in which case * the default system UI style will be used. + * + *

Note: There should be no remote objects in the bundle, all included remote objects will + * be removed from the bundle before transmission.

*/ @DataClass.Generated.Member public @NonNull Builder setStyle(@NonNull Bundle value) { @@ -293,7 +302,7 @@ public final class InlinePresentationSpec implements Parcelable { } @DataClass.Generated( - time = 1586935491105L, + time = 1588109681295L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/widget/inline/InlinePresentationSpec.java", inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static @android.annotation.NonNull android.os.Bundle defaultStyle()\nprivate boolean styleEquals(android.os.Bundle)\npublic void filterContentTypes()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []") diff --git a/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java index 8e4f38ef15d3..35c56818a108 100644 --- a/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java @@ -16,9 +16,13 @@ package com.android.internal.util; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.os.Binder; import android.os.Bundle; import androidx.test.filters.SmallTest; @@ -110,4 +114,54 @@ public class InlinePresentationStyleUtilsTest { bundle2.putInt("KEY", 22); assertFalse(InlinePresentationStyleUtils.bundleEquals(bundle1, bundle2)); } + + @Test + public void testFilterContentTypes_nullOrEmpty() { + InlinePresentationStyleUtils.filterContentTypes(null); + InlinePresentationStyleUtils.filterContentTypes(new Bundle()); + } + + @Test + public void testFilterContentTypes_basic() { + Bundle bundle = new Bundle(); + bundle.putInt("int", 11); + bundle.putString("str", "test"); + bundle.putString("null", null); + + InlinePresentationStyleUtils.filterContentTypes(bundle); + + assertEquals(11, bundle.getInt("int")); + assertEquals("test", bundle.getString("str")); + assertTrue(bundle.keySet().contains("null")); + } + + @Test + public void testFilterContentTypes_binder_removedBinder() { + Bundle bundle = new Bundle(); + bundle.putInt("int", 11); + bundle.putString("str", "test"); + bundle.putString("null", null); + bundle.putBinder("binder", new Binder()); + + InlinePresentationStyleUtils.filterContentTypes(bundle); + + assertEquals(11, bundle.getInt("int")); + assertEquals("test", bundle.getString("str")); + assertTrue(bundle.keySet().contains("null")); + assertNull(bundle.getBinder("binder")); + } + + @Test + public void testFilterContentTypes_binderInChild_removedBinder() { + Bundle child = new Bundle(); + child.putBinder("binder", new Binder()); + Bundle bundle = new Bundle(); + bundle.putBundle("child", child); + + InlinePresentationStyleUtils.filterContentTypes(bundle); + + Bundle child2 = bundle.getBundle("child"); + assertNotNull(child2); + assertNull(child2.getBinder("binder")); + } } -- GitLab From 9c988b2980a3f8c6dcb83e6ca7812fdf7d823f13 Mon Sep 17 00:00:00 2001 From: Santos Cordon Date: Thu, 23 Apr 2020 17:08:40 +0100 Subject: [PATCH 066/166] Add logging to Dream stop. Unexpected stop in dreams get logged very generically and are difficult to debug. Additionally, in many cases of stopping, DreamController requests the dream to stop wherein the dream finishes itself. This ultimately gets logged as a normal finish. Added code to save the context of the original request to stop so that it gets logged appropriately. Bug: 150436621 Test: Manually tested the new logged reasons. Change-Id: I25e348026b514dffe540cf8c4cf3a9b87d385fde --- .../server/dreams/DreamController.java | 26 ++++++++++++------- .../server/dreams/DreamManagerService.java | 18 ++++++------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 532045320988..5bc69943033e 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -62,6 +62,7 @@ final class DreamController { private final Listener mListener; private final IWindowManager mIWindowManager; private long mDreamStartTime; + private String mSavedStopReason; private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -77,14 +78,15 @@ final class DreamController { public void run() { if (mCurrentDream != null && mCurrentDream.mBound && !mCurrentDream.mConnected) { Slog.w(TAG, "Bound dream did not connect in the time allotted"); - stopDream(true /*immediate*/); + stopDream(true /*immediate*/, "slow to connect"); } } }; private final Runnable mStopStubbornDreamRunnable = () -> { Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted"); - stopDream(true /*immediate*/); + stopDream(true /*immediate*/, "slow to finish"); + mSavedStopReason = null; }; public DreamController(Context context, Handler handler, Listener listener) { @@ -116,7 +118,7 @@ final class DreamController { public void startDream(Binder token, ComponentName name, boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { - stopDream(true /*immediate*/); + stopDream(true /*immediate*/, "starting new dream"); Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream"); try { @@ -141,12 +143,12 @@ final class DreamController { Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(userId))) { Slog.e(TAG, "Unable to bind dream service: " + intent); - stopDream(true /*immediate*/); + stopDream(true /*immediate*/, "bindService failed"); return; } } catch (SecurityException ex) { Slog.e(TAG, "Unable to bind dream service: " + intent, ex); - stopDream(true /*immediate*/); + stopDream(true /*immediate*/, "unable to bind service: SecExp."); return; } @@ -157,7 +159,7 @@ final class DreamController { } } - public void stopDream(boolean immediate) { + public void stopDream(boolean immediate, String reason) { if (mCurrentDream == null) { return; } @@ -173,6 +175,7 @@ final class DreamController { // Give the dream a moment to wake up and finish itself gently. mCurrentDream.mWakingGently = true; try { + mSavedStopReason = reason; mCurrentDream.mService.wakeUp(); mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT); return; @@ -186,7 +189,9 @@ final class DreamController { mCurrentDream = null; Slog.i(TAG, "Stopping dream: name=" + oldDream.mName + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze - + ", userId=" + oldDream.mUserId); + + ", userId=" + oldDream.mUserId + + ", reason='" + reason + "'" + + (mSavedStopReason == null ? "" : "(from '" + mSavedStopReason + "')")); MetricsLogger.hidden(mContext, oldDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING); MetricsLogger.histogram(mContext, @@ -195,6 +200,7 @@ final class DreamController { mHandler.removeCallbacks(mStopUnconnectedDreamRunnable); mHandler.removeCallbacks(mStopStubbornDreamRunnable); + mSavedStopReason = null; if (oldDream.mSentStartBroadcast) { mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL); @@ -233,7 +239,7 @@ final class DreamController { mCurrentDream.mDreamingStartedCallback); } catch (RemoteException ex) { Slog.e(TAG, "The dream service died unexpectedly.", ex); - stopDream(true /*immediate*/); + stopDream(true /*immediate*/, "attach failed"); return; } @@ -287,7 +293,7 @@ final class DreamController { mHandler.post(() -> { mService = null; if (mCurrentDream == DreamRecord.this) { - stopDream(true /*immediate*/); + stopDream(true /*immediate*/, "binder died"); } }); } @@ -312,7 +318,7 @@ final class DreamController { mHandler.post(() -> { mService = null; if (mCurrentDream == DreamRecord.this) { - stopDream(true /*immediate*/); + stopDream(true /*immediate*/, "service disconnected"); } }); } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index eb0257e95b6c..70b2cd4edb57 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -118,7 +118,7 @@ public final class DreamManagerService extends SystemService { public void onReceive(Context context, Intent intent) { writePulseGestureEnabled(); synchronized (mLock) { - stopDreamLocked(false /*immediate*/); + stopDreamLocked(false /*immediate*/, "user switched"); } } }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); @@ -177,7 +177,7 @@ public final class DreamManagerService extends SystemService { // for example when being undocked. long time = SystemClock.uptimeMillis(); mPowerManager.userActivity(time, false /*noChangeLights*/); - stopDreamInternal(false /*immediate*/); + stopDreamInternal(false /*immediate*/, "request awaken"); } private void finishSelfInternal(IBinder token, boolean immediate) { @@ -194,7 +194,7 @@ public final class DreamManagerService extends SystemService { // device may simply go to sleep. synchronized (mLock) { if (mCurrentDreamToken == token) { - stopDreamLocked(immediate); + stopDreamLocked(immediate, "finished self"); } } } @@ -215,9 +215,9 @@ public final class DreamManagerService extends SystemService { } } - private void stopDreamInternal(boolean immediate) { + private void stopDreamInternal(boolean immediate, String reason) { synchronized (mLock) { - stopDreamLocked(immediate); + stopDreamLocked(immediate, reason); } } @@ -370,7 +370,7 @@ public final class DreamManagerService extends SystemService { return; } - stopDreamLocked(true /*immediate*/); + stopDreamLocked(true /*immediate*/, "starting new dream"); Slog.i(TAG, "Entering dreamland."); @@ -387,7 +387,7 @@ public final class DreamManagerService extends SystemService { () -> mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock))); } - private void stopDreamLocked(final boolean immediate) { + private void stopDreamLocked(final boolean immediate, String reason) { if (mCurrentDreamToken != null) { if (immediate) { Slog.i(TAG, "Leaving dreamland."); @@ -403,7 +403,7 @@ public final class DreamManagerService extends SystemService { @Override public void run() { Slog.i(TAG, "Performing gentle wake from dream."); - mController.stopDream(immediate); + mController.stopDream(immediate, reason); } }); } @@ -690,7 +690,7 @@ public final class DreamManagerService extends SystemService { @Override public void stopDream(boolean immediate) { - stopDreamInternal(immediate); + stopDreamInternal(immediate, "requested stopDream"); } @Override -- GitLab From b03365182c803c75863bce64edabcafeda385305 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Wed, 29 Apr 2020 11:17:48 +0100 Subject: [PATCH 067/166] Update nullability of extension versions in Rollback. This updates nullability annotations in Rollback and RollbackStore as suggested in comments on ag/11256434. Bug: 152737927 Test: atest RollbackUnitTest Test: atest RollbackTest Test: atest RollbackStoreTest Change-Id: Ib0973378fdd24ce275b0f3e1a687f9161d3c0e7d --- .../core/java/com/android/server/rollback/Rollback.java | 7 ++++--- .../java/com/android/server/rollback/RollbackStore.java | 5 ++--- .../src/com/android/server/rollback/RollbackUnitTest.java | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index a0f40708627e..ece5a553fd95 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -59,6 +59,7 @@ import java.text.ParseException; import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** @@ -182,7 +183,7 @@ class Rollback { /** * The extension versions supported at the time of rollback creation. */ - private final SparseIntArray mExtensionVersions; + @NonNull private final SparseIntArray mExtensionVersions; /** * Constructs a new, empty Rollback instance. @@ -210,7 +211,7 @@ class Rollback { mState = ROLLBACK_STATE_ENABLING; mTimestamp = Instant.now(); mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0]; - mExtensionVersions = extensionVersions; + mExtensionVersions = Objects.requireNonNull(extensionVersions); } Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId, @@ -234,7 +235,7 @@ class Rollback { mState = state; mApkSessionId = apkSessionId; mRestoreUserDataInProgress = restoreUserDataInProgress; - mExtensionVersions = extensionVersions; + mExtensionVersions = Objects.requireNonNull(extensionVersions); // TODO(b/120200473): Include this field during persistence. This field will be used to // decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting // this field is not backward compatible. We won't fix b/120200473 until S to minimize the diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 792cc45ee180..c304302fc0b1 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -21,7 +21,6 @@ import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.rollback.Rollback.rollbackStateFromString; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; @@ -178,7 +177,7 @@ class RollbackStore { return ceSnapshotInodes; } - private static @Nullable JSONArray extensionVersionsToJson( + private static @NonNull JSONArray extensionVersionsToJson( SparseIntArray extensionVersions) throws JSONException { JSONArray array = new JSONArray(); for (int i = 0; i < extensionVersions.size(); i++) { @@ -190,7 +189,7 @@ class RollbackStore { return array; } - private static @Nullable SparseIntArray extensionVersionsFromJson(JSONArray json) + private static @NonNull SparseIntArray extensionVersionsFromJson(JSONArray json) throws JSONException { if (json == null) { return new SparseIntArray(0); diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index 57eae1d26f7c..8667801889cf 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -331,7 +331,7 @@ public class RollbackUnitTest { public void notifySessionWithSuccess() { int[] sessionIds = new int[]{ 7777, 8888 }; Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER, - sessionIds, null); + sessionIds, new SparseIntArray(0)); // The 1st invocation returns false because not all child sessions are notified. assertThat(rollback.notifySessionWithSuccess()).isFalse(); // The 2nd invocation returns true because now all child sessions are notified. @@ -342,7 +342,7 @@ public class RollbackUnitTest { public void allPackagesEnabled() { int[] sessionIds = new int[]{ 7777, 8888 }; Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER, - sessionIds, null); + sessionIds, new SparseIntArray(0)); // #allPackagesEnabled returns false when 1 out of 2 packages is enabled. rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false)); assertThat(rollback.allPackagesEnabled()).isFalse(); -- GitLab From a244a01bc906cbe06819ce7e7c188d085f70caff Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Wed, 29 Apr 2020 19:13:48 +0800 Subject: [PATCH 068/166] Fixes failed tests in PackageManagerTests - #testDeleteNormalInternalRetainData Removes the check for the directory. Test does not have the access right of the data folder of other packages. - #testIsSignedBy Updates the correct public key for key set A. Bug: 152007236 Test: atest PackageManagerTests Change-Id: I4adbf5e1d321e7f4ed9f1005ec52eb1ea3d04c27 --- core/tests/coretests/AndroidManifest.xml | 2 +- .../coretests/src/android/content/pm/PackageManagerTests.java | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index cc9c91441e84..67a57aaaa101 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1470,7 +1470,7 @@ + android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMpNthdOxud7roPDZMMomOqXgJJdRfIWpkKEqmC61Mv+Nf6QY3TorEwJeghjSmqj7IbBKrtvfQq4E2XJO1HuspmQO4Ng2gvn+r+6EwNfKc9k55d6s+27SR867jKurBbHNtZMG+tjL1yH4r+tNzcuJCsgyAFqLmxFdcxEwzNvREyRpoYc5RDR0mmTwkMCUhJ6CId1EYEKiCEdNzxv+fWPEb21u+/MWpleGCILs8kglRVb2q/WOzAAvGr4FY5plfaE6N+lr7+UschQ+aMi1+uqewo2o0qPFVmZP5hnwj55K4UMzu/NhhDqQQsX4cSGES1KgHo5MTqRqZjN/I7emw5pFQIDAQAB"/> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 79cb1f9f85f9..567552f66b35 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -971,8 +971,6 @@ public class PackageManagerTests extends AndroidTestCase { if (retainData) { assertNotNull(info); assertEquals(info.packageName, ip.pkg.packageName); - File file = new File(info.dataDir); - assertTrue(file.exists()); } else { assertNull(info); } @@ -990,7 +988,6 @@ public class PackageManagerTests extends AndroidTestCase { @LargeTest - @Suppress // TODO(b/152007236): un-suppress when we root cause this public void testDeleteNormalInternalRetainData() throws Exception { deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA); } @@ -2306,7 +2303,6 @@ public class PackageManagerTests extends AndroidTestCase { } } - @Suppress // TODO(b/152007236): un-suppress when we root cause this public void testIsSignedBy() throws Exception { PackageManager pm = getPm(); String mPkgName = mContext.getPackageName(); -- GitLab From 2de4baf18304160a2a6a6a5b62e3622bb9827686 Mon Sep 17 00:00:00 2001 From: Mohammad Samiul Islam Date: Wed, 29 Apr 2020 12:08:14 +0100 Subject: [PATCH 069/166] Make ApexManagerTest use resources that are not prebuilt Currently ApexManagerTest uses a prebuilt apex for testing. This resource was built in a dev environment and targets R, which is not a valid version on release builds. This CL replaces the prebuilt resource with one that gets rebuilt whenever the test is run. Bug: 155242557 Test: atest ApexManagerTest Change-Id: Id6e9e9f688b07da1ad80f44f7486d7266e3a21f3 --- services/tests/servicestests/Android.bp | 1 + .../servicestests/res/raw/apex_test.apex | Bin 318688 -> 0 bytes .../android/server/pm/ApexManagerTest.java | 42 +++++++++--------- 3 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 services/tests/servicestests/res/raw/apex_test.apex diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index b457856e8630..40a17061c7f1 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -99,6 +99,7 @@ android_test { ":PackageParserTestApp1", ":PackageParserTestApp2", ":PackageParserTestApp3", + ":apex.test", ], resource_zips: [":FrameworksServicesTests_apks_as_resources"], } diff --git a/services/tests/servicestests/res/raw/apex_test.apex b/services/tests/servicestests/res/raw/apex_test.apex deleted file mode 100644 index 19b1c5e2e1c67b0eba95fc77ebaceb86ce355feb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 318688 zcmWIWW@h1HVBlb2U|>+RYGLxR%41+)0AV493;c-%sTJ|LiFui6sl_FFS;hHz|4uP5 znBF|W&maKOF^We+U^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1cpKgRC6gP zDCH&QrYcz}C?)6T>Luo-6y;~8=z+TadL^mFB}zI9AT?#FMa7xD2#^WWIm;yjbsE?6dgK0Xt8yFcJ z7Bo2n;|7oWIpzHvra-S~(g5qaiRF0;3@? z8UmvsFu)#87(##Kwl-j>g03ZJMe-w;{z-S1JhQMeDjE2By2#kinXb6mk zz-S1JhQMeDjE2By2n^d00PXSRVPIs^gYNz1VPIm?(_>%&?co(+;9|BWHr#{DmJxj^fb}7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7f#DVcp#J~$>I{u=H7gF;~xkA}c#2#kin zXb6mkz-S1JhQMeDjE2By2#kinXb6mk!0-!!(f0HVu}@<&5pGz3ONU^E0qLtr!nMnhmU1V%$( zGz3ONU^E0qLtr!nhEE9aFo4eW$2|X^mw|!d4!;Ovabj*kPHKEXQEF0YW==_bT4qk_ z@bUeqPewyvGz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtuD^0BHW-IbingYAyu@ zrM$%4R3$3~rR4lvy~MnfqWsJhJhUYC8@db zX_+~xR>cN{wbE2YPc%)9AM}Q&{TcL5q^Scnj?vWkL2vl4j)8Y#Ln8wN!vP0)*^elH z7_k+LBsrK9O*O)7h=htSsQf$E3E@FVbTtss;YsQpAD;dq)>jFCiB&l~)ot25@cugB z$iTqhfN1+6@*fieqJuc?k~48nexC~ez6Z$bXY(*C=^&P1^N z#|3jDnY0QI$p0U9B5S9&|CcnrU`Dnd#(u#Ja|tZ|8Bp54Fm;sBR%Rgoi?t)00kah$ zM%(xY`9BX~8Jq?3Kg8z{5?lM98?Kr>2CF*A|Ar`DM&W>srLF(LR*Vv(Aut*OqaiRF z0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71* zAut*OqaiRF0;3@?8UmvsFd71*U^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0q zLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$( zGz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!n zMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ON zU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(6pV(zXb6mkz-S1JhQMeDjE2By2#kinXb6mk zz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By z2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1J zhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kin zXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeD zjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk zz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjDpb+7!85Z5Eu=C(GVC7fzc2c4S~@R z7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7 zfzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c z4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R z7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fl)9T0;3@?8UmvsFd71*Aut*O zqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs zFd71*Aut*O0~`X~{s9;GlpD)gR=GMvaLkCwyJOak>?HrAXY*GcDSolasklXUikY+b?PrSYFSxuv7TS{#GfL1BaQ{#39Rn zsh&G+ciFt?ytm&j3vP0mSn??4Dzf}B_a%%{98CNB!&9}RDp#ym3{0DdEbpS8FeT;o z?!L2$VaMA}1*)#O+7v}i`KO(X6aE(*-r};>_)WNmX{g2FrALs>ml0gGM&SD4cd>iC z>Tdg&8g}udN+Qe8c9XoFZp6y=_;SE!)8mU&w--NKj4V$m{D!)GSM|d4&OO`$Y?(*? z7s#a^S8uXy8cF^i>hVXZ``>0qP1`zo*VPvFrQfm=d2cLoI&oz*{t+QSY50!n$6x=9 zh7bPmL8Qb{)@TTfhQMeDjE2By2#kinNC^SQFed{B1_nlif0-O0Y*rA>1YyHT1t<$j z!=xECAmS_>5SkTaCWMJX!qg!I6U&lH^7C^P4D}54aA-m(rU9$?Znau|$Hfjd#oMP& zA8}qqX?2C?j)#Z7v_8X}=(}dm^y8y7Wlb3W zMFcL$az48@W`)P~Ya)joB9<&qWKXSsYn|fHYZ9FF@1>0zi_#~>wG3rj@BMafb8qz2 zXu7cLgZb-g3%(8JAMOW!hW z$dp?0y{v(0&FcFlx4%?;G3ZTm=anj6^><;JeP8>It-b>>cs}~7Yep# z{{1ZI^jrV!(S7ST%v|_w{esu)A72yn3Q~3I7ME&e+oy2o=Bk}e(sHKHL!}H~@I5}> z_F%3~xW4Hu4G4^oa^;lV$2klKY-IS#Qj4>6+p&A@`KZ+UNesN}H2lT&3k?~}^Nuv^T&;C;^8U${1&_Y1T)e(=)vQZf>+ZbS zx{F23f9mxc?sGU;_T_qNe&p}b+pj+DU*X9^HdUT?zVmQ~ybN7OWRipBpBwPJ!~)d` zqZtGk7~mKxheQ=;BpM=daWO&pP6U@EvEmyN7+ShmrpL=Q|Cq7)@ZMU+P@d`^PhQ@X5b(9SdisZa6Nyvya^~;UVM2f`iW|Wuci3%c{`)&LEmtkXoUaom$C|oS&B!C_Xx0;>z|Nx z`C7h2v{YB}a!ZSD=cW03@4IS-x4JaYR2w}bZ!co;FMCP1TqEfpy>PbhfQlar&{cm-A8i*V(^{%QHFAi6>H_Rxhi{GysmS_N{*8+x-C7nTsr()Uc~R{(qj)^f1KC;;?f?Cyu}Mewp)oi zy)%9z`1KWt7rcd}cE+~={Hs_VYA{yf)BvG$2~K0f_A@BL02 zZ^a|rO5M4a5|r)4&Ngmz+&uC8)%n6T;YO)hL2WU08D~Rp<=k-It*|lUsK${A2U=DB zw$(bUKmIgnG52JX)6(-eeKm@{wq~$ZxU1}6|7FTauKW{M&mQ!aT^_b)!<|f>{~>Pz zcRkRb{QhpyLZ*@pOQW})3le&Nd6A9PEH!EQg*i)anH?8Dl(+kr2iudraNW$$%LQL0 z_yykE?zr!?bG79Z&#x(NCpP&uWIUT;vo}_ajak#+;5*j+bvFF&OL~R(uCQEVQC+;} z(}NE`94BzxVU=lnw&pMUtv6057yNnVeI{$0|4r68PY3RtUDYLb1# zUf(O^4xcdZN=e3xFDn>K)pF+;T61a47hLs1?r9^>{Hl|ub|tc})CsjZ9^Yojrka1V zs&cNu*XvBjd3Affax8Y;z{niYe9+!~xl7fZDSJiF8k{efU%bM6vba%UH+P=JTYaVa z*gfydljYX`bi~RlN$9LlZ112?F`k7 zm-Bod7IBEz@@eX<`CbKiN{lJZ&hu4Vo_xME+x6jPQw2NQpR*TESDF%ddiAX~XKRiH zC;0CL)u#CG?Yn#}CTFAl%zI7|d#4;+XX-ocXTR5kuD5zAHw5y%WIF8y-#yqaetPe$ z1DAgA8ALQ?N%<~#`$|4&qVJlOCw6>$A)K(+XjaO$C%5iMyoi#${i^NWUAZH6C*y5z zc_cQ4SNI-XGRf}wsp6Tn^L`p%D3d#X-l8sR;mfnrbEV_G&M|KlT3I+Rv0_o`ot-<9 zj~>got!IU;T+eEYy^K)*B+%M(qC{)mBZgf4 z=YFqs#MWD9f=Yr>JQ@O{Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs zFd71*Aut*OqaiRF0;3^-KLi}ZoYLTPjv%G5`9>Ddd?QGJqX9(X!U5jQEL;o>;CVbn zD=kxV0S*=h1`y_ExXurnz>6;^P0CKKG`)G5A3T4F%cN29(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7!85J8v>*2a!1$Yj;_lc>FaV)#{uvC-}bbDk%0k(`54Y{ z7o`^Gmlh?b7V9Mz6(^hCJi$FW6gYTG^ih|MhQMeDjE2By2#kinXb6mkz-S1Jh5)V* zU}E3_xAH+7m>C&l7-WzYGBP0eObUz9zzL3Dnl^?c-t?sdBcRQ017My1_m1_4e}O7*fAi&4nqzpTtWRV5e5zp z&;kraE09SrEX=^b;Ft&ANbQ@LmzkDYT%uQzn=^H?zdp00#IgA=FX?p&2{j#wbdo*l zxuer!g2m39EAy8{oz3e^er((^ng79Ni=PaBcI_?qdv2EL`p!7H+4%Fn%6sYe&aiQ9 zHC2(+T@^h+>s9~bGj&pHnqx08eSaXir@{OKUsz8GyW9cc3RY1I?_B{)T&#Bm`7U?q zTfNfCrhSgI)giIR>mP5wlf3I^+RNvmR~j|1SuQ`<@J4g)hU`_nPlfKk5Byx;;q`jk zikl^0d-+n{3n#7?taME|tJ#;3bvUHm+$lOL{o3`&(@)pz>GcM+nwJW5Um4uK z6*6_^gxbdKFMoZX{J4Afs~u8nHYTt7cR}2$O>Ft*xV1u-f;_zYp1&(Ae#aK}S8}GI zIpI8$HrtvZ&c(_>CwXu@y3Qr{J+*EaQNiuXh^|8U+wfRjTjZDga+AmWl`h9_w;63y&6j3#T^{Io@%QO9%J&#PM{CJo5zX)Sd3#p!8e@Aj%O0SI+$Fd(HKguQ6 z3E8p=?`(OT&3IAg?E2vAmg|a~Tg7j_(DzPRt>t%Z$tOOsz0BWr@_(&CB);o%d`j=^MXQtVoJqKQ zB-r%yp;IaeHFDA$r)|lZ{bs^eYx@HyE^e*xDfn5twmK#6+k@D@te|MzyMygyCnEzx z7!$r|6l7pvK#j(rV8^qezwIyomfwCpO(Kn_*wbW*$o=y<#huAJMbko|6!dD$w(Lir(k?sit@<^3&6gZ-3i9uikgx{=uIwReR>W zfByPog>BsV>%YIUR2F&cm{Zv0eX2{zg-zf?%5{f%Ygif_3cMC5#pvlAWffvNlimKBEB&|U_)^Zo{p#;>cFPVct3UBS-sNAMaww%?wqcirw@e3nVVZ@F;CEZM zi3xm<3dQbU+MjjXhT->d#`WCHPZ_;lJ27xHusbZ76|c&fv&FOI^!4B33#96&Zhfn-4Re%5XU*VjcM`sF1@Vzvx`15#K~Cdv*5-C49B6 zmYe#m`Ej%7+w>B9iFJDxe`2*Xyj|z*AUyZfR4?b-=|99WU#i;l+|)gMaklo4YkJ)? zUmEs24|hK+n2|okRaEKmmv0qivQ|Y~`!pmsz0qz`-&n}<{8UGfRBo3J>y(|mc-m$CwvkE?msoOepi*}FZ=>R6K&hvzBp z;H3xiCm$2-vskeCILC!unac4;89y(G59CaV<+k56|9NVnh4WpHo0iZ2tfE ze151(%F?c7k7MT_dvl_gBP!tP)g|t8n9b9yN;dsGuXdP&Tlr7(8%DH03&x_TDwSf(GXy#K=mj@P39qlFeaKfM+@|M{if?W>Gs{O+?a5YX%> z(0{@FYEAxhKA~lN!Lea^Qg7{|yjE_=zxPhhY4fHTjvE)eJGR-*hdIRm&wrMsyi8I| zk{K<*Kmv-~mE!g+? zro!`2`}2Pv%?*5=lhZg8Fuy!jl5pXSnkwXcMpZ=IOywW`af zcFNVWjoWvNi?GXlS?9|tQnJ^x*ZEJ4?HQAg5%a=IkGW6hexKC(Idt7RBcbaKhLdZ} z?e;iXo?5P#ICG!*4n3#8pBz`8S;JocSj<*PV4Yii4Oi_VpQU$f6{RmfURJcD)kI&b z&$|4|xwV|m-=|&k*=70t+UZqE8;n2ux(DU`o0?PWlQcQ*-IS|a<{eb1Jij@yuI;(a z&5sXF!+Z1OZYJMe=O}f|x^h}nklRJglUr1}5^Gx*H?v1C^Eld6+qdlArTHo=Cg-O5 zPq6;oJ0;`{??Xk!I~Vn{HosT=#rfUoPtKR4%w`&aGY#JE$x^He`X&D(^ml47Q*rCT z@AJf09zAyXYjN-M)t|roIH-MdQ*L*4$xf~I!cuLixyf(8bTS3EUVYo~&3%5|S$F1M zGrxU@W&fCSbC!L-bH*>~mG0L1f^E&h{afQU9$otN-jRqmx_+7*KR&X1itx0H$1u1_{JY{^#Ew@c1Z6k3&f?AOA#5h+vnrdO^M zj^6LGXV=x8J5%;Yy*As(_3Ku2|E^OjdJ-45{!-EEy*Oe2hlf=MdlL`pTo9VSG;D+W z3gH_Ec3jo`Fzuq>orUk-xyY#7yxTo_v#aLOGe0)xd|oN`?894$HBUeJH}CVW;z{}?%)yX&4gM_*-`U})=X zoL!a~W&G0V@?Y;nsiV&$O;2CX%ydvbp1texg7w#~?JAWHpPl7eYp?S7<*i!+(l4TC zwRm&}e?DHm+U`ct#qN@Kj~74LYEu2UKl0Ghoh9KR%F84lP19Hq(EMjk=$vUL675k} zoCLC)CTjB@mDtw4*Kr zUq?SrH`m}0JzuxfGrsvv1`G`kcX$3jxLa@yGrOAgf`$hnM>N;PiLtRp+i~eC{dm1y zCwg1Y>)Gb#kDV`UsWAN{@OZ|v&5Q!a{>JU!{?*t-I-~N02A}!;=@(Km_kT~yT-<3R z`_kfH&SuA?lc$=!E??f)yX@`Y1n4B45Owd3xqNwT)FfH9?zNeId+vJK^#4EIdVkp!e%3ihW4AB- z%w2hHdc>;E{bk0M8ca-wP8{nF@GaGJG}O3%+{O8LUv;g2z-`rVm!|EvzdD^X+3}u< z@5+pjYZo<+US53S`qL=8Q~_zWj=Xx0HjBNR7zz)j+q_uyZLi3|TXt72n93=9lczJJ~3bAkKld5@HL8;?w5iJv%a zwQ!=t;j2xqO}v|RD}^VOd@}ywt@+02Sy%8UXDik7jT=5n3*7Zudo0Io>hlH;HU6xCgv9ln3))vm^c|`Of-zXH78ZdfR~L^tIebBJ1-+6H!Fid;}k<~15P&P zP!={}rqEzRVFN)Bhl7X9IWZ?QEx#x)Gtp4YKm;Vn&co}QUz%5vn3<;#mYG^^C}6+` z66fM!hjyf4BHS1v%m@)B0|mGnI2px+p{JQSKxw_?{9FS$ab80ULjz+2LnA|DQ^P26 zUK29|V?%Q&cd$CUiAf1LfEigCn46gR84Q}3xR{!l7#a3HXjxz*ZJ%ZFB;pgZ#A8m! zTNgTYO!;nIbkhym8Z;x<;Z$9MyYYY50L{?q(s!R<#<3Xk`ClpfDDzsmVCDa_1*#7_ zShsKcIdR&dyvbe<)Yt`oWZbixzqC8+>HVxvn;336ne3dYvf|Iyvqh7qCv3AbUcpp! z!OgFbHH6JL*8X6({Cm~!uTrKhm{g$gt6tdDbqW8%_=7K}nRj%~YZ5owpn7ZlcjNuX z&0oH_E$;WvMe-)|u^jW2TwTqYH6@#c)BkQZ{e9Kq_)NanEH;MK%s-=p!#?R|opQXe zIdq@N$#VxeRos%9&A!UtKJi?E|7Ff}rb*Hk#%3wv3fiSxYyRdc1%6slu`NKVQ`|gd zL0rq!4v|c@E%tYKvYmw|+%{p}{6am5_o7+MU4eRcdGiqW5Wo406LZDX1zn?C+3xe) zJa*yjo@YY8-T9a9Dm(u5wxjzyuFRN~%UjNrMy`2zRCdC&YmX94KHZ#_^k~|oe+CC@ z_9qyquDtUi;kMonlk_WxCrpUsW?p(y=|$hg{R~T4bo#>Qddt;$3;(?Fz*2PWoD_Rr z`=l+`&QGee-R@I$<(<;YBM+ZjOv_hKt0)z;-)2x#_EXA8eD=Z3T`#XJJnp)XVef-) zj9>VoWqwb1xcBEu)D;N7A94$CruUu9O27TEM4+g&$1qn{y~=s3 zrE%#bZkEpDD(?ksn{4>rO^-To+Uv7a-@0`b(tdZY$21-Ge*2=mzI27vwZ*z~T#fHZ z8n`S8WbW9s@Ws^j$p%H2H$7YuAJBDkj(J>dO|Hx2WHu++b;}ZJO_Z)O*1mS*DqWbo z?QQ#)(kJS6ZS6wr+j1<|Uwr29iT9$j!m8)tGmk%QujbmbIzg|Yc&gOeu8$s5UR5mq zu+r0${kLcI&-{|zHHk|Fa{@Qlf6EGv-JB{mKgqJE_lSPooHGkdmgjxE%zQ~OC3$x_ zN5LsCp^Sa@AGXe|?k{xo+9jS9z$)3Y{@pWw%cWP2^*lY=GI{#43CiLPyA&orw|t?X za>d=}+VyE&ejnUjPOS2jIN9Xxt6_B|Q#_#cb;`U&$+Gi$EKGm0oS%K`O47dP_H!46 zx#$J3e*G;_bzspYKAj8d{6c1`Y;kdQxBiH}e#@v_e<&`*M)~JO8-cBh7I#b(nJxT? z^^Nggr*C>D`QLM=1$@{lT2YgwJX!0*+NlcDV!uz|IPlp=zD_8^aPxPGW48}xJ)Eh> z^H-DW;F+&?rwcRfv~dl(estQ?PxX$6yo{_ThmUme+lb6^67Gs);EhAy6QKQ zuk6henOFDvMo5s(ioCy;CJ7C=a%bfrPL`s zJFOq#B`$Si$H%n1wt9CvNri2q9LqX4KRh0?C%$L5tj52a{Tp`Pw%cpZeVC>6?~T4{ zAEpPhUoUB0u=ne%9JAM_rnrVbTKRv`(gfLxv&ZZ?O#Z})E#^JzZrvo@CVT8`gy%Z; zYIBV>3%6>2oBQ?io)=F0p3eQ|u#xLb&li`>Y8BBvYgo!tO-xBa zeLB%~NAz@Y=9S@2kNh`pylnen30uVmow?<}33^7ihHk2Bf7UgApr zl)v6;|J<1622JO#e|hSDa%OY!t7gNjYW)RD4X-1=-ca_>SU>w%F}oPgk~s_K$3LE3 z!t&lm&ixX5!s&O9cdQE9q4MU@&1Fs1=U-2F(qHmQ;>@|jXAjKPlTc#}U&iv;|9p7OVsV1H=FS|3Ql)7+`Gl7HpGo z?M74ALp8sbEzB(A)Rz}(EjGgr9owT5MYeMja} z#dz852+?V$QY_|`Z8kicQP5Equ~W_Z=&8vwZzycOxYHuW>aL03y~HU|QjVKmio{(K zUJ=3<)a>J;t?4p*ZR5?1Z^=DgCsJDfCMD=?n_oFCp<~7TT+^L0$9a2KyHAqga@~|+ zP@8OE{^@=|!{_>0JMRat{W0b5J*iU%tQn{6s4!J6Kk`HkaJ+ootwZMS5y6K#^M$-^MHHP) z42|s#V_ui3=@%HvKo$RfLMq99>124o4yo5lgiBW(zBNG<`;sn4JCLgOj1_lNY7Gk)- zk7^nCJiuJoYV@q){Jejs7#K`%o)|qE5H+Aid7~jP8UmvsFd71*Aut*OqaiRF0;3@? z8UmvsFd71*Auy;z0DU~*gLWGqg8>Tz2n#TzAdLs)2{15##{&uyD|7M_Q}i-((@k$C zjE)2h>i8XX+-L}lhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2Ba2?6vTI{KY`Nd5?5 zf%ecxZ}3A3l~Lws2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-R~z!4N6c&cjo4gR=G0GYZfzc2c4S~@R7!85Z5Eu=C(GVC7fuS1$pniY|1L7{JVx$|0 zKzB(YeAZCNg6(E0bOVs@z5!iSgfJkbm<8>=n_xF|-N?u6gYL&c=+-Vp*Bun>h^`&^ n#CXtI{0Qy;YLK)eoy;HL&B_K+#lgVAaLR^(fnfm)0|Ns9oqub? diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 3718c5c5a5e1..0fbd1b64c379 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -36,7 +36,6 @@ import android.apex.ApexSessionParams; import android.apex.IApexService; import android.content.Context; import android.content.pm.PackageInfo; -import android.os.FileUtils; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -44,7 +43,6 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.frameworks.servicestests.R; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.TestPackageParser2; @@ -52,11 +50,12 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; @SmallTest @Presubmit @@ -64,6 +63,7 @@ import java.io.InputStream; public class ApexManagerTest { private static final String TEST_APEX_PKG = "com.android.apex.test"; + private static final String TEST_APEX_FILE_NAME = "apex.test.apex"; private static final int TEST_SESSION_ID = 99999999; private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777}; private ApexManager mApexManager; @@ -274,7 +274,7 @@ public class ApexManagerTest { } private ApexInfo[] createApexInfo(boolean isActive, boolean isFactory) { - File apexFile = copyRawResourceToFile(TEST_APEX_PKG, R.raw.apex_test); + File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME); ApexInfo apexInfo = new ApexInfo(); apexInfo.isActive = isActive; apexInfo.isFactory = isFactory; @@ -308,27 +308,29 @@ public class ApexManagerTest { return params; } - /** - * Copies a specified {@code resourceId} to a temp file. Returns a non-null file if the copy - * succeeded - */ - File copyRawResourceToFile(String baseName, int resourceId) { - File outFile; + // Extracts the binary data from a resource and writes it to a temp file + private static File extractResource(String baseName, String fullResourceName) { + File file; try { - outFile = File.createTempFile(baseName, ".apex"); + file = File.createTempFile(baseName, ".apex"); } catch (IOException e) { throw new AssertionError("CreateTempFile IOException" + e); } - - try (InputStream is = mContext.getResources().openRawResource(resourceId); - FileOutputStream os = new FileOutputStream(outFile)) { - assertThat(FileUtils.copy(is, os)).isGreaterThan(0L); - } catch (FileNotFoundException e) { - throw new AssertionError("File not found exception " + e); + try ( + InputStream in = ApexManager.class.getClassLoader() + .getResourceAsStream(fullResourceName); + OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { + if (in == null) { + throw new IllegalArgumentException("Resource not found: " + fullResourceName); + } + byte[] buf = new byte[65536]; + int chunkSize; + while ((chunkSize = in.read(buf)) != -1) { + out.write(buf, 0, chunkSize); + } + return file; } catch (IOException e) { - throw new AssertionError("IOException" + e); + throw new AssertionError("Exception while converting stream to file" + e); } - - return outFile; } } -- GitLab From 7b1be3e68d68ef40d06812a57eea5922f3eebc60 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Mon, 27 Apr 2020 15:21:07 +0800 Subject: [PATCH 070/166] Change AccessibilityServiceReported atoms to log from settings module. * Lower cost to log from accessibility settings Metrics council review bug: 153342192 Bug: 151285965 Test: build pass Change-Id: Ia1b479ff5e7ece46da734212a747739b9de3ba90 --- cmds/statsd/src/atoms.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 8d756281c431..9d8fa5f87246 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -426,7 +426,7 @@ message Atom { UserLifecycleEventOccurred user_lifecycle_event_occurred = 265 [(module) = "framework"]; AccessibilityShortcutReported accessibility_shortcut_reported = 266 [(module) = "framework"]; - AccessibilityServiceReported accessibility_service_reported = 267 [(module) = "framework"]; + AccessibilityServiceReported accessibility_service_reported = 267 [(module) = "settings"]; SdkExtensionStatus sdk_extension_status = 354; // StatsdStats tracks platform atoms with ids upto 500. @@ -9453,7 +9453,7 @@ message UserLifecycleEventOccurred { * Logs when accessibility shortcut clicked. * * Logged from: - * frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java + * frameworks/base/services/accessibility/java/com/android/server/accessibility */ message AccessibilityShortcutReported { // The accessibility feature(including installed a11y service, framework a11y feature, @@ -9473,7 +9473,7 @@ message AccessibilityShortcutReported { * Logs when accessibility service status changed. * * Logged from: - * frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java + * packages/apps/Settings/src/com/android/settings/accessibility */ message AccessibilityServiceReported { // The accessibility service package name. -- GitLab From bf87c15b337168bf94f36185cccd46b02aec41a2 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Wed, 22 Apr 2020 17:18:25 +0200 Subject: [PATCH 071/166] Decouple InsetsController from ViewRootImpl Such that all it's goodness can also be used in context when ViewRootImpl isn't available, like the SystemUI controller used for Car and Split Test: InsetsControllerTest Fixes: 154631128 Change-Id: I54a3f8a34810472d9273e4627a7811b7abd0863f --- .../android/view/ImeInsetsSourceConsumer.java | 2 +- core/java/android/view/InsetsController.java | 248 +++++++++--------- core/java/android/view/ViewRootImpl.java | 2 +- .../view/ViewRootInsetsControllerHost.java | 215 +++++++++++++++ .../view/ImeInsetsSourceConsumerTest.java | 3 +- .../android/view/InsetsControllerTest.java | 3 +- .../view/InsetsSourceConsumerTest.java | 3 +- 7 files changed, 351 insertions(+), 125 deletions(-) create mode 100644 core/java/android/view/ViewRootInsetsControllerHost.java diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index df891303bb1d..38b6c03a02f9 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -217,6 +217,6 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } private InputMethodManager getImm() { - return mController.getViewRoot().mContext.getSystemService(InputMethodManager.class); + return mController.getHost().getInputMethodManager(); } } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index f135328c44fe..887607972bbc 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -37,9 +37,7 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.CancellationSignal; import android.os.Handler; -import android.os.RemoteException; import android.util.ArraySet; -import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.view.InsetsSourceConsumer.ShowResult; @@ -53,6 +51,7 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; +import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; @@ -72,6 +71,91 @@ import java.util.function.BiFunction; */ public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks { + public interface Host { + + Handler getHandler(); + + /** + * Notifies host that {@link InsetsController#getState()} has changed. + */ + void notifyInsetsChanged(); + + void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation); + Bounds dispatchWindowInsetsAnimationStart( + @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds); + WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets, + @NonNull List runningAnimations); + void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation); + + /** + * Requests host to apply surface params in synchronized manner. + */ + void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params); + + /** + * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean) + */ + void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible, + boolean hasControl); + + /** + * Called when insets have been modified by the client and should be reported back to WM. + */ + void onInsetsModified(InsetsState insetsState); + + /** + * @return Whether the host has any callbacks it wants to synchronize the animations with. + * If there are no callbacks, the animation will be off-loaded to another thread and + * slightly different animation curves are picked. + */ + boolean hasAnimationCallbacks(); + + /** + * @see WindowInsetsController#setSystemBarsAppearance + */ + void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask); + + /** + * @see WindowInsetsController#getSystemBarsAppearance() + */ + @Appearance int getSystemBarsAppearance(); + + /** + * @see WindowInsetsController#setSystemBarsBehavior + */ + void setSystemBarsBehavior(@Behavior int behavior); + + /** + * @see WindowInsetsController#getSystemBarsBehavior + */ + @Behavior int getSystemBarsBehavior(); + + /** + * Releases a surface and ensure that this is done after {@link #applySurfaceParams} has + * finished applying params. + */ + void releaseSurfaceControlFromRt(SurfaceControl surfaceControl); + + /** + * If this host is a view hierarchy, adds a pre-draw runnable to ensure proper ordering as + * described in {@link WindowInsetsAnimation.Callback#onPrepare}. + * + * If this host isn't a view hierarchy, the runnable can be executed immediately. + */ + void addOnPreDrawRunnable(Runnable r); + + /** + * Adds a runnbale to be executed during {@link Choreographer#CALLBACK_INSETS_ANIMATION} + * phase. + */ + void postInsetsAnimationCallback(Runnable r); + + /** + * Obtains {@link InputMethodManager} instance from host. + */ + InputMethodManager getInputMethodManager(); + } + private static final int ANIMATION_DURATION_SHOW_MS = 275; private static final int ANIMATION_DURATION_HIDE_MS = 340; @@ -346,7 +430,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final Rect mFrame = new Rect(); private final BiFunction mConsumerCreator; private final SparseArray mSourceConsumers = new SparseArray<>(); - private final ViewRootImpl mViewRoot; + private final Host mHost; private final Handler mHandler; private final SparseArray mTmpControlArray = new SparseArray<>(); @@ -370,8 +454,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private boolean mStartingAnimation; private int mCaptionInsetsHeight = 0; - private SyncRtSurfaceTransactionApplier mApplier; - private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; private final ArrayList mControllableInsetsChangedListeners = new ArrayList<>(); @@ -379,22 +461,22 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** Set of inset types for which an animation was started since last resetting this field */ private @InsetsType int mLastStartedAnimTypes; - public InsetsController(ViewRootImpl viewRoot) { - this(viewRoot, (controller, type) -> { + public InsetsController(Host host) { + this(host, (controller, type) -> { if (type == ITYPE_IME) { return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller); } else { return new InsetsSourceConsumer(type, controller.mState, Transaction::new, controller); } - }, viewRoot.mHandler); + }, host.getHandler()); } @VisibleForTesting - public InsetsController(ViewRootImpl viewRoot, + public InsetsController(Host host, BiFunction consumerCreator, Handler handler) { - mViewRoot = viewRoot; + mHost = host; mConsumerCreator = consumerCreator; mHandler = handler; mAnimCallback = () -> { @@ -402,10 +484,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (mRunningAnimations.isEmpty()) { return; } - if (mViewRoot.mView == null) { - // The view has already detached from window. - return; - } mTmpFinishedControls.clear(); mTmpRunningAnims.clear(); @@ -433,8 +511,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(), mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags, null /* typeSideMap */); - mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets, - mUnmodifiableTmpRunningAnims); + mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims); for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) { dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation()); @@ -447,7 +524,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (mFrame.equals(frame)) { return; } - mViewRoot.notifyInsetsChanged(); + mHost.notifyInsetsChanged(); mFrame.set(frame); } @@ -476,7 +553,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mLastDispachedState.set(state, true /* copySources */); applyLocalVisibilityOverride(); if (localStateChanged) { - mViewRoot.notifyInsetsChanged(); + mHost.notifyInsetsChanged(); } if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) { sendStateToWindowManager(); @@ -733,7 +810,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final InsetsAnimationControlRunner runner = useInsetsAnimationThread ? new InsetsAnimationThreadControlRunner(controls, frame, mState, listener, typesReady, this, durationMs, interpolator, - animationType, mViewRoot.mHandler) + animationType, mHost.getHandler()) : new InsetsAnimationControlImpl(controls, frame, mState, listener, typesReady, this, durationMs, interpolator, animationType); @@ -860,21 +937,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { - if (mApplier == null) { - if (mViewRoot.mView == null) { - throw new IllegalStateException("View of the ViewRootImpl is not initiated."); - } - mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView); - } - if (mViewRoot.mView.isHardwareAccelerated()) { - mApplier.scheduleApply(false /* earlyWakeup */, params); - } else { - // Window doesn't support hardware acceleration, no synchronization for now. - // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every - // frame instead. - mApplier.applyParams(new Transaction(), -1 /* frame */, false /* earlyWakeup */, - params); - } + mHost.applySurfaceParams(params); } void notifyControlRevoked(InsetsSourceConsumer consumer) { @@ -900,7 +963,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation ArraySet types = toInternalType(control.getTypes()); for (int j = types.size() - 1; j >= 0; j--) { if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) { - mViewRoot.notifyInsetsChanged(); + mHost.notifyInsetsChanged(); } } break; @@ -928,7 +991,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public void notifyVisibilityChanged() { - mViewRoot.notifyInsetsChanged(); + mHost.notifyInsetsChanged(); sendStateToWindowManager(); } @@ -937,7 +1000,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation */ public void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible, boolean hasControl) { - mViewRoot.updateCompatSysUiVisibility(type, visible, hasControl); + mHost.updateCompatSysUiVisibility(type, visible, hasControl); } /** @@ -954,10 +1017,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation getSourceConsumer(ITYPE_IME).onWindowFocusLost(); } - ViewRootImpl getViewRoot() { - return mViewRoot; - } - /** * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden. * @hide @@ -994,12 +1053,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation tmpState.addSource(mState.getSource(consumer.getType())); } } - - try { - mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState); - } catch (RemoteException e) { - Log.e(TAG, "Failed to call insetsModified", e); - } + mHost.onInsetsModified(tmpState); } @VisibleForTesting @@ -1009,7 +1063,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return; } - boolean hasAnimationCallbacks = hasAnimationCallbacks(); + boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks(); final InternalAnimationControlListener listener = new InternalAnimationControlListener(show, hasAnimationCallbacks, types); @@ -1024,13 +1078,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } - private boolean hasAnimationCallbacks() { - if (mViewRoot.mView == null) { - return false; - } - return mViewRoot.mView.hasWindowInsetsAnimationCallback(); - } - private void hideDirectly( @InsetsType int types, boolean animationFinished, @AnimationType int animationType) { final ArraySet internalTypes = InsetsState.toInternalType(types); @@ -1064,37 +1111,28 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation public void startAnimation(InsetsAnimationControlImpl controller, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds) { - if (mViewRoot.mView == null) { - return; - } - mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation); - mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this); - if (controller.isCancelled()) { - return true; - } - for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { - RunningAnimation runningAnimation = mRunningAnimations.get(i); - if (runningAnimation.runner == controller) { - runningAnimation.startDispatched = true; - } + mHost.dispatchWindowInsetsAnimationPrepare(animation); + mHost.addOnPreDrawRunnable(() -> { + if (controller.isCancelled()) { + return; + } + for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { + RunningAnimation runningAnimation = mRunningAnimations.get(i); + if (runningAnimation.runner == controller) { + runningAnimation.startDispatched = true; } - mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds); - mStartingAnimation = true; - controller.mReadyDispatched = true; - listener.onReady(controller, types); - mStartingAnimation = false; - return true; } + mHost.dispatchWindowInsetsAnimationStart(animation, bounds); + mStartingAnimation = true; + controller.mReadyDispatched = true; + listener.onReady(controller, types); + mStartingAnimation = false; }); - mViewRoot.mView.invalidate(); } @VisibleForTesting public void dispatchAnimationEnd(WindowInsetsAnimation animation) { - mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation); + mHost.dispatchWindowInsetsAnimationEnd(animation); } @VisibleForTesting @@ -1106,30 +1144,19 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return; } if (!mAnimCallbackScheduled) { - mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, - mAnimCallback, null /* token*/); + mHost.postInsetsAnimationCallback(mAnimCallback); mAnimCallbackScheduled = true; } } @Override public void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask) { - mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_APPEARANCE_CONTROLLED; - final InsetsFlags insetsFlags = mViewRoot.mWindowAttributes.insetsFlags; - if (insetsFlags.appearance != appearance) { - insetsFlags.appearance = (insetsFlags.appearance & ~mask) | (appearance & mask); - mViewRoot.mWindowAttributesChanged = true; - mViewRoot.scheduleTraversals(); - } + mHost.setSystemBarsAppearance(appearance, mask); } @Override public @Appearance int getSystemBarsAppearance() { - if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) { - // We only return the requested appearance, not the implied one. - return 0; - } - return mViewRoot.mWindowAttributes.insetsFlags.appearance; + return mHost.getSystemBarsAppearance(); } @Override @@ -1139,21 +1166,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void setSystemBarsBehavior(@Behavior int behavior) { - mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED; - if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) { - mViewRoot.mWindowAttributes.insetsFlags.behavior = behavior; - mViewRoot.mWindowAttributesChanged = true; - mViewRoot.scheduleTraversals(); - } + mHost.setSystemBarsBehavior(behavior); } @Override public @Appearance int getSystemBarsBehavior() { - if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_BEHAVIOR_CONTROLLED) == 0) { - // We only return the requested behavior, not the implied one. - return 0; - } - return mViewRoot.mWindowAttributes.insetsFlags.behavior; + return mHost.getSystemBarsBehavior(); } private @InsetsType int calculateControllableTypes() { @@ -1198,22 +1216,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mControllableInsetsChangedListeners.remove(listener); } - /** - * At the time we receive new leashes (e.g. InsetsSourceConsumer is processing - * setControl) we need to release the old leash. But we may have already scheduled - * a SyncRtSurfaceTransaction applier to use it from the RenderThread. To avoid - * synchronization issues we also release from the RenderThread so this release - * happens after any existing items on the work queue. - */ + @Override public void releaseSurfaceControlFromRt(SurfaceControl sc) { - if (mViewRoot.mView != null && mViewRoot.mView.isHardwareAccelerated()) { - mViewRoot.registerRtFrameCallback(frame -> { - sc.release(); - }); - // Make sure a frame gets scheduled. - mViewRoot.mView.invalidate(); - } else { - sc.release(); - } + mHost.releaseSurfaceControlFromRt(sc); + } + + Host getHost() { + return mHost; } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8b1e6cbdb6ff..3ce31cb18375 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -757,7 +757,7 @@ public final class ViewRootImpl implements ViewParent, mChoreographer = useSfChoreographer ? Choreographer.getSfInstance() : Choreographer.getInstance(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); - mInsetsController = new InsetsController(this); + mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this)); String processorOverrideName = context.getResources().getString( R.string.config_inputEventCompatProcessorOverrideClassName); diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java new file mode 100644 index 000000000000..d8bf58f78339 --- /dev/null +++ b/core/java/android/view/ViewRootInsetsControllerHost.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; + +import android.annotation.NonNull; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Log; +import android.view.inputmethod.InputMethodManager; + +import java.util.List; + +/** + * Implements {@link InsetsController.Host} for {@link ViewRootImpl}s. + * @hide + */ +public class ViewRootInsetsControllerHost implements InsetsController.Host { + + private final String TAG = "VRInsetsControllerHost"; + + private final ViewRootImpl mViewRoot; + private SyncRtSurfaceTransactionApplier mApplier; + + public ViewRootInsetsControllerHost(ViewRootImpl viewRoot) { + mViewRoot = viewRoot; + } + + @Override + public Handler getHandler() { + return mViewRoot.mHandler; + } + + @Override + public void notifyInsetsChanged() { + mViewRoot.notifyInsetsChanged(); + } + + @Override + public void addOnPreDrawRunnable(Runnable r) { + if (mViewRoot.mView == null) { + return; + } + mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this); + r.run(); + return true; + } + }); + mViewRoot.mView.invalidate(); + } + + @Override + public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) { + if (mViewRoot.mView == null) { + return; + } + mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation); + } + + @Override + public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart( + @NonNull WindowInsetsAnimation animation, + @NonNull WindowInsetsAnimation.Bounds bounds) { + if (mViewRoot.mView == null) { + return null; + } + return mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds); + } + + @Override + public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets, + @NonNull List runningAnimations) { + if (mViewRoot.mView == null) { + // The view has already detached from window. + return null; + } + return mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets, runningAnimations); + } + + @Override + public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) { + mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation); + } + + @Override + public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params) { + if (mApplier == null) { + if (mViewRoot.mView == null) { + throw new IllegalStateException("View of the ViewRootImpl is not initiated."); + } + mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView); + } + if (mViewRoot.mView.isHardwareAccelerated()) { + mApplier.scheduleApply(false /* earlyWakeup */, params); + } else { + // Window doesn't support hardware acceleration, no synchronization for now. + // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every + // frame instead. + mApplier.applyParams(new SurfaceControl.Transaction(), -1 /* frame */, + false /* earlyWakeup */, params); + } + } + + @Override + public void postInsetsAnimationCallback(Runnable r) { + mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, r, + null /* token */); + } + + @Override + public void updateCompatSysUiVisibility(int type, boolean visible, boolean hasControl) { + mViewRoot.updateCompatSysUiVisibility(type, visible, hasControl); + } + + @Override + public void onInsetsModified(InsetsState insetsState) { + try { + mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState); + } catch (RemoteException e) { + Log.e(TAG, "Failed to call insetsModified", e); + } + } + + @Override + public boolean hasAnimationCallbacks() { + if (mViewRoot.mView == null) { + return false; + } + return mViewRoot.mView.hasWindowInsetsAnimationCallback(); + } + + @Override + public void setSystemBarsAppearance(int appearance, int mask) { + mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_APPEARANCE_CONTROLLED; + final InsetsFlags insetsFlags = mViewRoot.mWindowAttributes.insetsFlags; + if (insetsFlags.appearance != appearance) { + insetsFlags.appearance = (insetsFlags.appearance & ~mask) | (appearance & mask); + mViewRoot.mWindowAttributesChanged = true; + mViewRoot.scheduleTraversals(); + } + } + + @Override + public int getSystemBarsAppearance() { + if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) { + // We only return the requested appearance, not the implied one. + return 0; + } + return mViewRoot.mWindowAttributes.insetsFlags.appearance; + } + + @Override + public void setSystemBarsBehavior(int behavior) { + mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED; + if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) { + mViewRoot.mWindowAttributes.insetsFlags.behavior = behavior; + mViewRoot.mWindowAttributesChanged = true; + mViewRoot.scheduleTraversals(); + } + } + + @Override + public int getSystemBarsBehavior() { + if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_BEHAVIOR_CONTROLLED) == 0) { + // We only return the requested behavior, not the implied one. + return 0; + } + return mViewRoot.mWindowAttributes.insetsFlags.behavior; + } + + @Override + public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) { + + // At the time we receive new leashes (e.g. InsetsSourceConsumer is processing + // setControl) we need to release the old leash. But we may have already scheduled + // a SyncRtSurfaceTransaction applier to use it from the RenderThread. To avoid + // synchronization issues we also release from the RenderThread so this release + // happens after any existing items on the work queue. + + if (mViewRoot.mView != null && mViewRoot.mView.isHardwareAccelerated()) { + mViewRoot.registerRtFrameCallback(frame -> { + surfaceControl.release(); + }); + // Make sure a frame gets scheduled. + mViewRoot.mView.invalidate(); + } else { + surfaceControl.release(); + } + } + + @Override + public InputMethodManager getInputMethodManager() { + return mViewRoot.mContext.getSystemService(InputMethodManager.class); + } +} diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index 5c9e3397c6a5..164c372768c0 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -78,7 +78,8 @@ public class ImeInsetsSourceConsumerTest { } catch (BadTokenException e) { // activity isn't running, we will ignore BadTokenException. } - mController = Mockito.spy(new InsetsController(viewRootImpl)); + mController = Mockito.spy(new InsetsController( + new ViewRootInsetsControllerHost(viewRootImpl))); final Rect rect = new Rect(5, 5, 5, 5); mController.calculateInsets( false, diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index ade31d8e4173..ccf4192ac2f4 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -123,7 +123,8 @@ public class InsetsControllerTest { } mTestClock = new OffsettableClock(); mTestHandler = new TestHandler(null, mTestClock); - mController = new InsetsController(mViewRoot, (controller, type) -> { + mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot), + (controller, type) -> { if (type == ITYPE_IME) { return new InsetsSourceConsumer(type, controller.getState(), Transaction::new, controller) { diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index cc93f9a5b59c..25f94131f820 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -89,7 +89,8 @@ public class InsetsSourceConsumerTest { state.addSource(mSpyInsetsSource); mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, state, - () -> mMockTransaction, new InsetsController(viewRootImpl)); + () -> mMockTransaction, + new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl))); }); instrumentation.waitForIdleSync(); -- GitLab From 1d7a50cea800d667ab886b3ea3e8aef9923ba59c Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Tue, 28 Apr 2020 13:51:40 -0400 Subject: [PATCH 072/166] Add latching threshold for ThresholdSensorImpl This allows ThresholdSensorImpl to account for noise in their sensor output. They must go below one threshold to trigger a "below" output, and then must go above a different threshold to switch back to an "above" state. This new second, latching threshold is optional and defaults to the existing threshold if not set. For the ProximitySensor, we introduce proximity_sensor_threshold_latch and proximity_sensor_secondary_threshold_latch in config.xml. Fixes: 147026387 Test: atest SystemUITests && manual Change-Id: Ie0b7b357a48fd27424d12b890cda9e00073e1eb3 --- packages/SystemUI/res/values/config.xml | 14 ++++- .../systemui/util/sensors/SensorModule.java | 2 + .../util/sensors/ThresholdSensorImpl.java | 61 ++++++++++++++++--- .../util/sensors/ThresholdSensorImplTest.java | 60 ++++++++++++++++-- 4 files changed, 124 insertions(+), 13 deletions(-) diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index e60b54305956..e32fe4da2bf6 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -205,14 +205,26 @@ far break points. A sensor value less than this is considered "near". --> + + + - + + + 130 diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java index 5a93e6c9f77a..c5a658f298dc 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java @@ -38,6 +38,7 @@ public class SensorModule { .setSensorDelay(SensorManager.SENSOR_DELAY_NORMAL) .setSensorResourceId(R.string.proximity_sensor_type) .setThresholdResourceId(R.dimen.proximity_sensor_threshold) + .setThresholdLatchResourceId(R.dimen.proximity_sensor_threshold_latch) .build(); } catch (IllegalStateException e) { Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); @@ -56,6 +57,7 @@ public class SensorModule { return thresholdSensorBuilder .setSensorResourceId(R.string.proximity_sensor_secondary_type) .setThresholdResourceId(R.dimen.proximity_sensor_secondary_threshold) + .setThresholdLatchResourceId(R.dimen.proximity_sensor_secondary_threshold_latch) .build(); } catch (IllegalStateException e) { return thresholdSensorBuilder.setSensor(null).setThresholdValue(0).build(); diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java index 333e8da2a84f..5bedea173f19 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java @@ -44,14 +44,16 @@ class ThresholdSensorImpl implements ThresholdSensor { private List mListeners = new ArrayList<>(); private Boolean mLastBelow; private String mTag; + private final float mThresholdLatch; private int mSensorDelay; private SensorEventListener mSensorEventListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { boolean below = event.values[0] < mThreshold; + boolean above = event.values[0] > mThresholdLatch; logDebug("Sensor value: " + event.values[0]); - onSensorEvent(below, event.timestamp); + onSensorEvent(below, above, event.timestamp); } @Override @@ -60,10 +62,11 @@ class ThresholdSensorImpl implements ThresholdSensor { }; private ThresholdSensorImpl(AsyncSensorManager sensorManager, - Sensor sensor, float threshold, int sensorDelay) { + Sensor sensor, float threshold, float thresholdLatch, int sensorDelay) { mSensorManager = sensorManager; mSensor = sensor; mThreshold = threshold; + mThresholdLatch = thresholdLatch; mSensorDelay = sensorDelay; } @@ -165,13 +168,32 @@ class ThresholdSensorImpl implements ThresholdSensor { mLastBelow = null; // Forget what we know. } - private void onSensorEvent(boolean below, long timestampNs) { + /** + * Call when the sensor reports a new value. + * + * Separate below-threshold and above-thresholds are specified. this allows latching behavior, + * where a different threshold can be specified for triggering the sensor depending on if it's + * going from above to below or below to above. To outside listeners of this class, the class + * still appears entirely binary. + */ + private void onSensorEvent(boolean belowThreshold, boolean aboveThreshold, long timestampNs) { Assert.isMainThread(); - if (!mRegistered || mLastBelow != null && mLastBelow == below) { + if (!mRegistered) { return; } - mLastBelow = below; - alertListenersInternal(below, timestampNs); + if (mLastBelow != null) { + // If we last reported below and are not yet above, change nothing. + if (mLastBelow && !aboveThreshold) { + return; + } + // If we last reported above and are not yet below, change nothing. + if (!mLastBelow && !belowThreshold) { + return; + } + } + mLastBelow = belowThreshold; + logDebug("Alerting below: " + belowThreshold); + alertListenersInternal(belowThreshold, timestampNs); } @@ -192,9 +214,11 @@ class ThresholdSensorImpl implements ThresholdSensor { private final AsyncSensorManager mSensorManager; private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;; private float mThresholdValue; + private float mThresholdLatchValue; private Sensor mSensor; private boolean mSensorSet; private boolean mThresholdSet; + private boolean mThresholdLatchValueSet; @Inject Builder(@Main Resources resources, AsyncSensorManager sensorManager) { @@ -222,6 +246,15 @@ class ThresholdSensorImpl implements ThresholdSensor { return this; } + Builder setThresholdLatchResourceId(int thresholdLatchResourceId) { + try { + setThresholdLatchValue(mResources.getFloat(thresholdLatchResourceId)); + } catch (Resources.NotFoundException e) { + // no-op + } + return this; + } + Builder setSensorType(String sensorType) { Sensor sensor = findSensorByType(sensorType); if (sensor != null) { @@ -233,6 +266,15 @@ class ThresholdSensorImpl implements ThresholdSensor { Builder setThresholdValue(float thresholdValue) { mThresholdValue = thresholdValue; mThresholdSet = true; + if (!mThresholdLatchValueSet) { + mThresholdLatchValue = mThresholdValue; + } + return this; + } + + Builder setThresholdLatchValue(float thresholdLatchValue) { + mThresholdLatchValue = thresholdLatchValue; + mThresholdLatchValueSet = true; return this; } @@ -254,8 +296,13 @@ class ThresholdSensorImpl implements ThresholdSensor { throw new IllegalStateException("A threshold was not successfully set."); } + if (mThresholdValue > mThresholdLatchValue) { + throw new IllegalStateException( + "Threshold must be less than or equal to Threshold Latch"); + } + return new ThresholdSensorImpl( - mSensorManager, mSensor, mThresholdValue, mSensorDelay); + mSensorManager, mSensor, mThresholdValue, mThresholdLatchValue, mSensorDelay); } private Sensor findSensorByType(String sensorType) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java index 70b92dc01447..0d36bd30c8a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java @@ -37,19 +37,21 @@ import org.junit.runner.RunWith; public class ThresholdSensorImplTest extends SysuiTestCase { private ThresholdSensorImpl mThresholdSensor; + private FakeSensorManager mSensorManager; + private AsyncSensorManager mAsyncSensorManager; private FakeSensorManager.FakeProximitySensor mFakeProximitySensor; @Before public void setUp() throws Exception { allowTestableLooperAsMainThread(); - FakeSensorManager sensorManager = new FakeSensorManager(getContext()); + mSensorManager = new FakeSensorManager(getContext()); - AsyncSensorManager asyncSensorManager = new AsyncSensorManager( - sensorManager, null, new Handler()); + mAsyncSensorManager = new AsyncSensorManager( + mSensorManager, null, new Handler()); - mFakeProximitySensor = sensorManager.getFakeProximitySensor(); + mFakeProximitySensor = mSensorManager.getFakeProximitySensor(); ThresholdSensorImpl.Builder thresholdSensorBuilder = new ThresholdSensorImpl.Builder( - null, asyncSensorManager); + null, mAsyncSensorManager); mThresholdSensor = (ThresholdSensorImpl) thresholdSensorBuilder .setSensor(mFakeProximitySensor.getSensor()) .setThresholdValue(mFakeProximitySensor.getSensor().getMaximumRange()) @@ -226,6 +228,54 @@ public class ThresholdSensorImplTest extends SysuiTestCase { waitForSensorManager(); } + @Test + public void testHysteresis() { + float lowValue = 10f; + float highValue = 100f; + FakeSensorManager.FakeGenericSensor sensor = mSensorManager.getFakeLightSensor(); + ThresholdSensorImpl.Builder thresholdSensorBuilder = new ThresholdSensorImpl.Builder( + null, mAsyncSensorManager); + ThresholdSensorImpl thresholdSensor = (ThresholdSensorImpl) thresholdSensorBuilder + .setSensor(sensor.getSensor()) + .setThresholdValue(lowValue) + .setThresholdLatchValue(highValue) + .build(); + + TestableListener listener = new TestableListener(); + + assertFalse(thresholdSensor.isRegistered()); + thresholdSensor.register(listener); + waitForSensorManager(); + assertTrue(thresholdSensor.isRegistered()); + assertEquals(0, listener.mCallCount); + + sensor.sendSensorEvent(lowValue - 1); + + assertTrue(listener.mBelow); + assertEquals(1, listener.mCallCount); + + sensor.sendSensorEvent(lowValue + 1); + + assertTrue(listener.mBelow); + assertEquals(1, listener.mCallCount); + + sensor.sendSensorEvent(highValue + 1); + + assertFalse(listener.mBelow); + assertEquals(2, listener.mCallCount); + + sensor.sendSensorEvent(highValue - 1); + + assertFalse(listener.mBelow); + assertEquals(2, listener.mCallCount); + + + sensor.sendSensorEvent(lowValue - 1); + + assertTrue(listener.mBelow); + assertEquals(3, listener.mCallCount); + } + @Test public void testAlertAfterPause() { TestableListener listener = new TestableListener(); -- GitLab From ccd18a9d5810611a334cbf047a0e9c0d1df6f6b5 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Mon, 27 Apr 2020 11:59:26 -0400 Subject: [PATCH 073/166] Remove unused development setting Test: make, atest Bug: 151821676 Change-Id: I74c2b7ac17d99f150c1c185f4b69b3b2e72d2575 --- core/java/android/provider/Settings.java | 10 ---------- packages/SettingsLib/res/values/strings.xml | 5 ----- .../test/src/android/provider/SettingsBackupTest.java | 1 - 3 files changed, 16 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index fbd6cbae47d9..20bc792d2c77 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13829,16 +13829,6 @@ public final class Settings { public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS = "show_notification_channel_warnings"; - /** - * When enabled, requires all notifications in the conversation section to be backed - * by a long-lived sharing shortcut - * - * The value 1 - require a shortcut, 0 - do not require a shortcut - * @hide - */ - public static final String REQUIRE_SHORTCUTS_FOR_CONVERSATIONS = - "require_shortcuts_for_conversations"; - /** * Whether cell is enabled/disabled * @hide diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 7baaf494771b..7587cc1338f4 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -937,11 +937,6 @@ Displays on-screen warning when an app posts a notification without a valid channel - - Enforce shortcuts for conversation notifications - - Require notifications to be backed by a long-lived sharing shortcut in order to appear in the conversation section - Force allow apps on external diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 0dd7fb80379d..f5589d713bf7 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -420,7 +420,6 @@ public class SettingsBackupTest { Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS, Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT, Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, Settings.Global.SAFE_BOOT_DISALLOWED, Settings.Global.SELINUX_STATUS, Settings.Global.SELINUX_UPDATE_CONTENT_URL, -- GitLab From 7794a00407e6634fbfa09a5e8d36908440ca7c66 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Wed, 29 Apr 2020 17:13:57 +0200 Subject: [PATCH 074/166] WindowInsetsController: make available after addView Fixes an issue where the WindowInsetsController was only available after the first traversal. Bug: 155280356 Test: atest WindowInsetsControllerTests Change-Id: I225efcaa31e97da9e8ebc6bd7d24c433c8e80826 --- core/java/android/view/View.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f98c1f660cfa..310299910e96 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11519,6 +11519,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, ViewParent parent = getParent(); if (parent instanceof View) { return ((View) parent).getWindowInsetsController(); + } else if (parent instanceof ViewRootImpl) { + // Between WindowManager.addView() and the first traversal AttachInfo isn't set yet. + return ((ViewRootImpl) parent).getInsetsController(); } return null; } -- GitLab From b99b582f490139187ef1d1acde4dcec611c3b8d9 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Tue, 28 Apr 2020 12:53:38 -0700 Subject: [PATCH 075/166] Fix QC alarm spamming. Apps could reach their job count limit during an active session. When this happened, QC would correctly identify that the app was out of quota, but it wasn't correctly calculating the in quota time, so it was trying to set a time at 0 (or whatever the last in quota time had been). This caused a loop where AlarmManager would fire the alarm, QC would see the app was still out of quota, and set a new alarm for the same old time. This cycle was more common for apps in the RESTRICTED bucket that had a much lower job limit. Now we correctly calculate the in quota time and avoid the spammy loop. Bug: 154444435 Bug: 155245660 Test: atest CountQuotaTrackerTest Test: atest CtsJobSchedulerTestCases Test: atest QuotaControllerTest Change-Id: If07a6133b3ccf8201ac2fd86622bd8e1686f1b0a --- .../job/controllers/QuotaController.java | 36 +++++++--- .../server/utils/quota/CountQuotaTracker.java | 7 +- .../job/controllers/QuotaControllerTest.java | 71 +++++++++++++++++++ 3 files changed, 105 insertions(+), 9 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 8c55b50957da..d108f0b698f7 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -241,10 +241,11 @@ public final class QuotaController extends StateController { + "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", " + "sessionCountInWindow=" + sessionCountInWindow + ", " + "inQuotaTime=" + inQuotaTimeElapsed + ", " - + "jobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", " - + "jobCountInRateLimitingWindow=" + jobCountInRateLimitingWindow + ", " - + "sessionCountExpirationTime=" + sessionRateLimitExpirationTimeElapsed + ", " - + "sessionCountInRateLimitingWindow=" + sessionCountInRateLimitingWindow; + + "rateLimitJobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", " + + "rateLimitJobCountWindow=" + jobCountInRateLimitingWindow + ", " + + "rateLimitSessionCountExpirationTime=" + + sessionRateLimitExpirationTimeElapsed + ", " + + "rateLimitSessionCountWindow=" + sessionCountInRateLimitingWindow; } @Override @@ -863,12 +864,19 @@ public final class QuotaController extends StateController { stats.executionTimeInMaxPeriodMs = 0; stats.bgJobCountInMaxPeriod = 0; stats.sessionCountInWindow = 0; - stats.inQuotaTimeElapsed = 0; + if (stats.jobCountLimit == 0 || stats.sessionCountLimit == 0) { + // App won't be in quota until configuration changes. + stats.inQuotaTimeElapsed = Long.MAX_VALUE; + } else { + stats.inQuotaTimeElapsed = 0; + } Timer timer = mPkgTimers.get(userId, packageName); final long nowElapsed = sElapsedRealtimeClock.millis(); stats.expirationTimeElapsed = nowElapsed + MAX_PERIOD_MS; if (timer != null && timer.isActive()) { + // Exclude active sessions from the session count so that new jobs aren't prevented + // from starting due to an app hitting the session limit. stats.executionTimeInWindowMs = stats.executionTimeInMaxPeriodMs = timer.getCurrentDuration(nowElapsed); stats.bgJobCountInWindow = stats.bgJobCountInMaxPeriod = timer.getBgJobCount(); @@ -883,6 +891,10 @@ public final class QuotaController extends StateController { stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS); } + if (stats.bgJobCountInWindow >= stats.jobCountLimit) { + stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, + nowElapsed + stats.windowSizeMs); + } } List sessions = mTimingSessions.get(userId, packageName); @@ -1303,6 +1315,13 @@ public final class QuotaController extends StateController { inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed, stats.sessionRateLimitExpirationTimeElapsed); } + if (inQuotaTimeElapsed <= sElapsedRealtimeClock.millis()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + Slog.wtf(TAG, + "In quota time is " + (nowElapsed - inQuotaTimeElapsed) + "ms old. Now=" + + nowElapsed + ", inQuotaTime=" + inQuotaTimeElapsed + ": " + stats); + inQuotaTimeElapsed = nowElapsed + 5 * MINUTE_IN_MILLIS; + } mInQuotaAlarmListener.addAlarmLocked(userId, packageName, inQuotaTimeElapsed); } @@ -1916,8 +1935,8 @@ public final class QuotaController extends StateController { @GuardedBy("mLock") private void setNextAlarmLocked(long earliestTriggerElapsed) { if (mAlarmQueue.size() > 0) { - final long nextTriggerTimeElapsed = Math.max(earliestTriggerElapsed, - mAlarmQueue.peek().second); + final Pair alarm = mAlarmQueue.peek(); + final long nextTriggerTimeElapsed = Math.max(earliestTriggerElapsed, alarm.second); // Only schedule the alarm if one of the following is true: // 1. There isn't one currently scheduled // 2. The new alarm is significantly earlier than the previous alarm. If it's @@ -1928,7 +1947,8 @@ public final class QuotaController extends StateController { || nextTriggerTimeElapsed < mTriggerTimeElapsed - 3 * MINUTE_IN_MILLIS || mTriggerTimeElapsed < nextTriggerTimeElapsed) { if (DEBUG) { - Slog.d(TAG, "Scheduling start alarm at " + nextTriggerTimeElapsed); + Slog.d(TAG, "Scheduling start alarm at " + nextTriggerTimeElapsed + + " for app " + alarm.first); } mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextTriggerTimeElapsed, ALARM_TAG_QUOTA_CHECK, this, mHandler); diff --git a/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java index 7fe4bf849443..b77df2d18859 100644 --- a/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java +++ b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java @@ -408,7 +408,12 @@ public class CountQuotaTracker extends QuotaTracker { void updateExecutionStatsLocked(final int userId, @NonNull final String packageName, @Nullable final String tag, @NonNull ExecutionStats stats) { stats.countInWindow = 0; - stats.inQuotaTimeElapsed = 0; + if (stats.countLimit == 0) { + // UPTC won't be in quota until configuration changes. + stats.inQuotaTimeElapsed = Long.MAX_VALUE; + } else { + stats.inQuotaTimeElapsed = 0; + } // This can be used to determine when an app will have enough quota to transition from // out-of-quota to in-quota. diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index bc0b184cd359..c8ec7b5503d1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -620,6 +620,77 @@ public class QuotaControllerTest { assertEquals(expectedStats, inputStats); } + @Test + public void testUpdateExecutionStatsLocked_WithTimer() { + final long now = sElapsedRealtimeClock.millis(); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + + ExecutionStats expectedStats = new ExecutionStats(); + ExecutionStats inputStats = new ExecutionStats(); + inputStats.windowSizeMs = expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS; + inputStats.jobCountLimit = expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; + inputStats.sessionCountLimit = expectedStats.sessionCountLimit = + mQcConstants.MAX_SESSION_COUNT_RARE; + // Active timer isn't counted as session yet. + expectedStats.sessionCountInWindow = 0; + // Timer only, under quota. + for (int i = 1; i < mQcConstants.MAX_JOB_COUNT_RARE; ++i) { + JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", i); + setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(7000); + + expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis(); + expectedStats.executionTimeInWindowMs = expectedStats.executionTimeInMaxPeriodMs = + 7000 * i; + expectedStats.bgJobCountInWindow = expectedStats.bgJobCountInMaxPeriod = i; + mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); + assertEquals(expectedStats, inputStats); + assertTrue(mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, + RARE_INDEX)); + } + + // Add old session. Make sure values are combined correctly. + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS), + 10 * MINUTE_IN_MILLIS, 5)); + expectedStats.sessionCountInWindow = 1; + + expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS; + expectedStats.executionTimeInWindowMs += 10 * MINUTE_IN_MILLIS; + expectedStats.executionTimeInMaxPeriodMs += 10 * MINUTE_IN_MILLIS; + expectedStats.bgJobCountInWindow += 5; + expectedStats.bgJobCountInMaxPeriod += 5; + // Active timer is under quota, so out of quota due to old session. + expectedStats.inQuotaTimeElapsed = + sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS; + mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); + assertEquals(expectedStats, inputStats); + assertFalse( + mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); + + // Quota should be exceeded due to activity in active timer. + JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", 0); + setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window + mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); + mQuotaController.prepareForExecutionLocked(jobStatus); + advanceElapsedClock(10000); + + expectedStats.executionTimeInWindowMs += 10000; + expectedStats.executionTimeInMaxPeriodMs += 10000; + expectedStats.bgJobCountInWindow++; + expectedStats.bgJobCountInMaxPeriod++; + // Out of quota due to activity in active timer, so in quota time should be when enough + // time has passed since active timer. + expectedStats.inQuotaTimeElapsed = + sElapsedRealtimeClock.millis() + expectedStats.windowSizeMs; + mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats); + assertEquals(expectedStats, inputStats); + assertFalse( + mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX)); + } + /** * Tests that getExecutionStatsLocked returns the correct stats. */ -- GitLab From f2aeed46da2be41262036a2b7ab26150d4e1b5a8 Mon Sep 17 00:00:00 2001 From: Ben Lin Date: Thu, 17 Oct 2019 15:53:07 -0700 Subject: [PATCH 076/166] Check for PIPable activities after moving task to back. When we move a task to the back, there is a possibility that the previous stack won't be put into a paused state if it's in a multi-resume environment (e.g. freeform windows). If that's the case and the activity is eligible for PIP, we pause the task so that PIP mode can be triggered via onUserLeaveHint. We pause the task instead of using ATM#requestPictureInPictureMode because the latter is not able to handle the two scenarios we want to support on minimize: 1. If the app wants to enter PIP -> enter PIP 2. If the app doesn't want to -> put the app to stopped Using requestPIPMode works for 1. but won't put the app to stopped when the app decides not to enter PIP. Pausing the task explicitely does handle both cases correctly, as it triggers onUserLeaveHint and only puts the app to stopped if the app did not enter PIP mode. Also, don't call on resumeFocusedStacksTopActivities when in a multi-resume environment. There is nothing to resume, and the focused stack is already updated when moving the task back. Bug: 142680249 Bug: 141156846 Test: In an environment with freeform window, try: 1) Minimizing a PIPable activity - sees it enters PIP 2) With two windows in freeform, try switching focus and see they perform as expected 3) Minimizing an activity that supports PIP but doesn't want to enter PIP on onUserLeaveHint - it gets STOPPED Change-Id: I59811a56a954d32458d741b8f44ccd37f5729094 --- .../java/com/android/server/wm/ActivityRecord.java | 13 ++++++++++--- .../java/com/android/server/wm/ActivityStack.java | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e79b804f76f9..4e7e5ef038c0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4607,7 +4607,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // the current contract for "auto-Pip" is that the app should enter it before onPause // returns. Just need to confirm this reasoning makes sense. final boolean deferHidingClient = canEnterPictureInPicture - && !isState(STOPPING, STOPPED, PAUSED); + && !isState(STARTED, STOPPING, STOPPED, PAUSED); setDeferHidingClient(deferHidingClient); setVisibility(false); @@ -4618,9 +4618,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // activity is hidden supportsEnterPipOnTaskSwitch = false; break; - - case INITIALIZING: case RESUMED: + // If the app is capable of entering PIP, we should try pausing it now + // so it can PIP correctly. + if (deferHidingClient) { + getRootTask().startPausingLocked( + mStackSupervisor.mUserLeaving /* userLeaving */, + false /* uiSleeping */, null /* resuming */); + break; + } + case INITIALIZING: case PAUSING: case PAUSED: case STARTED: diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 8bf46bc7c2e8..fcadf66e870e 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2951,9 +2951,9 @@ class ActivityStack extends Task { mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */, false /* deferResume */); + } else { + mRootWindowContainer.resumeFocusedStacksTopActivities(); } - - mRootWindowContainer.resumeFocusedStacksTopActivities(); return true; } -- GitLab From 6c36583ae1a228717dd275a9fffa2b619a139cec Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Wed, 29 Apr 2020 09:23:36 -0700 Subject: [PATCH 077/166] Add check for cross user permission Fixes: 153995991 Fixes: 153996872 Fixes: 153996866 Test: Manual. Device boots Change-Id: I0e32631b21725265e6dd638ea526879a351abb00 --- .../server/pm/PackageManagerService.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8b191dda5590..05cc4d49b154 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4420,6 +4420,11 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } + if (!mUserManager.exists(userId)) { + throw new SecurityException("User doesn't exist"); + } + mPermissionManager.enforceCrossUserPermission( + callingUid, userId, false, false, "checkPackageStartable"); final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId); synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); @@ -5777,9 +5782,15 @@ public class PackageManagerService extends IPackageManager.Stub @Override public ChangedPackages getChangedPackages(int sequenceNumber, int userId) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { + final int callingUid = Binder.getCallingUid(); + if (getInstantAppPackageName(callingUid) != null) { return null; } + if (!mUserManager.exists(userId)) { + return null; + } + mPermissionManager.enforceCrossUserPermission( + callingUid, userId, false, false, "getChangedPackages"); synchronized (mLock) { if (sequenceNumber >= mChangedPackagesSequenceNumber) { return null; @@ -8772,8 +8783,10 @@ public class PackageManagerService extends IPackageManager.Stub private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!mUserManager.exists(userId)) return null; - flags = updateFlagsForComponent(flags, userId); final int callingUid = Binder.getCallingUid(); + mPermissionManager.enforceCrossUserPermission( + callingUid, userId, false, false, "resolveContentProvider"); + flags = updateFlagsForComponent(flags, userId); final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId); if (providerInfo == null) { return null; -- GitLab From 9d1eeeebc58501ab9c057fbea1460c86127d7311 Mon Sep 17 00:00:00 2001 From: Gavin Corkery Date: Mon, 27 Apr 2020 11:20:42 +0100 Subject: [PATCH 078/166] Attach logging package to staged rollback IDs To ensure consistency in pre-reboot and post-reboot metrics, associate the logging package name of a rolled back train with the staged rollback id. This uses the assumption that there is at most one logging parent (train version package) associated with the rollback. This means that there will be only one ROLLBACK_SUCCESS or ROLLBACK_FAILURE log for each event. This change changes the format of the last-staged-rollback-ids file. The file now has one line for each rollback id, rather than one line containing all ids. Test: Manual test of logging flow for a train that is configured correctly, and configured incorrectly Test: atest RollbackUnitTest Bug: 154911735 Change-Id: Ie942fdb9313137c2dc6e696840262dd457558c6d Merged-In: Ie942fdb9313137c2dc6e696840262dd457558c6d --- .../RollbackPackageHealthObserver.java | 68 +++++++++++-------- .../rollback/WatchdogRollbackLogger.java | 50 +++++--------- .../server/rollback/RollbackUnitTest.java | 21 ++++++ 3 files changed, 78 insertions(+), 61 deletions(-) diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index f6d46e24246c..3601f1d94474 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -36,9 +36,9 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; import android.os.SystemProperties; -import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FrameworkStatsLog; @@ -56,9 +56,7 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Set; @@ -156,12 +154,11 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes(); } - List rollbackIds = popLastStagedRollbackIds(); - Iterator rollbackIterator = rollbackIds.iterator(); - while (rollbackIterator.hasNext()) { - int rollbackId = rollbackIterator.next(); - WatchdogRollbackLogger.logRollbackStatusOnBoot( - mContext, rollbackId, rollbackManager.getRecentlyCommittedRollbacks()); + SparseArray rollbackIds = popLastStagedRollbackIds(); + for (int i = 0; i < rollbackIds.size(); i++) { + WatchdogRollbackLogger.logRollbackStatusOnBoot(mContext, + rollbackIds.keyAt(i), rollbackIds.valueAt(i), + rollbackManager.getRecentlyCommittedRollbacks()); } } @@ -224,7 +221,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve packageInstaller.getSessionInfo(sessionId); if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) { mContext.unregisterReceiver(listener); - saveStagedRollbackId(rollbackId); + saveStagedRollbackId(rollbackId, logPackage); WatchdogRollbackLogger.logEvent(logPackage, FrameworkStatsLog .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED, @@ -268,39 +265,54 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } } - private void saveStagedRollbackId(int stagedRollbackId) { + private void saveStagedRollbackId(int stagedRollbackId, @Nullable VersionedPackage logPackage) { + writeStagedRollbackId(mLastStagedRollbackIdsFile, stagedRollbackId, logPackage); + } + + static void writeStagedRollbackId(File file, int stagedRollbackId, + @Nullable VersionedPackage logPackage) { try { - FileOutputStream fos = new FileOutputStream( - mLastStagedRollbackIdsFile, /*append*/true); + FileOutputStream fos = new FileOutputStream(file, true); PrintWriter pw = new PrintWriter(fos); - pw.append(",").append(String.valueOf(stagedRollbackId)); + String logPackageName = logPackage != null ? logPackage.getPackageName() : ""; + pw.append(String.valueOf(stagedRollbackId)).append(",").append(logPackageName); + pw.println(); pw.flush(); FileUtils.sync(fos); pw.close(); } catch (IOException e) { Slog.e(TAG, "Failed to save last staged rollback id", e); + file.delete(); + } + } + + private SparseArray popLastStagedRollbackIds() { + try { + return readStagedRollbackIds(mLastStagedRollbackIdsFile); + } finally { mLastStagedRollbackIdsFile.delete(); } } - private List popLastStagedRollbackIds() { - try (BufferedReader reader = - new BufferedReader(new FileReader(mLastStagedRollbackIdsFile))) { - String line = reader.readLine(); - // line is of format : ",id1,id2,id3....,idn" - String[] sessionIdsStr = line.split(","); - ArrayList result = new ArrayList<>(); - for (String sessionIdStr: sessionIdsStr) { - if (!TextUtils.isEmpty(sessionIdStr.trim())) { - result.add(Integer.parseInt(sessionIdStr)); + static SparseArray readStagedRollbackIds(File file) { + SparseArray result = new SparseArray<>(); + try { + String line; + BufferedReader reader = new BufferedReader(new FileReader(file)); + while ((line = reader.readLine()) != null) { + // Each line is of the format: "id,logging_package" + String[] values = line.trim().split(","); + String rollbackId = values[0]; + String logPackageName = ""; + if (values.length > 1) { + logPackageName = values[1]; } + result.put(Integer.parseInt(rollbackId), logPackageName); } - return result; } catch (Exception ignore) { - return Collections.emptyList(); - } finally { - mLastStagedRollbackIdsFile.delete(); + return new SparseArray<>(); } + return result; } diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java index 659de0093ead..e72c185a0d2d 100644 --- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java +++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java @@ -36,6 +36,7 @@ import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; @@ -43,7 +44,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.server.PackageWatchdog; -import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -115,7 +115,7 @@ public final class WatchdogRollbackLogger { } - static void logRollbackStatusOnBoot(Context context, int rollbackId, + static void logRollbackStatusOnBoot(Context context, int rollbackId, String logPackageName, List recentlyCommittedRollbacks) { PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); @@ -132,23 +132,15 @@ public final class WatchdogRollbackLogger { return; } - // Identify the logging parent for this rollback. When all configurations are correct, - // each package in the rollback has a logging parent set in metadata. - final Set loggingPackageNames = new ArraySet<>(); - for (PackageRollbackInfo packageRollback : rollback.getPackages()) { - final String loggingParentName = getLoggingParentName(context, - packageRollback.getPackageName()); - if (loggingParentName != null) { - loggingPackageNames.add(loggingParentName); - } - } - // Use the version of the logging parent that was installed before // we rolled back for logging purposes. - final List oldLoggingPackages = new ArrayList<>(); - for (PackageRollbackInfo packageRollback : rollback.getPackages()) { - if (loggingPackageNames.contains(packageRollback.getPackageName())) { - oldLoggingPackages.add(packageRollback.getVersionRolledBackFrom()); + VersionedPackage oldLoggingPackage = null; + if (!TextUtils.isEmpty(logPackageName)) { + for (PackageRollbackInfo packageRollback : rollback.getPackages()) { + if (logPackageName.equals(packageRollback.getPackageName())) { + oldLoggingPackage = packageRollback.getVersionRolledBackFrom(); + break; + } } } @@ -159,22 +151,14 @@ public final class WatchdogRollbackLogger { return; } - // If no logging packages are found, use a null package to ensure the rollback status - // is still logged. - if (oldLoggingPackages.isEmpty()) { - oldLoggingPackages.add(null); - } - - for (VersionedPackage oldLoggingPackage : oldLoggingPackages) { - if (sessionInfo.isStagedSessionApplied()) { - logEvent(oldLoggingPackage, - WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, - WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); - } else if (sessionInfo.isStagedSessionFailed()) { - logEvent(oldLoggingPackage, - WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, - WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); - } + if (sessionInfo.isStagedSessionApplied()) { + logEvent(oldLoggingPackage, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); + } else if (sessionInfo.isStagedSessionFailed()) { + logEvent(oldLoggingPackage, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); } } diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index 8667801889cf..01c89b349687 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.util.IntArray; +import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseLongArray; @@ -354,6 +355,26 @@ public class RollbackUnitTest { assertThat(rollback.allPackagesEnabled()).isTrue(); } + @Test + public void readAndWriteStagedRollbackIdsFile() throws Exception { + File testFile = File.createTempFile("test", ".txt"); + RollbackPackageHealthObserver.writeStagedRollbackId(testFile, 2468, null); + RollbackPackageHealthObserver.writeStagedRollbackId(testFile, 12345, + new VersionedPackage("com.test.package", 1)); + RollbackPackageHealthObserver.writeStagedRollbackId(testFile, 13579, + new VersionedPackage("com.test.package2", 2)); + SparseArray readInfo = + RollbackPackageHealthObserver.readStagedRollbackIds(testFile); + assertThat(readInfo.size()).isEqualTo(3); + + assertThat(readInfo.keyAt(0)).isEqualTo(2468); + assertThat(readInfo.valueAt(0)).isEqualTo(""); + assertThat(readInfo.keyAt(1)).isEqualTo(12345); + assertThat(readInfo.valueAt(1)).isEqualTo("com.test.package"); + assertThat(readInfo.keyAt(2)).isEqualTo(13579); + assertThat(readInfo.valueAt(2)).isEqualTo("com.test.package2"); + } + @Test public void minExtVerConstraintNotViolated() { addPkgWithMinExtVersions("pkg0", new int[][] {{30, 4}}); -- GitLab From da7b3c286be7dc2d91f5476d8e2ce0f36799b42f Mon Sep 17 00:00:00 2001 From: chaviw Date: Fri, 24 Apr 2020 11:25:08 -0700 Subject: [PATCH 079/166] Re-parent children when Organized Window is no longer controlled When an Organized Window is no longer controlled by a specific client, we create a new SurfaceControl and reparent the children so the client no longer has control over the WindowContainer Test: Pip, TaskOrganizerMultiWindowTest Bug: 154558563 Change-Id: I76e0da115fd48f982a82bd6e2351e7171934acfc --- .../systemui/stackdivider/Divider.java | 14 ++- .../SplitScreenTaskOrganizer.java | 101 +++++++++++++----- .../com/android/server/wm/DisplayArea.java | 13 ++- .../core/java/com/android/server/wm/Task.java | 43 ++++++-- .../server/wm/TaskOrganizerController.java | 1 + .../android/server/wm/WindowContainer.java | 34 ++++++ .../TaskOrganizerMultiWindowTest.java | 15 +-- .../test/taskembed/TaskOrganizerPipTest.java | 5 +- 8 files changed, 172 insertions(+), 54 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index f36f8c131848..ad2b22fb6d9b 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -36,7 +36,6 @@ import android.provider.Settings; import android.util.Slog; import android.view.LayoutInflater; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.View; import android.window.TaskOrganizer; import android.window.WindowContainerToken; @@ -94,7 +93,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, private boolean mHomeStackResizable = false; private ForcedResizableInfoActivityController mForcedResizableController; private SystemWindows mSystemWindows; - final SurfaceSession mSurfaceSession = new SurfaceSession(); private DisplayController mDisplayController; private DisplayImeController mImeController; final TransactionPool mTransactionPool; @@ -493,7 +491,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, return; } try { - mSplits.init(mSurfaceSession); + mSplits.init(); // Set starting tile bounds based on middle target final WindowContainerTransaction tct = new WindowContainerTransaction(); int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; @@ -505,7 +503,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, return; } ActivityManagerWrapper.getInstance().registerTaskStackListener(mActivityRestartListener); - update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration()); } @Override @@ -582,6 +579,15 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } } + void onTaskVanished() { + mHandler.post(this::removeDivider); + } + + void onTasksReady() { + mHandler.post(() -> update(mDisplayController.getDisplayContext( + mContext.getDisplayId()).getResources().getConfiguration())); + } + void updateVisibility(final boolean visible) { if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible); if (mVisible != visible) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java index f5d6cb6fd4b0..fd6722ad7d4e 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java @@ -47,39 +47,27 @@ class SplitScreenTaskOrganizer extends TaskOrganizer { final Divider mDivider; private boolean mSplitScreenSupported = false; + final SurfaceSession mSurfaceSession = new SurfaceSession(); + SplitScreenTaskOrganizer(Divider divider) { mDivider = divider; } - void init(SurfaceSession session) throws RemoteException { + void init() throws RemoteException { registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - try { - mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, - WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, - WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - mPrimarySurface = mPrimary.token.getLeash(); - mSecondarySurface = mSecondary.token.getLeash(); - } catch (Exception e) { - // teardown to prevent callbacks - unregisterOrganizer(); - throw e; + synchronized (this) { + try { + mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, + WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, + WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + } catch (Exception e) { + // teardown to prevent callbacks + unregisterOrganizer(); + throw e; + } } - mSplitScreenSupported = true; - - // Initialize dim surfaces: - mPrimaryDim = new SurfaceControl.Builder(session).setParent(mPrimarySurface) - .setColorLayer().setName("Primary Divider Dim").build(); - mSecondaryDim = new SurfaceControl.Builder(session).setParent(mSecondarySurface) - .setColorLayer().setName("Secondary Divider Dim").build(); - SurfaceControl.Transaction t = getTransaction(); - t.setLayer(mPrimaryDim, Integer.MAX_VALUE); - t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f}); - t.setLayer(mSecondaryDim, Integer.MAX_VALUE); - t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f}); - t.apply(); - releaseTransaction(t); } boolean isSplitScreenSupported() { @@ -94,6 +82,67 @@ class SplitScreenTaskOrganizer extends TaskOrganizer { mDivider.mTransactionPool.release(t); } + @Override + public void onTaskAppeared(RunningTaskInfo taskInfo) { + synchronized (this) { + if (mPrimary == null || mSecondary == null) { + Log.w(TAG, "Received onTaskAppeared before creating root tasks " + taskInfo); + return; + } + + if (taskInfo.token.equals(mPrimary.token)) { + mPrimarySurface = taskInfo.token.getLeash(); + } else if (taskInfo.token.equals(mSecondary.token)) { + mSecondarySurface = taskInfo.token.getLeash(); + } + + if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) { + mSplitScreenSupported = true; + + // Initialize dim surfaces: + mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession) + .setParent(mPrimarySurface).setColorLayer() + .setName("Primary Divider Dim").build(); + mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession) + .setParent(mSecondarySurface).setColorLayer() + .setName("Secondary Divider Dim").build(); + SurfaceControl.Transaction t = getTransaction(); + t.setLayer(mPrimaryDim, Integer.MAX_VALUE); + t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f}); + t.setLayer(mSecondaryDim, Integer.MAX_VALUE); + t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f}); + t.apply(); + releaseTransaction(t); + + mDivider.onTasksReady(); + } + } + } + + @Override + public void onTaskVanished(RunningTaskInfo taskInfo) { + synchronized (this) { + final boolean isPrimaryTask = mPrimary != null + && taskInfo.token.equals(mPrimary.token); + final boolean isSecondaryTask = mSecondary != null + && taskInfo.token.equals(mSecondary.token); + + if (mSplitScreenSupported && (isPrimaryTask || isSecondaryTask)) { + mSplitScreenSupported = false; + + SurfaceControl.Transaction t = getTransaction(); + t.remove(mPrimaryDim); + t.remove(mSecondaryDim); + t.remove(mPrimarySurface); + t.remove(mSecondarySurface); + t.apply(); + releaseTransaction(t); + + mDivider.onTaskVanished(); + } + } + } + @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { if (taskInfo.displayId != DEFAULT_DISPLAY) { diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 9b34bd17d042..4ecf348ce85d 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -140,8 +140,12 @@ public class DisplayArea extends WindowContainer { void setOrganizer(IDisplayAreaOrganizer organizer) { if (mOrganizer == organizer) return; - sendDisplayAreaVanished(); + IDisplayAreaOrganizer lastOrganizer = mOrganizer; + // Update the new display area organizer before calling sendDisplayAreaVanished since it + // could result in a new SurfaceControl getting created that would notify the old organizer + // about it. mOrganizer = organizer; + sendDisplayAreaVanished(lastOrganizer); sendDisplayAreaAppeared(); } @@ -150,9 +154,10 @@ public class DisplayArea extends WindowContainer { mOrganizerController.onDisplayAreaAppeared(mOrganizer, this); } - void sendDisplayAreaVanished() { - if (mOrganizer == null) return; - mOrganizerController.onDisplayAreaVanished(mOrganizer, this); + void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) { + if (organizer == null) return; + migrateToNewSurfaceControl(); + mOrganizerController.onDisplayAreaVanished(organizer, this); } @Override diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 5a27f47b28aa..b874f1cbb225 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2004,6 +2004,14 @@ class Task extends WindowContainer { return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM); } + @Override + void migrateToNewSurfaceControl() { + super.migrateToNewSurfaceControl(); + mLastSurfaceSize.x = 0; + mLastSurfaceSize.y = 0; + updateSurfaceSize(getPendingTransaction()); + } + void updateSurfaceSize(SurfaceControl.Transaction transaction) { if (mSurfaceControl == null || mCreatedByOrganizer) { return; @@ -4400,16 +4408,25 @@ class Task extends WindowContainer { return mHasBeenVisible; } - /** In the case that these three conditions are true, we want to send the Task to - * the organizer: - * 1. We have a SurfaceControl - * 2. An organizer has been set - * 3. We have finished drawing + /** In the case that these conditions are true, we want to send the Task to the organizer: + * 1. An organizer has been set + * 2. The Task was created by the organizer + * or + * 2a. We have a SurfaceControl + * 2b. We have finished drawing * Any time any of these conditions are updated, the updating code should call * sendTaskAppeared. */ boolean taskAppearedReady() { - return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible(); + if (mTaskOrganizer == null) { + return false; + } + + if (mCreatedByOrganizer) { + return true; + } + + return mSurfaceControl != null && getHasBeenVisible(); } private void sendTaskAppeared() { @@ -4418,9 +4435,9 @@ class Task extends WindowContainer { } } - private void sendTaskVanished() { - if (mTaskOrganizer != null) { - mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this); + private void sendTaskVanished(ITaskOrganizer organizer) { + if (organizer != null) { + mAtmService.mTaskOrganizerController.onTaskVanished(organizer, this); } } @@ -4429,9 +4446,13 @@ class Task extends WindowContainer { if (mTaskOrganizer == organizer) { return false; } - // Let the old organizer know it has lost control. - sendTaskVanished(); + + ITaskOrganizer previousOrganizer = mTaskOrganizer; + // Update the new task organizer before calling sendTaskVanished since it could result in + // a new SurfaceControl getting created that would notify the old organizer about it. mTaskOrganizer = organizer; + // Let the old organizer know it has lost control. + sendTaskVanished(previousOrganizer); if (mTaskOrganizer != null) { sendTaskAppeared(); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 306c100e651c..9d229df1967e 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -211,6 +211,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void removeTask(Task t) { if (t.mTaskAppearedSent) { + t.migrateToNewSurfaceControl(); t.mTaskAppearedSent = false; mOrganizer.onTaskVanished(t); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 07cf4e5c1e80..1155bb8520ae 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -405,6 +405,40 @@ class WindowContainer extends ConfigurationContainer< updateSurfacePosition(); } + /** + * Create a new SurfaceControl for this WindowContainer and migrate all properties to the new + * SurfaceControl. Properties include: + * 1. Children + * 2. Position + * 3. Z order + * + * Remove the old SurfaceControl since it's no longer needed. + * + * This is used to revoke control of the SurfaceControl from a client process that was + * previously organizing this WindowContainer. + */ + void migrateToNewSurfaceControl() { + SurfaceControl.Transaction t = getPendingTransaction(); + t.remove(mSurfaceControl); + // Clear the last position so the new SurfaceControl will get correct position + mLastSurfacePosition.set(0, 0); + + createSurfaceControl(false /* force */); + if (mLastRelativeToLayer != null) { + t.setRelativeLayer(mSurfaceControl, mLastRelativeToLayer, mLastLayer); + } else { + t.setLayer(mSurfaceControl, mLastLayer); + } + + for (int i = 0; i < mChildren.size(); i++) { + SurfaceControl sc = mChildren.get(i).getSurfaceControl(); + if (sc != null) { + t.reparent(sc, mSurfaceControl); + } + } + scheduleAnimation(); + } + /** * Called when the surface is shown for the first time. */ diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java index 86c3fa0fe034..da4afec8a172 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java @@ -18,29 +18,24 @@ package com.android.test.taskembed; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; -import android.app.ActivityManager; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; -import android.window.ITaskOrganizer; -import android.window.IWindowContainerTransactionCallback; +import android.widget.LinearLayout; import android.window.TaskOrganizer; import android.window.WindowContainerTransaction; -import android.widget.LinearLayout; import android.window.WindowContainerTransactionCallback; -import android.window.WindowOrganizer; public class TaskOrganizerMultiWindowTest extends Activity { class SplitLayout extends LinearLayout implements View.OnTouchListener { @@ -173,6 +168,12 @@ public class TaskOrganizerMultiWindowTest extends Activity { setContentView(splitView); } + @Override + protected void onDestroy() { + super.onDestroy(); + mOrganizer.unregisterOrganizer(); + } + private void addFlags(Intent intent) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); } diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java index 2a1aa2e1de65..0a7a6d56249f 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java @@ -24,10 +24,10 @@ import android.content.Intent; import android.graphics.Rect; import android.os.IBinder; import android.view.ViewGroup; -import android.window.TaskOrganizer; -import android.window.WindowContainerTransaction; import android.view.WindowManager; import android.widget.FrameLayout; +import android.window.TaskOrganizer; +import android.window.WindowContainerTransaction; public class TaskOrganizerPipTest extends Service { static final int PIP_WIDTH = 640; @@ -76,5 +76,6 @@ public class TaskOrganizerPipTest extends Service { @Override public void onDestroy() { super.onDestroy(); + mOrganizer.unregisterOrganizer(); } } -- GitLab From 7de50009a336fc42c81ee4ff5407a059b1d51596 Mon Sep 17 00:00:00 2001 From: chaviw Date: Mon, 27 Apr 2020 12:33:30 -0700 Subject: [PATCH 080/166] Remove getLeash from WindowContainerToken If the WindowContainer was revoked from a registered organizer, the client could still call getLeash to system server and control the leash for the WindowContainer. Instead, pass the leash back to the client in onTaskAppeared and onDisplayAreaAppeared. Once the WindowContainer is revoked from the client, the leash will reference the old WindowContainer SurfaceControl and will not be able to control the WindowContainer anymore. Test: Split screen Test: DisplayAreaOrganizerTest Test: WindowOrganizerTest Bug: 154558563 Change-Id: I1f6eb987a2a3fecfef912a3009ee52989c85ff4b --- api/test-current.txt | 5 +- .../android/window/DisplayAreaOrganizer.java | 9 ++- .../android/window/IDisplayAreaOrganizer.aidl | 3 +- core/java/android/window/ITaskOrganizer.aidl | 9 ++- .../android/window/IWindowContainerToken.aidl | 5 -- core/java/android/window/TaskOrganizer.java | 8 ++- .../window/TaskOrganizerTaskEmbedder.java | 5 +- .../android/window/WindowContainerToken.java | 13 ---- .../systemui/pip/PipTaskOrganizer.java | 4 +- .../SplitScreenTaskOrganizer.java | 6 +- .../wm/DisplayAreaOrganizerController.java | 4 +- .../server/wm/TaskOrganizerController.java | 4 +- .../android/server/wm/WindowContainer.java | 10 ---- .../android/server/wm/ActivityTestsBase.java | 3 +- .../server/wm/DisplayAreaOrganizerTest.java | 8 ++- .../server/wm/WindowOrganizerTests.java | 59 +++++++++++-------- .../TaskOrganizerMultiWindowTest.java | 38 ++++++------ .../test/taskembed/TaskOrganizerPipTest.java | 15 ++--- .../com/android/test/taskembed/TaskView.java | 37 +++++------- 19 files changed, 120 insertions(+), 125 deletions(-) diff --git a/api/test-current.txt b/api/test-current.txt index 5df84ec97bf7..bde91e38d054 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5283,7 +5283,7 @@ package android.window { public class DisplayAreaOrganizer extends android.window.WindowOrganizer { ctor public DisplayAreaOrganizer(); - method public void onDisplayAreaAppeared(@NonNull android.window.DisplayAreaInfo); + method public void onDisplayAreaAppeared(@NonNull android.window.DisplayAreaInfo, @NonNull android.view.SurfaceControl); method public void onDisplayAreaVanished(@NonNull android.window.DisplayAreaInfo); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void registerOrganizer(int); field public static final int FEATURE_DEFAULT_TASK_CONTAINER = 1; // 0x1 @@ -5303,7 +5303,7 @@ package android.window { method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static android.window.WindowContainerToken getImeTarget(int); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static java.util.List getRootTasks(int, @NonNull int[]); method @BinderThread public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo); - method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo); + method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl); method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo); method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer(int); @@ -5314,7 +5314,6 @@ package android.window { public final class WindowContainerToken implements android.os.Parcelable { method public int describeContents(); - method @Nullable public android.view.SurfaceControl getLeash(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index f3ef5a0c0aa2..a2fd128e2246 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.os.RemoteException; import android.util.Singleton; +import android.view.SurfaceControl; /** * Interface for WindowManager to delegate control of display areas. @@ -64,7 +65,8 @@ public class DisplayAreaOrganizer extends WindowOrganizer { } } - public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo) {} + public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo, + @NonNull SurfaceControl leash) {} public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {} @@ -76,8 +78,9 @@ public class DisplayAreaOrganizer extends WindowOrganizer { private final IDisplayAreaOrganizer mInterface = new IDisplayAreaOrganizer.Stub() { @Override - public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo) { - DisplayAreaOrganizer.this.onDisplayAreaAppeared(displayAreaInfo); + public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo, + @NonNull SurfaceControl leash) { + DisplayAreaOrganizer.this.onDisplayAreaAppeared(displayAreaInfo, leash); } @Override diff --git a/core/java/android/window/IDisplayAreaOrganizer.aidl b/core/java/android/window/IDisplayAreaOrganizer.aidl index 39a9235a4224..5e3e5e8e7d42 100644 --- a/core/java/android/window/IDisplayAreaOrganizer.aidl +++ b/core/java/android/window/IDisplayAreaOrganizer.aidl @@ -17,13 +17,14 @@ package android.window; import android.window.DisplayAreaInfo; +import android.view.SurfaceControl; /** * Interface for WindowManager to delegate control of display areas. * {@hide} */ oneway interface IDisplayAreaOrganizer { - void onDisplayAreaAppeared(in DisplayAreaInfo displayAreaInfo); + void onDisplayAreaAppeared(in DisplayAreaInfo displayAreaInfo, in SurfaceControl leash); void onDisplayAreaVanished(in DisplayAreaInfo displayAreaInfo); void onDisplayAreaInfoChanged(in DisplayAreaInfo displayAreaInfo); } diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl index b4f0162b71af..67465252d38e 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -25,7 +25,14 @@ import android.window.WindowContainerToken; * {@hide} */ oneway interface ITaskOrganizer { - void onTaskAppeared(in ActivityManager.RunningTaskInfo taskInfo); + /** + * A callback when the Task is available for the registered organizer. The client is responsible + * for releasing the SurfaceControl in the callback. + * + * @param taskInfo The information about the Task that's available + * @param leash A persistent leash for this Task. + */ + void onTaskAppeared(in ActivityManager.RunningTaskInfo taskInfo, in SurfaceControl leash); void onTaskVanished(in ActivityManager.RunningTaskInfo taskInfo); /** diff --git a/core/java/android/window/IWindowContainerToken.aidl b/core/java/android/window/IWindowContainerToken.aidl index 57c7abf9c7e1..81a04e9c10af 100644 --- a/core/java/android/window/IWindowContainerToken.aidl +++ b/core/java/android/window/IWindowContainerToken.aidl @@ -24,9 +24,4 @@ import android.view.SurfaceControl; * @hide */ interface IWindowContainerToken { - - /** - * Gets a persistent leash for this container or {@code null}. - */ - SurfaceControl getLeash(); } diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index f661d9af5999..1f5e53369cd8 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -24,6 +24,7 @@ import android.annotation.TestApi; import android.app.ActivityManager; import android.os.RemoteException; import android.util.Singleton; +import android.view.SurfaceControl; import java.util.List; @@ -59,7 +60,8 @@ public class TaskOrganizer extends WindowOrganizer { } @BinderThread - public void onTaskAppeared(@NonNull ActivityManager.RunningTaskInfo taskInfo) {} + public void onTaskAppeared(@NonNull ActivityManager.RunningTaskInfo taskInfo, + @NonNull SurfaceControl leash) {} @BinderThread public void onTaskVanished(@NonNull ActivityManager.RunningTaskInfo taskInfo) {} @@ -155,8 +157,8 @@ public class TaskOrganizer extends WindowOrganizer { private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { - TaskOrganizer.this.onTaskAppeared(taskInfo); + public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { + TaskOrganizer.this.onTaskAppeared(taskInfo, leash); } @Override diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java index b63741ec69c4..2fb46509f337 100644 --- a/core/java/android/window/TaskOrganizerTaskEmbedder.java +++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java @@ -21,7 +21,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import android.app.ActivityManager; import android.app.ActivityOptions; -import android.app.TaskStackListener; import android.content.Context; import android.graphics.Rect; import android.util.Log; @@ -215,14 +214,14 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder { private class TaskOrganizerImpl extends TaskOrganizer { @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { + public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (DEBUG) { log("taskAppeared: " + taskInfo.taskId); } mTaskInfo = taskInfo; mTaskToken = taskInfo.token; - mTaskLeash = mTaskToken.getLeash(); + mTaskLeash = leash; mTransaction.reparent(mTaskLeash, mSurfaceControl) .show(mTaskLeash) .show(mSurfaceControl) diff --git a/core/java/android/window/WindowContainerToken.java b/core/java/android/window/WindowContainerToken.java index 3316d0e5b71f..c92ccae66ff8 100644 --- a/core/java/android/window/WindowContainerToken.java +++ b/core/java/android/window/WindowContainerToken.java @@ -17,14 +17,10 @@ package android.window; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.TestApi; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; -import android.view.SurfaceControl; -import android.window.IWindowContainerToken; /** * Interface for a window container to communicate with the window manager. This also acts as a @@ -45,15 +41,6 @@ public final class WindowContainerToken implements Parcelable { mRealToken = IWindowContainerToken.Stub.asInterface(in.readStrongBinder()); } - @Nullable - public SurfaceControl getLeash() { - try { - return mRealToken.getLeash(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** @hide */ public IBinder asBinder() { return mRealToken.asBinder(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 9c8fb7c52eec..d38c481752c6 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -250,7 +250,7 @@ public class PipTaskOrganizer extends TaskOrganizer { } @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo info) { + public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { Objects.requireNonNull(info, "Requires RunningTaskInfo"); final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( info.topActivity, getAspectRatioOrDefault(info.pictureInPictureParams), @@ -259,7 +259,7 @@ public class PipTaskOrganizer extends TaskOrganizer { mTaskInfo = info; mToken = mTaskInfo.token; mInPip = true; - mLeash = mToken.getLeash(); + mLeash = leash; final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); mBoundsToRestore.put(mToken.asBinder(), currentBounds); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java index fd6722ad7d4e..717edc591d7f 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java @@ -83,7 +83,7 @@ class SplitScreenTaskOrganizer extends TaskOrganizer { } @Override - public void onTaskAppeared(RunningTaskInfo taskInfo) { + public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (this) { if (mPrimary == null || mSecondary == null) { Log.w(TAG, "Received onTaskAppeared before creating root tasks " + taskInfo); @@ -91,9 +91,9 @@ class SplitScreenTaskOrganizer extends TaskOrganizer { } if (taskInfo.token.equals(mPrimary.token)) { - mPrimarySurface = taskInfo.token.getLeash(); + mPrimarySurface = leash; } else if (taskInfo.token.equals(mSecondary.token)) { - mSecondarySurface = taskInfo.token.getLeash(); + mSecondarySurface = leash; } if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) { diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index 2c8c8201c534..b6c71bb17631 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.view.SurfaceControl; import android.window.IDisplayAreaOrganizer; import android.window.IDisplayAreaOrganizerController; @@ -113,7 +114,8 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) { try { - organizer.onDisplayAreaAppeared(da.getDisplayAreaInfo()); + SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl()); + organizer.onDisplayAreaAppeared(da.getDisplayAreaInfo(), outSurfaceControl); } catch (RemoteException e) { // Oh well... } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 9d229df1967e..83105c2f8696 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; +import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.WindowContainerToken; @@ -112,7 +113,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final RunningTaskInfo taskInfo = task.getTaskInfo(); mDeferTaskOrgCallbacksConsumer.accept(() -> { try { - mTaskOrganizer.onTaskAppeared(taskInfo); + SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl()); + mTaskOrganizer.onTaskAppeared(taskInfo, outSurfaceControl); } catch (RemoteException e) { Slog.e(TAG, "Exception sending onTaskAppeared callback", e); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 1155bb8520ae..3f8d7b5710aa 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2557,16 +2557,6 @@ class WindowContainer extends ConfigurationContainer< return (RemoteToken) binder; } - @Override - public SurfaceControl getLeash() { - final WindowContainer wc = getContainer(); - if (wc == null) return null; - // We need to copy the SurfaceControl instead of returning the original - // because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls - // to release themselves. - return new SurfaceControl(wc.getSurfaceControl()); - } - WindowContainerToken toWindowContainerToken() { if (mWindowContainerToken == null) { mWindowContainerToken = new WindowContainerToken(this); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 0700f9f2b29c..2991859c5dd4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -49,6 +49,7 @@ import android.os.Build; import android.os.Bundle; import android.os.UserHandle; import android.service.voice.IVoiceInteractionSession; +import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.WindowContainerToken; @@ -552,7 +553,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { mMoveToSecondaryOnEnter = move; } @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo info) { + public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { } @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java index 307b40f1b039..6a1f50d7e58a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java @@ -27,6 +27,8 @@ import android.graphics.Rect; import android.os.Binder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; +import android.window.DisplayAreaInfo; import android.window.IDisplayAreaOrganizer; import androidx.test.filters.SmallTest; @@ -74,7 +76,8 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { @Test public void testAppearedVanished() throws RemoteException { IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); - verify(organizer).onDisplayAreaAppeared(any()); + verify(organizer) + .onDisplayAreaAppeared(any(DisplayAreaInfo.class), any(SurfaceControl.class)); unregisterMockOrganizer(organizer); verify(organizer).onDisplayAreaVanished(any()); @@ -83,7 +86,8 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { @Test public void testChanged() throws RemoteException { IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); - verify(organizer).onDisplayAreaAppeared(any()); + verify(organizer) + .onDisplayAreaAppeared(any(DisplayAreaInfo.class), any(SurfaceControl.class)); mDisplayContent.setBounds(new Rect(0, 0, 1000, 1000)); verify(organizer).onDisplayAreaInfoChanged(any()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 7d2e88014f45..aa68c6900f6f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -65,6 +65,7 @@ import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.util.Rational; import android.view.Display; +import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.WindowContainerTransaction; @@ -133,7 +134,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); task.setTaskOrganizer(organizer); - verify(organizer).onTaskAppeared(any()); + verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); + task.removeImmediately(); verify(organizer).onTaskVanished(any()); @@ -147,11 +149,12 @@ public class WindowOrganizerTests extends WindowTestsBase { task.setTaskOrganizer(organizer); - verify(organizer, never()).onTaskAppeared(any()); + verify(organizer, never()) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); task.setHasBeenVisible(true); assertTrue(stack.getHasBeenVisible()); - verify(organizer).onTaskAppeared(any()); + verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); task.removeImmediately(); verify(organizer).onTaskVanished(any()); @@ -167,7 +170,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // that even though a TaskOrganizer is set remove doesn't emit // a vanish callback, because we never emitted appear. task.setTaskOrganizer(organizer); - verify(organizer, never()).onTaskAppeared(any()); + verify(organizer, never()) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); task.removeImmediately(); verify(organizer, never()).onTaskVanished(any()); } @@ -180,10 +184,10 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED); task.setTaskOrganizer(organizer); - verify(organizer).onTaskAppeared(any()); + verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); task.setTaskOrganizer(organizer2); verify(organizer).onTaskVanished(any()); - verify(organizer2).onTaskAppeared(any()); + verify(organizer2).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); } @Test @@ -194,10 +198,10 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED); stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - verify(organizer).onTaskAppeared(any()); + verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.setWindowingMode(WINDOWING_MODE_PINNED); verify(organizer).onTaskVanished(any()); - verify(organizer2).onTaskAppeared(any()); + verify(organizer2).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); } @Test @@ -207,7 +211,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - verify(organizer, never()).onTaskAppeared(any()); + verify(organizer, never()) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); @@ -222,7 +227,7 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); stack.setTaskOrganizer(organizer); - verify(organizer).onTaskAppeared(any()); + verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); stack.setTaskOrganizer(null); @@ -237,7 +242,7 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - verify(organizer).onTaskAppeared(any()); + verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); @@ -257,7 +262,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // First organizer is registered, verify a task appears when changing windowing mode stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - verify(organizer, times(1)).onTaskAppeared(any()); + verify(organizer, times(1)) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); // Now we replace the registration and1 verify the new organizer receives tasks @@ -265,7 +271,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); // One each for task and task2 - verify(organizer2, times(2)).onTaskAppeared(any()); + verify(organizer2, times(2)) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); verify(organizer2, times(0)).onTaskVanished(any()); // One for task verify(organizer).onTaskVanished(any()); @@ -274,11 +281,13 @@ public class WindowOrganizerTests extends WindowTestsBase { // Now we unregister the second one, the first one should automatically be reregistered // so we verify that it's now seeing changes. mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); - verify(organizer, times(3)).onTaskAppeared(any()); + verify(organizer, times(3)) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); verify(organizer2, times(2)).onTaskVanished(any()); stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - verify(organizer, times(4)).onTaskAppeared(any()); + verify(organizer, times(4)) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); verify(organizer2, times(2)).onTaskVanished(any()); assertTrue(stack3.isOrganized()); } @@ -291,7 +300,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task task = createTask(stack); final Task task2 = createTask(stack); stack.setWindowingMode(WINDOWING_MODE_PINNED); - verify(organizer, times(1)).onTaskAppeared(any()); + verify(organizer, times(1)) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); verify(organizer, times(1)).onTaskVanished(any()); @@ -304,7 +314,8 @@ public class WindowOrganizerTests extends WindowTestsBase { stack.setWindowingMode(WINDOWING_MODE_PINNED); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED); - verify(organizer, times(1)).onTaskAppeared(any()); + verify(organizer, times(1)) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); } @Test @@ -459,7 +470,7 @@ public class WindowOrganizerTests extends WindowTestsBase { public void testTileAddRemoveChild() { ITaskOrganizer listener = new ITaskOrganizer.Stub() { @Override - public void onTaskAppeared(RunningTaskInfo taskInfo) { } + public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { } @Override public void onTaskVanished(RunningTaskInfo container) { } @@ -515,7 +526,7 @@ public class WindowOrganizerTests extends WindowTestsBase { final boolean[] called = {false}; ITaskOrganizer listener = new ITaskOrganizer.Stub() { @Override - public void onTaskAppeared(RunningTaskInfo taskInfo) { } + public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { } @Override public void onTaskVanished(RunningTaskInfo container) { } @@ -577,7 +588,7 @@ public class WindowOrganizerTests extends WindowTestsBase { final ArrayMap lastReportedTiles = new ArrayMap<>(); ITaskOrganizer listener = new ITaskOrganizer.Stub() { @Override - public void onTaskAppeared(RunningTaskInfo taskInfo) { } + public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { } @Override public void onTaskVanished(RunningTaskInfo container) { } @@ -804,7 +815,7 @@ public class WindowOrganizerTests extends WindowTestsBase { RunningTaskInfo mInfo; @Override - public void onTaskAppeared(RunningTaskInfo info) { + public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) { mInfo = info; } @Override @@ -907,12 +918,14 @@ public class WindowOrganizerTests extends WindowTestsBase { task.setTaskOrganizer(organizer); // setHasBeenVisible was already called once by the set-up code. task.setHasBeenVisible(true); - verify(organizer, times(1)).onTaskAppeared(any()); + verify(organizer, times(1)) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); task.setTaskOrganizer(null); verify(organizer, times(1)).onTaskVanished(any()); task.setTaskOrganizer(organizer); - verify(organizer, times(2)).onTaskAppeared(any()); + verify(organizer, times(2)) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); task.removeImmediately(); verify(organizer, times(2)).onTaskVanished(any()); diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java index da4afec8a172..073ae30aaf1a 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java @@ -38,7 +38,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; public class TaskOrganizerMultiWindowTest extends Activity { - class SplitLayout extends LinearLayout implements View.OnTouchListener { + static class SplitLayout extends LinearLayout implements View.OnTouchListener { View mView1; View mView2; View mDividerView; @@ -94,8 +94,8 @@ public class TaskOrganizerMultiWindowTest extends Activity { class ResizingTaskView extends TaskView { final Intent mIntent; boolean launched = false; - ResizingTaskView(Context c, TaskOrganizer o, int windowingMode, Intent i) { - super(c, o, windowingMode); + ResizingTaskView(Context c, Intent i) { + super(c); mIntent = i; } @@ -120,9 +120,9 @@ public class TaskOrganizerMultiWindowTest extends Activity { } } - TaskView mTaskView1; - TaskView mTaskView2; - boolean gotFirstTask = false; + private TaskView mTaskView1; + private TaskView mTaskView2; + private boolean mGotFirstTask = false; class Organizer extends TaskOrganizer { private int receivedTransactions = 0; @@ -141,17 +141,17 @@ public class TaskOrganizerMultiWindowTest extends Activity { }; @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo ti) { - if (!gotFirstTask) { - mTaskView1.reparentTask(ti.token); - gotFirstTask = true; + public void onTaskAppeared(ActivityManager.RunningTaskInfo ti, SurfaceControl leash) { + if (!mGotFirstTask) { + mTaskView1.reparentTask(ti.token, leash); + mGotFirstTask = true; } else { - mTaskView2.reparentTask(ti.token); + mTaskView2.reparentTask(ti.token, leash); } } } - Organizer mOrganizer = new Organizer(); + private Organizer mOrganizer = new Organizer(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -159,10 +159,8 @@ public class TaskOrganizerMultiWindowTest extends Activity { mOrganizer.registerOrganizer(WINDOWING_MODE_MULTI_WINDOW); - mTaskView1 = new ResizingTaskView(this, mOrganizer, WINDOWING_MODE_MULTI_WINDOW, - makeSettingsIntent()); - mTaskView2 = new ResizingTaskView(this, mOrganizer, WINDOWING_MODE_MULTI_WINDOW, - makeContactsIntent()); + mTaskView1 = new ResizingTaskView(this, makeSettingsIntent()); + mTaskView2 = new ResizingTaskView(this, makeContactsIntent()); View splitView = new SplitLayout(this, mTaskView1, mTaskView2); setContentView(splitView); @@ -178,14 +176,14 @@ public class TaskOrganizerMultiWindowTest extends Activity { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); } - Intent makeSettingsIntent() { + private Intent makeSettingsIntent() { Intent intent = new Intent(); intent.setAction(android.provider.Settings.ACTION_SETTINGS); addFlags(intent); return intent; } - Intent makeContactsIntent() { + private Intent makeContactsIntent() { Intent intent = new Intent(); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_APP_CONTACTS); @@ -193,14 +191,14 @@ public class TaskOrganizerMultiWindowTest extends Activity { return intent; } - Bundle makeLaunchOptions(int width, int height) { + private Bundle makeLaunchOptions(int width, int height) { ActivityOptions o = ActivityOptions.makeBasic(); o.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); o.setLaunchBounds(new Rect(0, 0, width, height)); return o.toBundle(); } - void launchOrganizedActivity(Intent i, int width, int height) { + private void launchOrganizedActivity(Intent i, int width, int height) { startActivity(i, makeLaunchOptions(width, height)); } } diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java index 0a7a6d56249f..8fc5c5d78b60 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java @@ -23,6 +23,7 @@ import android.app.Service; import android.content.Intent; import android.graphics.Rect; import android.os.IBinder; +import android.view.SurfaceControl; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; @@ -30,14 +31,14 @@ import android.window.TaskOrganizer; import android.window.WindowContainerTransaction; public class TaskOrganizerPipTest extends Service { - static final int PIP_WIDTH = 640; - static final int PIP_HEIGHT = 360; + private static final int PIP_WIDTH = 640; + private static final int PIP_HEIGHT = 360; - TaskView mTaskView; + private TaskView mTaskView; class Organizer extends TaskOrganizer { - public void onTaskAppeared(ActivityManager.RunningTaskInfo ti) { - mTaskView.reparentTask(ti.token); + public void onTaskAppeared(ActivityManager.RunningTaskInfo ti, SurfaceControl leash) { + mTaskView.reparentTask(ti.token, leash); final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.scheduleFinishEnterPip(ti.token, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT)); @@ -45,7 +46,7 @@ public class TaskOrganizerPipTest extends Service { } } - Organizer mOrganizer = new Organizer(); + private Organizer mOrganizer = new Organizer(); @Override public IBinder onBind(Intent intent) { @@ -66,7 +67,7 @@ public class TaskOrganizerPipTest extends Service { FrameLayout layout = new FrameLayout(this); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(PIP_WIDTH, PIP_HEIGHT); - mTaskView = new TaskView(this, mOrganizer, WINDOWING_MODE_PINNED); + mTaskView = new TaskView(this); layout.addView(mTaskView, lp); WindowManager wm = getSystemService(WindowManager.class); diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java index aa041f22a46e..208018c2543a 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java @@ -16,34 +16,27 @@ package com.android.test.taskembed; -import android.app.ActivityTaskManager; import android.content.Context; -import android.window.TaskOrganizer; -import android.window.WindowContainerToken; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceView; -import android.window.ITaskOrganizer; +import android.window.WindowContainerToken; /** * Simple SurfaceView wrapper which registers a TaskOrganizer * after it's Surface is ready. */ class TaskView extends SurfaceView implements SurfaceHolder.Callback { - final TaskOrganizer mTaskOrganizer; - final int mWindowingMode; WindowContainerToken mWc; + private SurfaceControl mLeash; - boolean mSurfaceCreated = false; - boolean mNeedsReparent; + private boolean mSurfaceCreated = false; + private boolean mNeedsReparent; - TaskView(Context c, TaskOrganizer o, int windowingMode) { + TaskView(Context c) { super(c); getHolder().addCallback(this); setZOrderOnTop(true); - - mTaskOrganizer = o; - mWindowingMode = windowingMode; } @Override @@ -63,27 +56,25 @@ class TaskView extends SurfaceView implements SurfaceHolder.Callback { public void surfaceDestroyed(SurfaceHolder holder) { } - void reparentTask(WindowContainerToken wc) { + void reparentTask(WindowContainerToken wc, SurfaceControl leash) { mWc = wc; - if (mSurfaceCreated == false) { + mLeash = leash; + if (!mSurfaceCreated) { mNeedsReparent = true; } else { reparentLeash(); } } - void reparentLeash() { + private void reparentLeash() { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - SurfaceControl leash = null; - try { - leash = mWc.getLeash(); - } catch (Exception e) { - // System server died.. oh well + if (mLeash == null) { + return; } - t.reparent(leash, getSurfaceControl()) - .setPosition(leash, 0, 0) - .show(leash) + t.reparent(mLeash, getSurfaceControl()) + .setPosition(mLeash, 0, 0) + .show(mLeash) .apply(); } } -- GitLab From 0f00675d7483fe08b4dd10a8572b9d34fe8e1839 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Wed, 29 Apr 2020 09:37:10 -0700 Subject: [PATCH 081/166] Add documentation for OPSTR_LOADER_USAGE_STATS Fixes: 150219240 Test: None. Doc only change Change-Id: I6b156acd49acf95b34ecacff6d252b7ccab6303a --- core/java/android/app/AppOpsManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 37f683ef435f..33bacf0d1146 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1083,7 +1083,7 @@ public class AppOpsManager { * @hide */ public static final int OP_ACTIVATE_PLATFORM_VPN = AppProtoEnums.APP_OP_ACTIVATE_PLATFORM_VPN; - /** @hide */ + /** @hide Controls whether or not read logs are available for incremental installations. */ public static final int OP_LOADER_USAGE_STATS = AppProtoEnums.APP_OP_LOADER_USAGE_STATS; // App op deprecated/removed. -- GitLab From 80eda9119b72671a470785fd7a17d9aa6819b699 Mon Sep 17 00:00:00 2001 From: kholoud mohamed Date: Wed, 29 Apr 2020 17:40:33 +0100 Subject: [PATCH 082/166] Fix icon color in dark theme. Bug: 154582949 Test: Visual testing Change-Id: Ib08135be632e288aa3afc5232e5800395ebb8b17 --- .../LayoutPreference/res/drawable/ic_swap_horiz_blue.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml index 04de17474368..afebeba2d5ff 100644 --- a/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml +++ b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml @@ -18,8 +18,9 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24"> + android:viewportHeight="24" + android:tint="?android:attr/colorAccent"> + android:fillColor="@android:color/white"/> -- GitLab From b635b3a2eb184df87728dfeb5a69582cf9768c33 Mon Sep 17 00:00:00 2001 From: Muhammad Qureshi Date: Wed, 22 Apr 2020 20:57:41 -0700 Subject: [PATCH 083/166] Add tests for mapIsolatedUidToHostUid methods - Remove unneeded dependency on atoms.proto - Remove extractIntoVector function. Assert on actual output directly. - Use a helper method for creating UidMap mock for all tests. - Add AttributionChain variants of existing tests in puller_util_tests. - Add tests in StatsLogProcessor_test to test mapIsolatedUidToHostUid() in StatsLogProcessor. Bug: 154285085 Test: bit statsd_test:* Change-Id: I6d0822be9f0fbca23bd2f441dc63b97110567307 --- cmds/statsd/src/atoms.proto | 4 +- cmds/statsd/tests/StatsLogProcessor_test.cpp | 105 ++++ .../tests/anomaly/AnomalyTracker_test.cpp | 4 +- .../tests/external/puller_util_test.cpp | 486 ++++++++++++------ .../tests/metrics/metrics_test_helper.h | 6 - cmds/statsd/tests/statsd_test_util.cpp | 45 +- cmds/statsd/tests/statsd_test_util.h | 21 +- 7 files changed, 498 insertions(+), 173 deletions(-) diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index b3da32fc943f..64d42bbd093d 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -454,9 +454,9 @@ message Atom { BluetoothActivityInfo bluetooth_activity_info = 10007 [(module) = "framework"]; ProcessMemoryState process_memory_state = 10013 [(module) = "framework"]; SystemElapsedRealtime system_elapsed_realtime = 10014 [(module) = "framework"]; - SystemUptime system_uptime = 10015 [(module) = "framework", (module) = "statsdtest"]; + SystemUptime system_uptime = 10015 [(module) = "framework"]; CpuActiveTime cpu_active_time = 10016 [(module) = "framework", (module) = "statsdtest"]; - CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework", (module) = "statsdtest"]; + CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework"]; DiskSpace disk_space = 10018 [deprecated=true, (module) = "statsdtest"]; RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"]; FullBatteryCapacity full_battery_capacity = 10020 [(module) = "framework"]; diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index b809286da5f4..e6144c52c436 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -1691,6 +1691,111 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { &buffer); } +TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUid) { + int hostUid = 20; + int isolatedUid = 30; + uint64_t eventTimeNs = 12355; + int atomId = 89; + int field1 = 90; + int field2 = 28; + sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); + ConfigKey cfgKey; + StatsdConfig config = MakeConfig(false); + sp processor = + CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); + + shared_ptr logEvent = makeUidLogEvent(atomId, eventTimeNs, hostUid, field1, field2); + + processor->OnLogEvent(logEvent.get()); + + const vector* actualFieldValues = &logEvent->getValues(); + EXPECT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value); +} + +TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUid) { + int hostUid = 20; + int isolatedUid = 30; + uint64_t eventTimeNs = 12355; + int atomId = 89; + int field1 = 90; + int field2 = 28; + sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); + ConfigKey cfgKey; + StatsdConfig config = MakeConfig(false); + sp processor = + CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); + + shared_ptr logEvent = + makeUidLogEvent(atomId, eventTimeNs, isolatedUid, field1, field2); + + processor->OnLogEvent(logEvent.get()); + + const vector* actualFieldValues = &logEvent->getValues(); + EXPECT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value); +} + +TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUidAttributionChain) { + int hostUid = 20; + int isolatedUid = 30; + uint64_t eventTimeNs = 12355; + int atomId = 89; + int field1 = 90; + int field2 = 28; + sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); + ConfigKey cfgKey; + StatsdConfig config = MakeConfig(false); + sp processor = + CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); + + shared_ptr logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {hostUid, 200}, + {"tag1", "tag2"}, field1, field2); + + processor->OnLogEvent(logEvent.get()); + + const vector* actualFieldValues = &logEvent->getValues(); + EXPECT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value); +} + +TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionChain) { + int hostUid = 20; + int isolatedUid = 30; + uint64_t eventTimeNs = 12355; + int atomId = 89; + int field1 = 90; + int field2 = 28; + sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid}); + ConfigKey cfgKey; + StatsdConfig config = MakeConfig(false); + sp processor = + CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap); + + shared_ptr logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {isolatedUid, 200}, + {"tag1", "tag2"}, field1, field2); + + processor->OnLogEvent(logEvent.get()); + + const vector* actualFieldValues = &logEvent->getValues(); + EXPECT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index c10703c36b98..6bde79f52e33 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -13,13 +13,15 @@ // limitations under the License. #include "src/anomaly/AnomalyTracker.h" -#include "../metrics/metrics_test_helper.h" #include #include #include + #include +#include "tests/statsd_test_util.h" + using namespace testing; using android::sp; using std::set; diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp index c2cfb371d329..a21dc8717776 100644 --- a/cmds/statsd/tests/external/puller_util_test.cpp +++ b/cmds/statsd/tests/external/puller_util_test.cpp @@ -21,9 +21,9 @@ #include #include "../metrics/metrics_test_helper.h" +#include "FieldValue.h" #include "annotations.h" #include "stats_event.h" -#include "statslog_statsdtest.h" #include "tests/statsd_test_util.h" #ifdef __ANDROID__ @@ -33,207 +33,371 @@ namespace os { namespace statsd { using namespace testing; -using std::make_shared; using std::shared_ptr; using std::vector; -using testing::Contains; /* * Test merge isolated and host uid */ namespace { -int uidAtomTagId = util::CPU_CLUSTER_TIME; -const vector uidAdditiveFields = {3}; -int nonUidAtomTagId = util::SYSTEM_UPTIME; -int timestamp = 1234; -int isolatedUid = 30; -int isolatedAdditiveData = 31; -int isolatedNonAdditiveData = 32; -int hostUid = 20; -int hostAdditiveData = 21; -int hostNonAdditiveData = 22; - -void extractIntoVector(vector> events, - vector>& ret) { - ret.clear(); - status_t err; - for (const auto& event : events) { - vector vec; - vec.push_back(event->GetInt(1, &err)); - vec.push_back(event->GetInt(2, &err)); - vec.push_back(event->GetInt(3, &err)); - ret.push_back(vec); - } +const int uidAtomTagId = 100; +const vector additiveFields = {3}; +const int nonUidAtomTagId = 200; +const int timestamp = 1234; +const int isolatedUid1 = 30; +const int isolatedUid2 = 40; +const int isolatedNonAdditiveData = 32; +const int isolatedAdditiveData = 31; +const int hostUid = 20; +const int hostNonAdditiveData = 22; +const int hostAdditiveData = 21; +const int attributionAtomTagId = 300; + +sp makeMockUidMap() { + return makeMockUidMapForOneHost(hostUid, {isolatedUid1, isolatedUid2}); } -std::shared_ptr makeUidLogEvent(uint64_t timestampNs, int uid, int data1, int data2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, uidAtomTagId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - - AStatsEvent_writeInt32(statsEvent, uid); - AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true); - AStatsEvent_writeInt32(statsEvent, data1); - AStatsEvent_writeInt32(statsEvent, data2); +} // anonymous namespace - std::shared_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; +TEST(PullerUtilTest, MergeNoDimension) { + vector> data = { + // 30->22->31 + makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData, + isolatedAdditiveData), + + // 20->22->21 + makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, + hostAdditiveData), + }; + + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); + + ASSERT_EQ(1, (int)data.size()); + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(2).mValue.int_value); } -std::shared_ptr makeNonUidAtomLogEvent(uint64_t timestampNs, int data1) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, nonUidAtomTagId); - AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, data1); - - std::shared_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0); - parseStatsEventToLogEvent(statsEvent, logEvent.get()); - return logEvent; +TEST(PullerUtilTest, MergeWithDimension) { + vector> data = { + // 30->32->31 + makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, + isolatedAdditiveData), + + // 20->32->21 + makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, + hostAdditiveData), + + // 20->22->21 + makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, + hostAdditiveData), + }; + + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); + + ASSERT_EQ(2, (int)data.size()); + + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); + + actualFieldValues = &data[1]->getValues(); + ASSERT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); } -} // anonymous namespace - -TEST(puller_util, MergeNoDimension) { - vector> inputData; - - // 30->22->31 - inputData.push_back( - makeUidLogEvent(timestamp, isolatedUid, hostNonAdditiveData, isolatedAdditiveData)); - - // 20->22->21 - inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); - - sp uidMap = new NaggyMock(); - EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); - EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); - mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); - - vector> actual; - extractIntoVector(inputData, actual); - vector expectedV1 = {20, 22, 52}; - EXPECT_EQ(1, (int)actual.size()); - EXPECT_THAT(actual, Contains(expectedV1)); +TEST(PullerUtilTest, NoMergeHostUidOnly) { + vector> data = { + // 20->32->31 + makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, + isolatedAdditiveData), + + // 20->22->21 + makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData, + hostAdditiveData), + }; + + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); + + ASSERT_EQ(2, (int)data.size()); + + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); + + actualFieldValues = &data[1]->getValues(); + ASSERT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); } -TEST(puller_util, MergeWithDimension) { - vector> inputData; - - // 30->32->31 - inputData.push_back( - makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData)); +TEST(PullerUtilTest, IsolatedUidOnly) { + vector> data = { + // 30->32->31 + makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, + isolatedAdditiveData), - // 20->32->21 - inputData.push_back( - makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData)); + // 30->22->21 + makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData, + hostAdditiveData), + }; - // 20->22->21 - inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); - - sp uidMap = new NaggyMock(); - EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); - EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); - mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); - - vector> actual; - extractIntoVector(inputData, actual); - vector expectedV1 = {20, 22, 21}; - vector expectedV2 = {20, 32, 52}; - EXPECT_EQ(2, (int)actual.size()); - EXPECT_THAT(actual, Contains(expectedV1)); - EXPECT_THAT(actual, Contains(expectedV2)); -} + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); -TEST(puller_util, NoMergeHostUidOnly) { - vector> inputData; + ASSERT_EQ(2, (int)data.size()); // 20->32->31 - inputData.push_back( - makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData)); + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value); // 20->22->21 - inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); - - sp uidMap = new NaggyMock(); - EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); - EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); - mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); - - // 20->32->31 - // 20->22->21 - vector> actual; - extractIntoVector(inputData, actual); - vector expectedV1 = {20, 32, 31}; - vector expectedV2 = {20, 22, 21}; - EXPECT_EQ(2, (int)actual.size()); - EXPECT_THAT(actual, Contains(expectedV1)); - EXPECT_THAT(actual, Contains(expectedV2)); + actualFieldValues = &data[1]->getValues(); + ASSERT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value); } -TEST(puller_util, IsolatedUidOnly) { - vector> inputData; +TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUid) { + vector> data = { + // 30->32->31 + makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData, + isolatedAdditiveData), - // 30->32->31 - inputData.push_back( - makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, isolatedAdditiveData)); + // 31->32->21 + makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid2, isolatedNonAdditiveData, + hostAdditiveData), - // 30->22->21 - inputData.push_back(makeUidLogEvent(timestamp, hostUid, hostNonAdditiveData, hostAdditiveData)); + // 20->32->21 + makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData, + hostAdditiveData), + }; - sp uidMap = new NaggyMock(); - EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); - EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid))).WillRepeatedly(ReturnArg<0>()); - mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields); - // 20->32->31 - // 20->22->21 - vector> actual; - extractIntoVector(inputData, actual); - vector expectedV1 = {20, 32, 31}; - vector expectedV2 = {20, 22, 21}; - EXPECT_EQ(2, (int)actual.size()); - EXPECT_THAT(actual, Contains(expectedV1)); - EXPECT_THAT(actual, Contains(expectedV2)); + ASSERT_EQ(1, (int)data.size()); + + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(3, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData, + actualFieldValues->at(2).mValue.int_value); } -TEST(puller_util, MultipleIsolatedUidToOneHostUid) { - vector> inputData; +TEST(PullerUtilTest, NoNeedToMerge) { + vector> data = { + // 32->31 + CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, isolatedNonAdditiveData, + isolatedAdditiveData), - // 30->32->31 - inputData.push_back( - makeUidLogEvent(timestamp, isolatedUid, isolatedNonAdditiveData, isolatedAdditiveData)); + // 22->21 + CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, hostNonAdditiveData, + hostAdditiveData), - // 31->32->21 - inputData.push_back( - makeUidLogEvent(timestamp, isolatedUid + 1, isolatedNonAdditiveData, hostAdditiveData)); + }; - // 20->32->21 - inputData.push_back( - makeUidLogEvent(timestamp, hostUid, isolatedNonAdditiveData, hostAdditiveData)); + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, nonUidAtomTagId, {} /*no additive fields*/); - sp uidMap = new NaggyMock(); - EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid)); - mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId, uidAdditiveFields); + ASSERT_EQ(2, (int)data.size()); - vector> actual; - extractIntoVector(inputData, actual); - vector expectedV1 = {20, 32, 73}; - EXPECT_EQ(1, (int)actual.size()); - EXPECT_THAT(actual, Contains(expectedV1)); + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(2, actualFieldValues->size()); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(1).mValue.int_value); + + actualFieldValues = &data[1]->getValues(); + ASSERT_EQ(2, actualFieldValues->size()); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ(hostAdditiveData, actualFieldValues->at(1).mValue.int_value); } -TEST(puller_util, NoNeedToMerge) { - vector> inputData; +TEST(PullerUtilTest, MergeNoDimensionAttributionChain) { + vector> data = { + // 30->tag1->400->tag2->22->31 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, + {"tag1", "tag2"}, hostNonAdditiveData, isolatedAdditiveData), + + // 20->tag1->400->tag2->22->21 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, + {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), + }; + + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); + + ASSERT_EQ(1, (int)data.size()); + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(5).mValue.int_value); +} - // 32 - inputData.push_back(makeNonUidAtomLogEvent(timestamp, isolatedNonAdditiveData)); +TEST(PullerUtilTest, MergeWithDimensionAttributionChain) { + vector> data = { + // 200->tag1->30->tag2->32->31 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, isolatedUid1}, + {"tag1", "tag2"}, isolatedNonAdditiveData, + isolatedAdditiveData), + + // 200->tag1->20->tag2->32->21 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid}, + {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), + + // 200->tag1->20->tag2->22->21 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid}, + {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), + }; + + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); + + ASSERT_EQ(2, (int)data.size()); + + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); + + actualFieldValues = &data[1]->getValues(); + ASSERT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); +} - // 22 - inputData.push_back(makeNonUidAtomLogEvent(timestamp, hostNonAdditiveData)); +TEST(PullerUtilTest, NoMergeHostUidOnlyAttributionChain) { + vector> data = { + // 20->tag1->400->tag2->32->31 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, + {"tag1", "tag2"}, isolatedNonAdditiveData, + isolatedAdditiveData), + + // 20->tag1->400->tag2->22->21 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, + {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), + }; + + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); + + ASSERT_EQ(2, (int)data.size()); + + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); + + actualFieldValues = &data[1]->getValues(); + ASSERT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); +} - sp uidMap = new NaggyMock(); - mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId, {} /*no additive fields*/); +TEST(PullerUtilTest, IsolatedUidOnlyAttributionChain) { + vector> data = { + // 30->tag1->400->tag2->32->31 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, + {"tag1", "tag2"}, isolatedNonAdditiveData, + isolatedAdditiveData), + + // 30->tag1->400->tag2->22->21 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, + {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData), + }; + + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); + + ASSERT_EQ(2, (int)data.size()); + + // 20->tag1->400->tag2->32->31 + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value); + + // 20->tag1->400->tag2->22->21 + actualFieldValues = &data[1]->getValues(); + ASSERT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value); +} - EXPECT_EQ(2, (int)inputData.size()); +TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUidAttributionChain) { + vector> data = { + // 30->tag1->400->tag2->32->31 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400}, + {"tag1", "tag2"}, isolatedNonAdditiveData, + isolatedAdditiveData), + + // 31->tag1->400->tag2->32->21 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid2, 400}, + {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), + + // 20->tag1->400->tag2->32->21 + makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400}, + {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData), + }; + + sp uidMap = makeMockUidMap(); + mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields); + + ASSERT_EQ(1, (int)data.size()); + + const vector* actualFieldValues = &data[0]->getValues(); + ASSERT_EQ(6, actualFieldValues->size()); + EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value); + EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value); + EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value); + EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value); + EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value); + EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData, + actualFieldValues->at(5).mValue.int_value); } } // namespace statsd diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h index be410b10d43b..46ef0f613bbe 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -48,12 +48,6 @@ public: void(const ConfigKey& configKey, wp provider)); }; -class MockUidMap : public UidMap { - public: - MOCK_CONST_METHOD1(getHostUidOrSelf, int(int uid)); - MOCK_CONST_METHOD1(getAppUid, std::set(const string& package)); -}; - HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value); MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value); diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 7216e1d8cc8e..2315fd7243bc 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -563,6 +563,48 @@ shared_ptr CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) { return logEvent; } +shared_ptr makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1, + int data2) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true); + AStatsEvent_writeInt32(statsEvent, data1); + AStatsEvent_writeInt32(statsEvent, data2); + + shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); + return logEvent; +} + +shared_ptr makeAttributionLogEvent(int atomId, int64_t eventTimeNs, + const vector& uids, const vector& tags, + int data1, int data2) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); + + writeAttribution(statsEvent, uids, tags); + AStatsEvent_writeInt32(statsEvent, data1); + AStatsEvent_writeInt32(statsEvent, data2); + + shared_ptr logEvent = std::make_shared(/*uid=*/0, /*pid=*/0); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); + return logEvent; +} + +sp makeMockUidMapForOneHost(int hostUid, const vector& isolatedUids) { + sp uidMap = new NaggyMock(); + EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(ReturnArg<0>()); + for (const int isolatedUid : isolatedUids) { + EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid)); + } + + return uidMap; +} + std::unique_ptr CreateScreenStateChangedEvent( uint64_t timestampNs, const android::view::DisplayStateEnum state) { AStatsEvent* statsEvent = AStatsEvent_obtain(); @@ -878,8 +920,7 @@ std::unique_ptr CreateOverlayStateChangedEvent(int64_t timestampNs, co sp CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config, const ConfigKey& key, const shared_ptr& puller, - const int32_t atomTag) { - sp uidMap = new UidMap(); + const int32_t atomTag, const sp uidMap) { sp pullerManager = new StatsPullerManager(); if (puller != nullptr) { pullerManager->RegisterPullAtomCallback(/*uid=*/0, atomTag, NS_PER_SEC, NS_PER_SEC * 10, {}, diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 4d68ea2ecb79..dc012c5381eb 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" @@ -24,6 +25,7 @@ #include "src/StatsLogProcessor.h" #include "src/hash.h" #include "src/logd/LogEvent.h" +#include "src/packages/UidMap.h" #include "src/stats_log_util.h" #include "stats_event.h" #include "statslog_statsdtest.h" @@ -32,6 +34,7 @@ namespace android { namespace os { namespace statsd { +using namespace testing; using ::aidl::android::os::BnPullAtomCallback; using ::aidl::android::os::IPullAtomCallback; using ::aidl::android::os::IPullAtomResultReceiver; @@ -44,6 +47,12 @@ const int UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED; enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE }; +class MockUidMap : public UidMap { +public: + MOCK_METHOD(int, getHostUidOrSelf, (int uid), (const)); + MOCK_METHOD(std::set, getAppUid, (const string& package), (const)); +}; + // Converts a ProtoOutputStream to a StatsLogReport proto. StatsLogReport outputStreamToProto(ProtoOutputStream* proto); @@ -212,6 +221,15 @@ std::shared_ptr CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs); +std::shared_ptr makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1, + int data2); + +std::shared_ptr makeAttributionLogEvent(int atomId, int64_t eventTimeNs, + const vector& uids, + const vector& tags, int data1, int data2); + +sp makeMockUidMapForOneHost(int hostUid, const vector& isolatedUids); + // Create log event for screen state changed. std::unique_ptr CreateScreenStateChangedEvent( uint64_t timestampNs, const android::view::DisplayStateEnum state); @@ -293,7 +311,8 @@ std::unique_ptr CreateOverlayStateChangedEvent(int64_t timestampNs, co sp CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config, const ConfigKey& key, const shared_ptr& puller = nullptr, - const int32_t atomTag = 0 /*for puller only*/); + const int32_t atomTag = 0 /*for puller only*/, + const sp = new UidMap()); // Util function to sort the log events by timestamp. void sortLogEventsByTimestamp(std::vector> *events); -- GitLab From fa7a0252b096cdb6d9b2758a599f36dc0e87f29a Mon Sep 17 00:00:00 2001 From: jiabin Date: Tue, 28 Apr 2020 16:19:47 -0700 Subject: [PATCH 084/166] Reset audio port generation when having recording callback event. In RecordingActivityMonitor, compare audio device type and address to know if legacy remote submix is active, which requires querying audio patches. When there is a recording callback event, the audio patches in AudioManager may not be up to date. In that case, when getting a recording callback event, reset audio port generation if the patch handle is not found in the cached audio patches. Test: repro steps in the bug Bug: 144063329 Change-Id: Ife5c9a65243d2e85e4cd9480e3b415f8bfddc6f5 --- media/java/android/media/AudioSystem.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 2d00010f51ee..da52cfef6bc6 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -686,6 +686,23 @@ public class AudioSystem String effectName = effects.length == 0 ? "None" : effects[0].name; if (cb != null) { + ArrayList audioPatches = new ArrayList<>(); + if (AudioManager.listAudioPatches(audioPatches) == AudioManager.SUCCESS) { + boolean patchFound = false; + int patchHandle = recordingFormat[6]; + for (AudioPatch patch : audioPatches) { + if (patch.id() == patchHandle) { + patchFound = true; + break; + } + } + if (!patchFound) { + // The cached audio patches in AudioManager is not up-to-date. + // Reset audio port generation to ensure callback side can + // get up-to-date audio port information. + AudioManager.resetAudioPortGeneration(); + } + } // TODO receive package name from native cb.onRecordingConfigurationChanged(event, riid, uid, session, source, portId, silenced, recordingFormat, clientEffects, effects, activeSource, ""); -- GitLab From 3051873947fa70a7aa6315076f665236692099a2 Mon Sep 17 00:00:00 2001 From: thiruram Date: Tue, 28 Apr 2020 12:48:44 -0700 Subject: [PATCH 085/166] Migrates Bouncer dismiss events from Tron to WW. Test: Manual(lunch coral-userdebug && make -j100) Bug: 147509048 Change-Id: I27139b28c32281d2cd0ec04c94cf00d8360acbd1 --- .../keyguard/KeyguardSecurityContainer.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 9cfcc52134ce..b99fb057ee65 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -56,6 +56,9 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; @@ -95,6 +98,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe // How much to scale the default slop by, to avoid accidental drags. private static final float SLOP_SCALE = 4f; + private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl(); + private KeyguardSecurityModel mSecurityModel; private LockPatternUtils mLockPatternUtils; @@ -178,6 +183,44 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe public void onCancelClicked(); } + @VisibleForTesting + public enum BouncerUiEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Default UiEvent used for variable initialization.") + UNKNOWN(0), + + @UiEvent(doc = "Bouncer is dismissed using extended security access.") + BOUNCER_DISMISS_EXTENDED_ACCESS(413), + + @UiEvent(doc = "Bouncer is dismissed using biometric.") + BOUNCER_DISMISS_BIOMETRIC(414), + + @UiEvent(doc = "Bouncer is dismissed without security access.") + BOUNCER_DISMISS_NONE_SECURITY(415), + + @UiEvent(doc = "Bouncer is dismissed using password security.") + BOUNCER_DISMISS_PASSWORD(416), + + @UiEvent(doc = "Bouncer is dismissed using sim security access.") + BOUNCER_DISMISS_SIM(417), + + @UiEvent(doc = "Bouncer is successfully unlocked using password.") + BOUNCER_PASSWORD_SUCCESS(418), + + @UiEvent(doc = "An attempt to unlock bouncer using password has failed.") + BOUNCER_PASSWORD_FAILURE(419); + + private final int mId; + + BouncerUiEvent(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } + public KeyguardSecurityContainer(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -574,17 +617,21 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe boolean finish = false; boolean strongAuth = false; int eventSubtype = -1; + BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN; if (mUpdateMonitor.getUserHasTrust(targetUserId)) { finish = true; eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS; } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) { finish = true; eventSubtype = BOUNCER_DISMISS_BIOMETRIC; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC; } else if (SecurityMode.None == mCurrentSecuritySelection) { SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); if (SecurityMode.None == securityMode) { finish = true; // no security required eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY; } else { showSecurityScreen(securityMode); // switch to the alternate security view } @@ -596,6 +643,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe strongAuth = true; finish = true; eventSubtype = BOUNCER_DISMISS_PASSWORD; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD; break; case SimPin: @@ -606,6 +654,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe KeyguardUpdateMonitor.getCurrentUser())) { finish = true; eventSubtype = BOUNCER_DISMISS_SIM; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM; } else { showSecurityScreen(securityMode); } @@ -630,6 +679,9 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype)); } + if (uiEvent != BouncerUiEvent.UNKNOWN) { + sUiEventLogger.log(uiEvent); + } if (finish) { mSecurityCallback.finish(strongAuth, targetUserId); } @@ -735,6 +787,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE)); + sUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS + : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE); } public void reset() { -- GitLab From 2501c602e227dac2536d85bce9ad67f6944eae5e Mon Sep 17 00:00:00 2001 From: Dan Sandler Date: Mon, 20 Apr 2020 23:31:40 -0400 Subject: [PATCH 086/166] Limit use of `cmd notification` to root and shell users only. Only the shell and system can use notification manager shell commands now. (Apps have APIs for that.) Also add a few new notification shell commands: list -> lists keys of active notifications get -> dumps the NotificationRecord snoozed -> lists snoozed notifications snooze (--for | --context ) unsnooze Bug: 150629389 Test: the shell commands themselves Test: atest com.android.server.notification.NotificationShellCmdTest Change-Id: Ie949821fcf1192efe8b6b3e95c9daca71347b11e --- .../NotificationManagerService.java | 4 +- .../notification/NotificationShellCmd.java | 116 +++++++++++++++++- .../NotificationShellCmdTest.java | 18 ++- 3 files changed, 134 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6c3177fe253a..fb7169b0134f 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -512,7 +512,7 @@ public class NotificationManagerService extends SystemService { private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE; private NotificationHistoryManager mHistoryManager; - private SnoozeHelper mSnoozeHelper; + protected SnoozeHelper mSnoozeHelper; private GroupHelper mGroupHelper; private int mAutoGroupAtCount; private boolean mIsTelevision; @@ -7913,7 +7913,7 @@ public class NotificationManagerService extends SystemService { void snoozeNotificationInt(String key, long duration, String snoozeCriterionId, ManagedServiceInfo listener) { String listenerName = listener == null ? null : listener.component.toShortString(); - if (duration <= 0 && snoozeCriterionId == null || key == null) { + if ((duration <= 0 && snoozeCriterionId == null) || key == null) { return; } diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java index e4a17740b0b7..da7864ba32c4 100644 --- a/services/core/java/com/android/server/notification/NotificationShellCmd.java +++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java @@ -50,6 +50,7 @@ import android.util.Slog; import java.io.PrintWriter; import java.net.URISyntaxException; import java.util.Collections; +import java.util.Date; /** * Implementation of `cmd notification` in NotificationManagerService. @@ -73,7 +74,12 @@ public class NotificationShellCmd extends ShellCommand { + " set_bubbles PACKAGE PREFERENCE (0=none 1=all 2=selected) " + "[user_id (current user if not specified)]\n" + " set_bubbles_channel PACKAGE CHANNEL_ID ALLOW " - + "[user_id (current user if not specified)]\n"; + + "[user_id (current user if not specified)]\n" + + " list\n" + + " get \n" + + " snooze --for \n" + + " unsnooze \n" + ; private static final String NOTIFY_USAGE = "usage: cmd notification post [flags] \n\n" @@ -118,6 +124,10 @@ public class NotificationShellCmd extends ShellCommand { mPm = mDirectService.getContext().getPackageManager(); } + protected boolean checkShellCommandPermission(int callingUid) { + return (callingUid == Process.ROOT_UID || callingUid == Process.SHELL_UID); + } + @Override public int onCommand(String cmd) { if (cmd == null) { @@ -140,7 +150,17 @@ public class NotificationShellCmd extends ShellCommand { } finally { Binder.restoreCallingIdentity(identity); } + final PrintWriter pw = getOutPrintWriter(); + + if (!checkShellCommandPermission(callingUid)) { + Slog.e(TAG, "error: permission denied: callingUid=" + + callingUid + " callingPackage=" + callingPackage); + pw.println("error: permission denied: callingUid=" + + callingUid + " callingPackage=" + callingPackage); + return 255; + } + try { switch (cmd.replace('-', '_')) { case "set_dnd": { @@ -316,6 +336,100 @@ public class NotificationShellCmd extends ShellCommand { case "notify": doNotify(pw, callingPackage, callingUid); break; + case "list": + for (String key : mDirectService.mNotificationsByKey.keySet()) { + pw.println(key); + } + break; + case "get": { + final String key = getNextArgRequired(); + final NotificationRecord nr = mDirectService.getNotificationRecord(key); + if (nr != null) { + nr.dump(pw, "", mDirectService.getContext(), false); + } else { + pw.println("error: no active notification matching key: " + key); + return 1; + } + break; + } + case "snoozed": { + final StringBuilder sb = new StringBuilder(); + final SnoozeHelper sh = mDirectService.mSnoozeHelper; + for (NotificationRecord nr : sh.getSnoozed()) { + final String pkg = nr.getSbn().getPackageName(); + final String key = nr.getKey(); + pw.println(key + " snoozed, time=" + + sh.getSnoozeTimeForUnpostedNotification( + nr.getUserId(), pkg, key) + + " context=" + + sh.getSnoozeContextForUnpostedNotification( + nr.getUserId(), pkg, key)); + } + break; + } + case "unsnooze": { + boolean mute = false; + String key = getNextArgRequired(); + if ("--mute".equals(key)) { + mute = true; + key = getNextArgRequired(); + } + if (null != mDirectService.mSnoozeHelper.getNotification(key)) { + pw.println("unsnoozing: " + key); + mDirectService.unsnoozeNotificationInt(key, null, mute); + } else { + pw.println("error: no snoozed otification matching key: " + key); + return 1; + } + break; + } + case "snooze": { + String subflag = getNextArg(); + if (subflag == null) { + subflag = "help"; + } else if (subflag.startsWith("--")) { + subflag = subflag.substring(2); + } + String flagarg = getNextArg(); + String key = getNextArg(); + if (key == null) subflag = "help"; + String criterion = null; + long duration = 0; + switch (subflag) { + case "context": + case "condition": + case "criterion": + criterion = flagarg; + break; + case "until": + case "for": + case "duration": + duration = Long.parseLong(flagarg); + break; + default: + pw.println("usage: cmd notification snooze (--for | " + + "--context ) "); + return 1; + } + if (null == mDirectService.getNotificationRecord(key)) { + pw.println("error: no notification matching key: " + key); + return 1; + } + if (duration > 0 || criterion != null) { + if (duration > 0) { + pw.println(String.format("snoozing <%s> until time: %s", key, + new Date(System.currentTimeMillis() + duration))); + } else { + pw.println(String.format("snoozing <%s> until criterion: %s", key, + criterion)); + } + mDirectService.snoozeNotificationInt(key, duration, criterion, null); + } else { + pw.println("error: invalid value for --" + subflag + ": " + flagarg); + return 1; + } + break; + } default: return handleDefaultCommands(cmd); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java index 0d44318e4aaa..00061931b18d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java @@ -98,7 +98,8 @@ public class NotificationShellCmdTest extends UiServiceTestCase { } private void doCmd(String... args) { - new NotificationShellCmd(mMockService) + System.out.println("Running command: " + String.join(" ", args)); + new TestNotificationShellCmd(mMockService) .exec(mBinder, in, out, err, args, mCallback, mResultReceiver); } @@ -267,4 +268,19 @@ public class NotificationShellCmdTest extends UiServiceTestCase { } } + + /** + * Version of NotificationShellCmd that allows this atest to work properly despite coming in + * from the wrong uid. + */ + private final class TestNotificationShellCmd extends NotificationShellCmd { + TestNotificationShellCmd(NotificationManagerService service) { + super(service); + } + + @Override + protected boolean checkShellCommandPermission(int callingUid) { + return true; + } + } } -- GitLab From b4efd1b0658690098f124a4f58d212d643ed0c8b Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Wed, 29 Apr 2020 10:52:50 -0700 Subject: [PATCH 087/166] Add bounds check when setting package usage Fixes: 149837263 Test: atest PackageUserStateTest#testPackageUseReasons Change-Id: I6593b5a3a41768c41aca6e48ad8308065f4c8470 --- .../pm/pkg/PackageStateUnserialized.java | 7 ++- .../server/pm/PackageUserStateTest.java | 54 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java index edb6d65bd96f..05879ec9545e 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java +++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java @@ -21,7 +21,6 @@ import static java.util.Collections.emptyList; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManager; -import android.content.pm.PackageParser; import android.content.pm.SharedLibraryInfo; import com.android.internal.util.DataClass; @@ -64,6 +63,12 @@ public class PackageStateUnserialized { } public PackageStateUnserialized setLastPackageUsageTimeInMills(int reason, long time) { + if (reason < 0) { + return this; + } + if (reason >= PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT) { + return this; + } getLastPackageUsageTimeInMills()[reason] = time; return this; } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java index 7c3efeb01f48..1cfbad93c2e5 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import android.content.pm.PackageManager; @@ -32,6 +33,8 @@ import android.util.ArraySet; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.pm.pkg.PackageStateUnserialized; + import org.junit.Test; import org.junit.runner.RunWith; @@ -300,4 +303,55 @@ public class PackageUserStateTest { // Everything is different assertThat(params1.equals(params2), is(false)); } + + /** + * Test fix for b/149772100. + */ + private static void assertLastPackageUsageUnset( + PackageStateUnserialized state) throws Exception { + for (int i = state.getLastPackageUsageTimeInMills().length - 1; i >= 0; --i) { + assertEquals(0L, state.getLastPackageUsageTimeInMills()[i]); + } + } + private static void assertLastPackageUsageSet( + PackageStateUnserialized state, int reason, long value) throws Exception { + for (int i = state.getLastPackageUsageTimeInMills().length - 1; i >= 0; --i) { + if (i == reason) { + assertEquals(value, state.getLastPackageUsageTimeInMills()[i]); + } else { + assertEquals(0L, state.getLastPackageUsageTimeInMills()[i]); + } + } + } + @Test + public void testPackageUseReasons() throws Exception { + final PackageStateUnserialized testState1 = new PackageStateUnserialized(); + testState1.setLastPackageUsageTimeInMills(-1, 10L); + assertLastPackageUsageUnset(testState1); + + final PackageStateUnserialized testState2 = new PackageStateUnserialized(); + testState2.setLastPackageUsageTimeInMills( + PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT, 20L); + assertLastPackageUsageUnset(testState2); + + final PackageStateUnserialized testState3 = new PackageStateUnserialized(); + testState3.setLastPackageUsageTimeInMills(Integer.MAX_VALUE, 30L); + assertLastPackageUsageUnset(testState3); + + final PackageStateUnserialized testState4 = new PackageStateUnserialized(); + testState4.setLastPackageUsageTimeInMills(0, 40L); + assertLastPackageUsageSet(testState4, 0, 40L); + + final PackageStateUnserialized testState5 = new PackageStateUnserialized(); + testState5.setLastPackageUsageTimeInMills( + PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER, 50L); + assertLastPackageUsageSet( + testState5, PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER, 50L); + + final PackageStateUnserialized testState6 = new PackageStateUnserialized(); + testState6.setLastPackageUsageTimeInMills( + PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L); + assertLastPackageUsageSet( + testState6, PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L); + } } -- GitLab From 36ecb3de9d3c7a79ba463bf3b713e1eecfe85f4f Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Thu, 30 Apr 2020 01:12:18 +0800 Subject: [PATCH 088/166] Public logSettingsTileClick for log some widget in Settings Bug: 137559984 Test: robotest Change-Id: I0b3d425556df0d1a717e7a156d05d0157653fc01 --- .../MetricsFeatureProvider.java | 7 +++++- .../MetricsFeatureProviderTest.java | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java index 7ef080178a2f..bd0b9e93b09d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java @@ -185,7 +185,12 @@ public class MetricsFeatureProvider { sourceMetricsCategory); } - private boolean logSettingsTileClick(String logKey, int sourceMetricsCategory) { + /** + * Logs an event when the setting key is clicked. + * + * @return true if the key is loggable, otherwise false + */ + public boolean logSettingsTileClick(String logKey, int sourceMetricsCategory) { if (TextUtils.isEmpty(logKey)) { // Not loggable return false; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java index 204a93333d81..0e2a3cbbbd75 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java @@ -218,4 +218,29 @@ public class MetricsFeatureProviderTest { assertThat(mProvider.getAttribution(activity)).isEqualTo(100); } + + @Test + public void logSettingsTileClick_hasKey_shouldLog() { + final String key = "abc"; + final boolean loggable = mProvider.logSettingsTileClick(key, + MetricsEvent.SETTINGS_GESTURES); + + assertThat(loggable).isTrue(); + verify(mLogWriter).action( + MetricsEvent.SETTINGS_GESTURES, + MetricsEvent.ACTION_SETTINGS_TILE_CLICK, + SettingsEnums.PAGE_UNKNOWN, + key, + 0); + } + + @Test + public void logSettingsTileClick_keyEmpty_shouldNotLog() { + final String key = ""; + boolean loggable = mProvider.logSettingsTileClick(key, + MetricsEvent.SETTINGS_GESTURES); + + assertThat(loggable).isFalse(); + verifyNoMoreInteractions(mLogWriter); + } } -- GitLab From 44d5ad8a57490a8c74b6f9a83be72f51c5525357 Mon Sep 17 00:00:00 2001 From: Curtis Belmonte Date: Mon, 27 Apr 2020 16:08:59 -0700 Subject: [PATCH 089/166] Condense landscape layout of auth credential password view Changes the layout of the biometric prompt auth credential password when in landscape orientation, in order to prevent the keyboard from obscuring the password/PIN entry or error text views. Specifically: - Removes the icon from the layout - Reduces/removes padding and spacers Test: CTS Verifier > Biometric Tests > 4a: Credential, Cipher Test: Biometric support demo app Fixes: 155041280 Change-Id: I8d576f76d2e90b1830a22aebbfc12559634b6b75 --- .../auth_credential_password_view.xml | 61 +++++++++++++++++++ packages/SystemUI/res/values/styles.xml | 4 +- .../biometrics/AuthCredentialView.java | 19 +++--- 3 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 packages/SystemUI/res/layout-land/auth_credential_password_view.xml diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml new file mode 100644 index 000000000000..d89f329039c6 --- /dev/null +++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index f67bb6b79f37..57d2040bed57 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -258,7 +258,7 @@ @@ -278,7 +278,7 @@