diff options
132 files changed, 2500 insertions, 814 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 812ca4aefb9b..8d64661ef8a6 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5112,6 +5112,7 @@ public final class ActivityThread extends ClientTransactionHandler { } } r.setState(ON_DESTROY); + mLastReportedWindowingMode.remove(r.activity.getActivityToken()); } schedulePurgeIdler(); // updatePendingActivityConfiguration() reads from mActivities to update diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 98a23f2b0075..3cb6293f0706 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -105,7 +105,8 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd public ActivityView( @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, boolean singleTaskInstance, boolean usePublicVirtualDisplay) { - this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, false); + this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, + false /* disableSurfaceViewBackgroundLayer */); } /** @hide */ @@ -113,12 +114,22 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, boolean singleTaskInstance, boolean usePublicVirtualDisplay, boolean disableSurfaceViewBackgroundLayer) { + this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, + disableSurfaceViewBackgroundLayer, false /* useTrustedDisplay */); + } + + // TODO(b/162901735): Refactor ActivityView with Builder + /** @hide */ + public ActivityView( + @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, + boolean singleTaskInstance, boolean usePublicVirtualDisplay, + boolean disableSurfaceViewBackgroundLayer, boolean useTrustedDisplay) { super(context, attrs, defStyle); if (useTaskOrganizer()) { mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this); } else { mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, singleTaskInstance, - usePublicVirtualDisplay); + usePublicVirtualDisplay, useTrustedDisplay); } mSurfaceView = new SurfaceView(context, null, 0, 0, disableSurfaceViewBackgroundLayer); // Since ActivityView#getAlpha has been overridden, we should use parent class's alpha diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index d0fd92294979..c51a84649a07 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1914,10 +1914,8 @@ class ContextImpl extends Context { @Override public Object getSystemService(String name) { if (vmIncorrectContextUseEnabled()) { - // We may override this API from outer context. - final boolean isUiContext = isUiContext() || isOuterUiContext(); // Check incorrect Context usage. - if (isUiComponent(name) && !isUiContext) { + if (isUiComponent(name) && !isSelfOrOuterUiContext()) { final String errorMessage = "Tried to access visual service " + SystemServiceRegistry.getSystemServiceClassName(name) + " from a non-visual Context:" + getOuterContext(); @@ -1934,15 +1932,17 @@ class ContextImpl extends Context { return SystemServiceRegistry.getSystemService(this, name); } - private boolean isOuterUiContext() { - return getOuterContext() != null && getOuterContext().isUiContext(); - } - @Override public String getSystemServiceName(Class<?> serviceClass) { return SystemServiceRegistry.getSystemServiceName(serviceClass); } + // TODO(b/149463653): check if we still need this method after migrating IMS to WindowContext. + private boolean isSelfOrOuterUiContext() { + // We may override outer context's isUiContext + return isUiContext() || getOuterContext() != null && getOuterContext().isUiContext(); + } + /** @hide */ @Override public boolean isUiContext() { @@ -2389,7 +2389,6 @@ class ContextImpl extends Context { context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId, overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(), mResources.getLoaders())); - context.mIsUiContext = isUiContext() || isOuterUiContext(); return context; } @@ -2409,6 +2408,11 @@ class ContextImpl extends Context { mResources.getLoaders())); context.mDisplay = display; context.mIsAssociatedWithDisplay = true; + // Note that even if a display context is derived from an UI context, it should not be + // treated as UI context because it does not handle configuration changes from the server + // side. If the context does need to handle configuration changes, please use + // Context#createWindowContext(int, Bundle). + context.mIsUiContext = false; return context; } @@ -2494,9 +2498,9 @@ class ContextImpl extends Context { @Override public Display getDisplay() { - if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay) { + if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay && !isSelfOrOuterUiContext()) { throw new UnsupportedOperationException("Tried to obtain display from a Context not " - + "associated with one. Only visual Contexts (such as Activity or one created " + + "associated with one. Only visual Contexts (such as Activity or one created " + "with Context#createWindowContext) or ones created with " + "Context#createDisplayContext are associated with displays. Other types of " + "Contexts are typically related to background entities and may return an " @@ -2770,6 +2774,7 @@ class ContextImpl extends Context { mDisplay = container.mDisplay; mIsAssociatedWithDisplay = container.mIsAssociatedWithDisplay; mIsSystemOrSystemUiContext = container.mIsSystemOrSystemUiContext; + mIsUiContext = container.isSelfOrOuterUiContext(); } else { mBasePackageName = packageInfo.mPackageName; ApplicationInfo ainfo = packageInfo.getApplicationInfo(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 2abe9cf9fce5..f98e26338063 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -684,4 +684,14 @@ interface IActivityManager { * Kills uid with the reason of permission change. */ void killUidForPermissionChange(int appId, int userId, String reason); + + /** + * Control the app freezer state. Returns true in case of success, false if the operation + * didn't succeed (for example, when the app freezer isn't supported). + * Handling the freezer state via this method is reentrant, that is it can be + * disabled and re-enabled multiple times in parallel. As long as there's a 1:1 disable to + * enable match, the freezer is re-enabled at last enable only. + * @param enable set it to true to enable the app freezer, false to disable it. + */ + boolean enableAppFreezer(in boolean enable); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b95a402013ec..af36260fedf2 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -207,7 +207,7 @@ public class Notification implements Parcelable * <p> * Avoids spamming the system with overly large strings such as full e-mails. */ - private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024; + private static final int MAX_CHARSEQUENCE_LENGTH = 1024; /** * Maximum entries of reply text that are accepted by Builder and friends. @@ -7821,7 +7821,7 @@ public class Notification implements Parcelable */ public Message(@NonNull CharSequence text, long timestamp, @Nullable Person sender, boolean remoteInputHistory) { - mText = text; + mText = safeCharSequence(text); mTimestamp = timestamp; mSender = sender; mRemoteInputHistory = remoteInputHistory; @@ -7935,7 +7935,7 @@ public class Notification implements Parcelable bundle.putLong(KEY_TIMESTAMP, mTimestamp); if (mSender != null) { // Legacy listeners need this - bundle.putCharSequence(KEY_SENDER, mSender.getName()); + bundle.putCharSequence(KEY_SENDER, safeCharSequence(mSender.getName())); bundle.putParcelable(KEY_SENDER_PERSON, mSender); } if (mDataMimeType != null) { diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index f3f00e50715b..f99d4e937f92 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -176,6 +176,13 @@ public class TaskInfo { */ public boolean isResizeable; + /** + * Screen orientation set by {@link #baseActivity} via + * {@link Activity#setRequestedOrientation(int)}. + * @hide + */ + public @ActivityInfo.ScreenOrientation int requestedOrientation; + TaskInfo() { // Do nothing } @@ -247,6 +254,7 @@ public class TaskInfo { ? ActivityInfo.CREATOR.createFromParcel(source) : null; isResizeable = source.readBoolean(); + requestedOrientation = source.readInt(); } /** @@ -297,6 +305,7 @@ public class TaskInfo { topActivityInfo.writeToParcel(dest, flags); } dest.writeBoolean(isResizeable); + dest.writeInt(requestedOrientation); } @Override @@ -315,6 +324,7 @@ public class TaskInfo { + " token=" + token + " topActivityType=" + topActivityType + " pictureInPictureParams=" + pictureInPictureParams - + " topActivityInfo=" + topActivityInfo; + + " topActivityInfo=" + topActivityInfo + + " requestedOrientation=" + requestedOrientation; } } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index fc4ccd072e75..191c4655c708 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1291,9 +1291,8 @@ public class PackageInstaller { * * @throws PackageManager.NameNotFoundException if the new owner could not be found. * @throws SecurityException if called after the session has been committed or abandoned. - * @throws SecurityException if the session does not update the original installer - * @throws SecurityException if streams opened through - * {@link #openWrite(String, long, long) are still open. + * @throws IllegalArgumentException if streams opened through + * {@link #openWrite(String, long, long) are still open. */ public void transfer(@NonNull String packageName) throws PackageManager.NameNotFoundException { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index c5a11abe1136..4f0c84e586a2 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1208,15 +1208,19 @@ public class InputMethodService extends AbstractInputMethodService { mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true); // IME layout should always be inset by navigation bar, no matter its current visibility, - // unless automotive requests it, since automotive may hide the navigation bar. + // unless automotive requests it. Automotive devices may request the navigation bar to be + // hidden when the IME shows up (controlled via config_automotiveHideNavBarForKeyboard) + // in order to maximize the visible screen real estate. When this happens, the IME window + // should animate from the bottom of the screen to reduce the jank that happens from the + // lack of synchronization between the bottom system window and the IME window. + if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) { + mWindow.getWindow().setDecorFitsSystemWindows(false); + } mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener( (v, insets) -> v.onApplyWindowInsets( new WindowInsets.Builder(insets).setInsets( navigationBars(), - mIsAutomotive && mAutomotiveHideNavBarForKeyboard - ? android.graphics.Insets.NONE - : insets.getInsetsIgnoringVisibility(navigationBars()) - ) + insets.getInsetsIgnoringVisibility(navigationBars())) .build())); // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index 683993f762c0..0185ba444ca4 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.util.Log; import android.util.SparseIntArray; @@ -255,7 +256,12 @@ public final class BinderProxy implements IBinder { // out of system_server to all processes hosting binder objects it holds a reference to; // since some of those processes might be frozen, we don't want to block here // forever. Disable the freezer. - Process.enableFreezer(false); + try { + ActivityManager.getService().enableAppFreezer(false); + } catch (RemoteException e) { + Log.e(Binder.TAG, "RemoteException while disabling app freezer"); + } + for (WeakReference<BinderProxy> weakRef : proxiesToQuery) { BinderProxy bp = weakRef.get(); String key; @@ -278,7 +284,11 @@ public final class BinderProxy implements IBinder { counts.put(key, i + 1); } } - Process.enableFreezer(true); + try { + ActivityManager.getService().enableAppFreezer(true); + } catch (RemoteException e) { + Log.e(Binder.TAG, "RemoteException while re-enabling app freezer"); + } Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray( new Map.Entry[counts.size()]); diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 49a1cb588a3e..bde332792e18 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1021,6 +1021,18 @@ public class Build { /** * R. + * + * <p>Applications targeting this or a later release will get these new changes in behavior. + * For more information about this release, see the + * <a href="/about/versions/11">Android 11 overview</a>.</p> + * <ul> + * <li><a href="/about/versions/11/behavior-changes-all">Behavior changes: all apps</a></li> + * <li><a href="/about/versions/11/behavior-changes-11">Behavior changes: Apps targeting + * Android 11</a></li> + * <li><a href="/about/versions/11/non-sdk-11">Updates to non-SDK interface restrictions + * in Android 11</a></li> + * </ul> + * */ public static final int R = 30; } diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java index ab4bb0b9f2cd..9c0bc45a346e 100644 --- a/core/java/android/os/LocaleList.java +++ b/core/java/android/os/LocaleList.java @@ -25,6 +25,7 @@ import android.icu.util.ULocale; import com.android.internal.annotations.GuardedBy; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -151,18 +152,18 @@ public final class LocaleList implements Parcelable { /** * Creates a new {@link LocaleList}. * + * If two or more same locales are passed, the repeated locales will be dropped. * <p>For empty lists of {@link Locale} items it is better to use {@link #getEmptyLocaleList()}, * which returns a pre-constructed empty list.</p> * * @throws NullPointerException if any of the input locales is <code>null</code>. - * @throws IllegalArgumentException if any of the input locales repeat. */ public LocaleList(@NonNull Locale... list) { if (list.length == 0) { mList = sEmptyList; mStringRepresentation = ""; } else { - final Locale[] localeList = new Locale[list.length]; + final ArrayList<Locale> localeList = new ArrayList<>(); final HashSet<Locale> seenLocales = new HashSet<Locale>(); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < list.length; i++) { @@ -170,10 +171,10 @@ public final class LocaleList implements Parcelable { if (l == null) { throw new NullPointerException("list[" + i + "] is null"); } else if (seenLocales.contains(l)) { - throw new IllegalArgumentException("list[" + i + "] is a repetition"); + // Dropping duplicated locale entries. } else { final Locale localeClone = (Locale) l.clone(); - localeList[i] = localeClone; + localeList.add(localeClone); sb.append(localeClone.toLanguageTag()); if (i < list.length - 1) { sb.append(','); @@ -181,7 +182,7 @@ public final class LocaleList implements Parcelable { seenLocales.add(localeClone); } } - mList = localeList; + mList = localeList.toArray(new Locale[localeList.size()]); mStringRepresentation = sb.toString(); } } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index a4077fbee892..efea9537c4cf 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -947,7 +947,7 @@ public class Process { /** * Enable or disable the freezer. When enable == false all frozen processes are unfrozen, - * but aren't removed from the freezer. Processes can still be added or removed + * but aren't removed from the freezer. While in this state, processes can be added or removed * by using setProcessFrozen, but they won't actually be frozen until the freezer is enabled * again. If enable == true the freezer is enabled again, and all processes * in the freezer (including the ones added while the freezer was disabled) are frozen. diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index e05991b33796..a79a1cfcd64f 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -118,7 +118,7 @@ public abstract class StorageManagerInternal { * affects them. */ public abstract void onAppOpsChanged(int code, int uid, - @Nullable String packageName, int mode); + @Nullable String packageName, int mode, int previousMode); /** * Asks the StorageManager to reset all state for the provided user; this will result diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index bcf3b49c6644..4a56761bbce5 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -53,6 +53,9 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CO import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; @@ -1449,14 +1452,13 @@ public final class ViewRootImpl implements ViewParent, } // Don't lose the mode we last auto-computed. - if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + if ((attrs.softInputMode & SOFT_INPUT_MASK_ADJUST) == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode - & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); + & ~SOFT_INPUT_MASK_ADJUST) | (oldSoftInputMode & SOFT_INPUT_MASK_ADJUST); } - if ((changes & LayoutParams.SOFT_INPUT_MODE_CHANGED) != 0) { + if (mWindowAttributes.softInputMode != oldSoftInputMode) { requestFitSystemWindows(); } @@ -2073,6 +2075,7 @@ public final class ViewRootImpl implements ViewParent, final int sysUiVis = inOutParams.systemUiVisibility | inOutParams.subtreeSystemUiVisibility; final int flags = inOutParams.flags; final int type = inOutParams.type; + final int adjust = inOutParams.softInputMode & SOFT_INPUT_MASK_ADJUST; if ((inOutParams.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) { inOutParams.insetsFlags.appearance = 0; @@ -2098,12 +2101,13 @@ public final class ViewRootImpl implements ViewParent, } } + inOutParams.privateFlags &= ~PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + if ((inOutParams.privateFlags & PRIVATE_FLAG_FIT_INSETS_CONTROLLED) != 0) { return; } int types = inOutParams.getFitInsetsTypes(); - int sides = inOutParams.getFitInsetsSides(); boolean ignoreVis = inOutParams.isFitInsetsIgnoringVisibility(); if (((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0 @@ -2118,10 +2122,13 @@ public final class ViewRootImpl implements ViewParent, if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { ignoreVis = true; } else if ((types & Type.systemBars()) == Type.systemBars()) { - types |= Type.ime(); + if (adjust == SOFT_INPUT_ADJUST_RESIZE) { + types |= Type.ime(); + } else { + inOutParams.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + } } inOutParams.setFitInsetsTypes(types); - inOutParams.setFitInsetsSides(sides); inOutParams.setFitInsetsIgnoringVisibility(ignoreVis); // The fitting of insets are not really controlled by the clients, so we remove the flag. @@ -2491,8 +2498,7 @@ public final class ViewRootImpl implements ViewParent, if (mFirst || mAttachInfo.mViewVisibilityChanged) { mAttachInfo.mViewVisibilityChanged = false; - int resizeMode = mSoftInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; + int resizeMode = mSoftInputMode & SOFT_INPUT_MASK_ADJUST; // If we are in auto resize mode, then we need to determine // what mode to use now. if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { @@ -2505,11 +2511,8 @@ public final class ViewRootImpl implements ViewParent, if (resizeMode == 0) { resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; } - if ((lp.softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) { - lp.softInputMode = (lp.softInputMode & - ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) | - resizeMode; + if ((lp.softInputMode & SOFT_INPUT_MASK_ADJUST) != resizeMode) { + lp.softInputMode = (lp.softInputMode & ~SOFT_INPUT_MASK_ADJUST) | resizeMode; params = lp; } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 5c6269421a1f..2a711f6974f3 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -2033,6 +2033,12 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_FIT_INSETS_CONTROLLED = 0x10000000; /** + * Flag to indicate that the parent frame of a window should be inset by IME. + * @hide + */ + public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 0x40000000; + + /** * An internal annotation for flags that can be specified to {@link #softInputMode}. * * @hide diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java index 9ccb4c172158..9013da36007e 100644 --- a/core/java/android/window/VirtualDisplayTaskEmbedder.java +++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java @@ -19,6 +19,7 @@ package android.window; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; import static android.view.Display.INVALID_DISPLAY; import android.app.ActivityManager; @@ -63,6 +64,7 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { private int mDisplayDensityDpi; private final boolean mSingleTaskInstance; private final boolean mUsePublicVirtualDisplay; + private final boolean mUseTrustedDisplay; private VirtualDisplay mVirtualDisplay; private Insets mForwardedInsets; private DisplayMetrics mTmpDisplayMetrics; @@ -77,10 +79,12 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { * only applicable if virtual displays are used */ public VirtualDisplayTaskEmbedder(Context context, VirtualDisplayTaskEmbedder.Host host, - boolean singleTaskInstance, boolean usePublicVirtualDisplay) { + boolean singleTaskInstance, boolean usePublicVirtualDisplay, + boolean useTrustedDisplay) { super(context, host); mSingleTaskInstance = singleTaskInstance; mUsePublicVirtualDisplay = usePublicVirtualDisplay; + mUseTrustedDisplay = useTrustedDisplay; } /** @@ -103,6 +107,9 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { if (mUsePublicVirtualDisplay) { virtualDisplayFlags |= VIRTUAL_DISPLAY_FLAG_PUBLIC; } + if (mUseTrustedDisplay) { + virtualDisplayFlags |= VIRTUAL_DISPLAY_FLAG_TRUSTED; + } mVirtualDisplay = displayManager.createVirtualDisplay( DISPLAY_NAME + "@" + System.identityHashCode(this), mHost.getWidth(), diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index a50a52219c74..3b5fecfc600a 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -113,6 +113,14 @@ public abstract class FileSystemProvider extends DocumentsProvider { // Default is no-op } + /** + * Callback indicating that the given document has been deleted or moved. This gives + * the provider a hook to revoke the uri permissions. + */ + protected void onDocIdDeleted(String docId) { + // Default is no-op + } + @Override public boolean onCreate() { throw new UnsupportedOperationException( @@ -283,6 +291,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { final String afterDocId = getDocIdForFile(after); onDocIdChanged(docId); + onDocIdDeleted(docId); onDocIdChanged(afterDocId); final File afterVisibleFile = getFileForDocId(afterDocId, true); @@ -312,6 +321,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { final String docId = getDocIdForFile(after); onDocIdChanged(sourceDocumentId); + onDocIdDeleted(sourceDocumentId); onDocIdChanged(docId); moveInMediaStore(visibleFileBefore, getFileForDocId(docId, true)); @@ -343,6 +353,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { } onDocIdChanged(docId); + onDocIdDeleted(docId); removeFromMediaStore(visibleFile); } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 7c32ca653114..6becb07d02a4 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -346,22 +346,6 @@ void android_os_Process_setProcessFrozen( } } -void android_os_Process_enableFreezer( - JNIEnv *env, jobject clazz, jboolean enable) -{ - bool success = true; - - if (enable) { - success = SetTaskProfiles(0, {"FreezerFrozen"}, true); - } else { - success = SetTaskProfiles(0, {"FreezerThawed"}, true); - } - - if (!success) { - jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); - } -} - jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid) { SchedPolicy sp; @@ -1360,7 +1344,6 @@ static const JNINativeMethod methods[] = { {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, {"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen}, - {"enableFreezer", "(Z)V", (void*)android_os_Process_enableFreezer}, {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory}, {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 5c2841aff1d8..7597e8732153 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -129,6 +129,7 @@ <!-- virtual display test permissions --> <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" /> + <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" /> <!-- color extraction test permissions --> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index 777f4a3e03a8..de81ff4d0ca5 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -19,6 +19,7 @@ package android.content; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.google.common.truth.Truth.assertThat; @@ -188,19 +189,38 @@ public class ContextTest { assertFalse(wrapper.isUiContext()); - wrapper = new ContextWrapper(new TestUiContext()); + wrapper = new ContextWrapper(getUiContext()); assertTrue(wrapper.isUiContext()); } - private static class TestUiContext extends ContextWrapper { - TestUiContext() { - super(null /* base */); - } + @Test + public void testIsUiContext_UiContextDerivedContext() { + final Context uiContext = getUiContext(); + Context context = uiContext.createAttributionContext(null /* attributionTag */); - @Override - public boolean isUiContext() { - return true; - } + assertTrue(context.isUiContext()); + + context = uiContext.createConfigurationContext(new Configuration()); + + assertTrue(context.isUiContext()); + } + + @Test + public void testIsUiContext_UiContextDerivedDisplayContext() { + final Context uiContext = getUiContext(); + final Display secondaryDisplay = + getSecondaryDisplay(uiContext.getSystemService(DisplayManager.class)); + final Context context = uiContext.createDisplayContext(secondaryDisplay); + + assertFalse(context.isUiContext()); + } + + private Context getUiContext() { + final Context appContext = ApplicationProvider.getApplicationContext(); + final DisplayManager displayManager = appContext.getSystemService(DisplayManager.class); + final Display display = displayManager.getDisplay(DEFAULT_DISPLAY); + return appContext.createDisplayContext(display) + .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */); } } diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index daf613976358..0f6284d22d10 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -247,6 +247,25 @@ public class VirtualDisplayTest extends AndroidTestCase { assertDisplayUnregistered(display); } + /** + * Ensures that an application can create a trusted virtual display with the permission + * {@code ADD_TRUSTED_DISPLAY}. + */ + public void testTrustedVirtualDisplay() throws Exception { + VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME, + WIDTH, HEIGHT, DENSITY, mSurface, + DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED); + assertNotNull("virtual display must not be null", virtualDisplay); + + Display display = virtualDisplay.getDisplay(); + try { + assertDisplayRegistered(display, Display.FLAG_PRIVATE | Display.FLAG_TRUSTED); + } finally { + virtualDisplay.release(); + } + assertDisplayUnregistered(display); + } + private void assertDisplayRegistered(Display display, int flags) { assertNotNull("display object must not be null", display); assertTrue("display must be valid", display.isValid()); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 5c16772488d0..4cf6715ba0ca 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -24,6 +24,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -117,7 +118,15 @@ public class ViewRootImplTest { final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); ViewRootImpl.adjustLayoutParamsForCompatibility(attrs); - // A window which fits system bars must fit IME, unless its type is toast or system alert. + assertEquals(Type.systemBars(), attrs.getFitInsetsTypes()); + } + + @Test + public void adjustLayoutParamsForCompatibility_fitSystemBarsAndIme() { + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); + attrs.softInputMode |= SOFT_INPUT_ADJUST_RESIZE; + ViewRootImpl.adjustLayoutParamsForCompatibility(attrs); + assertEquals(Type.systemBars() | Type.ime(), attrs.getFitInsetsTypes()); } diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index ed566a50ec58..533f6950fb89 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -68,6 +68,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.CRC32; @@ -2079,7 +2080,10 @@ public class ExifInterface { try { // Move the original file to temporary file. if (mFilename != null) { - tempFile = new File(mFilename + ".tmp"); + String parent = originalFile.getParent(); + String name = originalFile.getName(); + String tempPrefix = UUID.randomUUID().toString() + "_"; + tempFile = new File(parent, tempPrefix + name); if (!originalFile.renameTo(tempFile)) { throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath()); } 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 51a7245ea5c6..218c95c2496f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java @@ -141,7 +141,7 @@ public class CarKeyguardViewController extends OverlayViewController implements } @Override - protected boolean shouldShowNavigationBar() { + protected boolean shouldShowNavigationBarInsets() { return true; } @@ -177,7 +177,6 @@ public class CarKeyguardViewController extends OverlayViewController implements mKeyguardStateController.notifyKeyguardState(mShowing, /* occluded= */ false); mCarNavigationBarController.showAllKeyguardButtons(/* isSetUp= */ true); start(); - getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ true); reset(/* hideBouncerWhenShowing= */ false); notifyKeyguardUpdateMonitor(); } @@ -192,7 +191,6 @@ public class CarKeyguardViewController extends OverlayViewController implements mBouncer.hide(/* destroyView= */ true); mCarNavigationBarController.hideAllKeyguardButtons(/* isSetUp= */ true); stop(); - getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false); mKeyguardStateController.notifyKeyguardDoneFading(); mHandler.post(mViewMediatorCallback::keyguardGone); notifyKeyguardUpdateMonitor(); @@ -237,7 +235,6 @@ public class CarKeyguardViewController extends OverlayViewController implements public void onCancelClicked() { if (mBouncer == null) return; - getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false); getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false); mBouncer.hide(/* destroyView= */ true); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java index 3527bf93682f..e7d31949eb24 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java @@ -29,6 +29,8 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; +import com.android.systemui.car.notification.BottomNotificationPanelViewMediator; +import com.android.systemui.car.notification.TopNotificationPanelViewMediator; import com.android.systemui.dagger.qualifiers.Main; import java.lang.annotation.ElementType; @@ -95,6 +97,7 @@ public class SystemBarConfigs { populateMaps(); readConfigs(); checkEnabledBarsHaveUniqueBarTypes(); + checkSystemBarEnabledForNotificationPanel(); setInsetPaddingsForOverlappingCorners(); sortSystemBarSidesByZOrder(); } @@ -221,6 +224,34 @@ public class SystemBarConfigs { } } + private void checkSystemBarEnabledForNotificationPanel() throws RuntimeException { + + String notificationPanelMediatorName = + mResources.getString(R.string.config_notificationPanelViewMediator); + if (notificationPanelMediatorName == null) { + return; + } + + Class<?> notificationPanelMediatorUsed = null; + try { + notificationPanelMediatorUsed = Class.forName(notificationPanelMediatorName); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + if (!mTopNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom( + TopNotificationPanelViewMediator.class)) { + throw new RuntimeException( + "Top System Bar must be enabled to use " + notificationPanelMediatorName); + } + + if (!mBottomNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom( + BottomNotificationPanelViewMediator.class)) { + throw new RuntimeException("Bottom System Bar must be enabled to use " + + notificationPanelMediatorName); + } + } + private void setInsetPaddingsForOverlappingCorners() { setInsetPaddingForOverlappingCorner(TOP, LEFT); setInsetPaddingForOverlappingCorner(TOP, RIGHT); @@ -277,7 +308,7 @@ public class SystemBarConfigs { } private static boolean isHorizontalBar(@SystemBarSide int side) { - return side == TOP || side == BOTTOM; + return side == TOP || side == BOTTOM; } private static boolean isVerticalBar(@SystemBarSide int side) { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index 8d5843635e5f..7cd559a11158 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -91,7 +91,6 @@ public class NotificationPanelViewController extends OverlayPanelViewController private RecyclerView mNotificationList; private NotificationViewController mNotificationViewController; - private boolean mIsTracking; private boolean mNotificationListAtEnd; private float mFirstTouchDownOnGlassPane; private boolean mNotificationListAtEndAtTimeOfTouch; @@ -194,12 +193,12 @@ public class NotificationPanelViewController extends OverlayPanelViewController } @Override - protected boolean shouldShowNavigationBar() { + protected boolean shouldShowNavigationBarInsets() { return true; } @Override - protected boolean shouldShowStatusBar() { + protected boolean shouldShowStatusBarInsets() { return true; } @@ -299,14 +298,14 @@ public class NotificationPanelViewController extends OverlayPanelViewController // The glass pane is used to view touch events before passed to the notification list. // This allows us to initialize gesture listeners and detect when to close the notifications glassPane.setOnTouchListener((v, event) -> { - if (event.getActionMasked() == MotionEvent.ACTION_UP) { + if (isClosingAction(event)) { mNotificationListAtEndAtTimeOfTouch = false; } - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + if (isOpeningAction(event)) { mFirstTouchDownOnGlassPane = event.getRawX(); mNotificationListAtEndAtTimeOfTouch = mNotificationListAtEnd; // Reset the tracker when there is a touch down on the glass pane. - mIsTracking = false; + setIsTracking(false); // Pass the down event to gesture detector so that it knows where the touch event // started. closeGestureDetector.onTouchEvent(event); @@ -341,22 +340,21 @@ public class NotificationPanelViewController extends OverlayPanelViewController // If the card is swiping we should not allow the notification shade to close. // Hence setting mNotificationListAtEndAtTimeOfTouch to false will stop that - // for us. We are also checking for mIsTracking because while swiping the + // for us. We are also checking for isTracking() because while swiping the // notification shade to close if the user goes a bit horizontal while swiping // upwards then also this should close. - if (mIsNotificationCardSwiping && !mIsTracking) { + if (mIsNotificationCardSwiping && !isTracking()) { mNotificationListAtEndAtTimeOfTouch = false; } boolean handled = closeGestureDetector.onTouchEvent(event); - boolean isTracking = mIsTracking; + boolean isTracking = isTracking(); Rect rect = getLayout().getClipBounds(); float clippedHeight = 0; if (rect != null) { clippedHeight = rect.bottom; } - if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP - && mIsSwipingVerticallyToClose) { + if (!handled && isClosingAction(event) && mIsSwipingVerticallyToClose) { if (getSettleClosePercentage() < getPercentageFromEndingEdge() && isTracking) { animatePanel(DEFAULT_FLING_VELOCITY, false); } else if (clippedHeight != getLayout().getHeight() && isTracking) { @@ -369,7 +367,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController // Updating the mNotificationListAtEndAtTimeOfTouch state has to be done after // the event has been passed to the closeGestureDetector above, such that the // closeGestureDetector sees the up event before the state has changed. - if (event.getActionMasked() == MotionEvent.ACTION_UP) { + if (isClosingAction(event)) { mNotificationListAtEndAtTimeOfTouch = false; } return handled || isTracking; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java index 1a8f19e46798..aac4cfbf83c4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java @@ -78,6 +78,11 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController } @Override + protected boolean shouldFocusWindow() { + return false; + } + + @Override protected void showInternal() { getLayout().setVisibility(View.VISIBLE); } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java index 45808a8a0b3e..3c9879c671a5 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java @@ -191,6 +191,38 @@ public abstract class OverlayPanelViewController extends OverlayViewController { } } + /** Checks if a {@link MotionEvent} is an action to open the panel. + * @param e {@link MotionEvent} to check. + * @return true only if opening action. + */ + protected boolean isOpeningAction(MotionEvent e) { + if (mAnimateDirection == POSITIVE_DIRECTION) { + return e.getActionMasked() == MotionEvent.ACTION_DOWN; + } + + if (mAnimateDirection == NEGATIVE_DIRECTION) { + return e.getActionMasked() == MotionEvent.ACTION_UP; + } + + return false; + } + + /** Checks if a {@link MotionEvent} is an action to close the panel. + * @param e {@link MotionEvent} to check. + * @return true only if closing action. + */ + protected boolean isClosingAction(MotionEvent e) { + if (mAnimateDirection == POSITIVE_DIRECTION) { + return e.getActionMasked() == MotionEvent.ACTION_UP; + } + + if (mAnimateDirection == NEGATIVE_DIRECTION) { + return e.getActionMasked() == MotionEvent.ACTION_DOWN; + } + + return false; + } + /* ***************************************************************************************** * * Panel Animation * ***************************************************************************************** */ @@ -206,7 +238,6 @@ public abstract class OverlayPanelViewController extends OverlayViewController { } onAnimateCollapsePanel(); - getOverlayViewGlobalStateController().setWindowFocusable(false); animatePanel(mClosingVelocity, /* isClosing= */ true); } @@ -243,8 +274,7 @@ public abstract class OverlayPanelViewController extends OverlayViewController { * Depending on certain conditions, determines whether to fully expand or collapse the panel. */ protected void maybeCompleteAnimation(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_UP - && isPanelVisible()) { + if (isClosingAction(event) && isPanelVisible()) { if (mSettleClosePercentage < mPercentageFromEndingEdge) { animatePanel(DEFAULT_FLING_VELOCITY, false); } else { @@ -266,14 +296,17 @@ public abstract class OverlayPanelViewController extends OverlayViewController { float from = getCurrentStartPosition(rect); if (from != to) { animate(from, to, velocity, isClosing); - return; } + + // If we swipe down the notification panel all the way to the bottom of the screen + // (i.e. from == to), then we have finished animating the panel. + return; } // We will only be here if the shade is being opened programmatically or via button when // height of the layout was not calculated. - ViewTreeObserver notificationTreeObserver = getLayout().getViewTreeObserver(); - notificationTreeObserver.addOnGlobalLayoutListener( + ViewTreeObserver panelTreeObserver = getLayout().getViewTreeObserver(); + panelTreeObserver.addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { @@ -381,7 +414,6 @@ public abstract class OverlayPanelViewController extends OverlayViewController { getOverlayViewGlobalStateController().hideView(/* panelViewController= */ this); } getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - getOverlayViewGlobalStateController().setWindowFocusable(visible); } /* ***************************************************************************************** * @@ -476,6 +508,11 @@ public abstract class OverlayPanelViewController extends OverlayViewController { return mIsTracking; } + /** Sets whether the panel is currently tracking or not. */ + protected final void setIsTracking(boolean isTracking) { + mIsTracking = isTracking; + } + /** Returns {@code true} if the panel is currently animating. */ protected final boolean isAnimating() { return mIsAnimating; @@ -514,7 +551,7 @@ public abstract class OverlayPanelViewController extends OverlayViewController { } setPanelVisible(true); - // clips the view for the notification shade when the user scrolls to open. + // clips the view for the panel when the user scrolls to open. setViewClipBounds((int) event2.getRawY()); // Initially the scroll starts with height being zero. This checks protects from divide @@ -569,11 +606,11 @@ public abstract class OverlayPanelViewController extends OverlayViewController { boolean isInClosingDirection = mAnimateDirection * distanceY > 0; // This check is to figure out if onScroll was called while swiping the card at - // bottom of the list. At that time we should not allow notification shade to + // bottom of the panel. At that time we should not allow panel to // close. We are also checking for the upwards swipe gesture here because it is - // possible if a user is closing the notification shade and while swiping starts + // possible if a user is closing the panel and while swiping starts // to open again but does not fling. At that time we should allow the - // notification shade to close fully or else it would stuck in between. + // panel to close fully or else it would stuck in between. if (Math.abs(getLayout().getHeight() - y) > SWIPE_DOWN_MIN_DISTANCE && isInClosingDirection) { setViewClipBounds((int) y); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java index 53deb9d9dc5d..8adc1adcc41c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java @@ -136,16 +136,18 @@ public class OverlayViewController { } /** - * Returns {@code true} if navigation bar should be displayed over this view. + * Returns {@code true} if navigation bar insets should be displayed over this view. Has no + * effect if {@link #shouldFocusWindow} returns {@code false}. */ - protected boolean shouldShowNavigationBar() { + protected boolean shouldShowNavigationBarInsets() { return false; } /** - * Returns {@code true} if status bar should be displayed over this view. + * Returns {@code true} if status bar insets should be displayed over this view. Has no + * effect if {@link #shouldFocusWindow} returns {@code false}. */ - protected boolean shouldShowStatusBar() { + protected boolean shouldShowStatusBarInsets() { return false; } @@ -157,6 +159,15 @@ public class OverlayViewController { } /** + * Returns {@code true} if the window should be focued when this view is visible. Note that + * returning {@code false} here means that {@link #shouldShowStatusBarInsets} and + * {@link #shouldShowNavigationBarInsets} will have no effect. + */ + protected boolean shouldFocusWindow() { + return true; + } + + /** * Returns the insets types to fit to the sysui overlay window when this * {@link OverlayViewController} is in the foreground. */ diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java index 2494242c24f0..55f0975aeccf 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java @@ -18,7 +18,6 @@ package com.android.systemui.car.window; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import android.annotation.Nullable; import android.util.Log; @@ -118,6 +117,7 @@ public class OverlayViewGlobalStateController { updateInternalsWhenShowingView(viewController); refreshInsetTypesToFit(); + refreshWindowFocus(); refreshNavigationBarVisibility(); refreshStatusBarVisibility(); @@ -190,6 +190,7 @@ public class OverlayViewGlobalStateController { mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController)); refreshHighestZOrderWhenHidingView(viewController); refreshInsetTypesToFit(); + refreshWindowFocus(); refreshNavigationBarVisibility(); refreshStatusBarVisibility(); @@ -214,23 +215,37 @@ public class OverlayViewGlobalStateController { } private void refreshNavigationBarVisibility() { - mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); - if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) { + if (mZOrderVisibleSortedMap.isEmpty()) { mWindowInsetsController.show(navigationBars()); - } else { + return; + } + + // Do not hide navigation bar insets if the window is not focusable. + if (mHighestZOrder.shouldFocusWindow() && !mHighestZOrder.shouldShowNavigationBarInsets()) { mWindowInsetsController.hide(navigationBars()); + } else { + mWindowInsetsController.show(navigationBars()); } } private void refreshStatusBarVisibility() { - mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); - if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) { + if (mZOrderVisibleSortedMap.isEmpty()) { mWindowInsetsController.show(statusBars()); - } else { + return; + } + + // Do not hide status bar insets if the window is not focusable. + if (mHighestZOrder.shouldFocusWindow() && !mHighestZOrder.shouldShowStatusBarInsets()) { mWindowInsetsController.hide(statusBars()); + } else { + mWindowInsetsController.show(statusBars()); } } + private void refreshWindowFocus() { + setWindowFocusable(mHighestZOrder == null ? false : mHighestZOrder.shouldFocusWindow()); + } + private void refreshInsetTypesToFit() { if (mZOrderVisibleSortedMap.isEmpty()) { setFitInsetsTypes(statusBars()); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java index 029bd3702afe..c955fab592f3 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java @@ -16,6 +16,7 @@ package com.android.systemui.car.window; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import android.content.Context; @@ -104,6 +105,7 @@ public class SystemUIOverlayWindowController implements mLp.setTitle("SystemUIOverlayWindow"); mLp.packageName = mContext.getPackageName(); mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; mWindowManager.addView(mBaseLayout, mLp); mLpChanged.copyFrom(mLp); @@ -160,6 +162,7 @@ public class SystemUIOverlayWindowController implements private void updateWindow() { if (mLp != null && mLp.copyFrom(mLpChanged) != 0) { if (isAttached()) { + mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; mWindowManager.updateViewLayout(mBaseLayout, mLp); } } diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java index a831464e7987..c9ec34fd5f08 100644 --- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java +++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java @@ -32,6 +32,8 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.TransactionPool; import com.android.systemui.dagger.qualifiers.Main; +import java.util.Objects; + import javax.inject.Inject; import javax.inject.Singleton; @@ -90,7 +92,7 @@ public class DisplaySystemBarsController extends DisplayImeController { } @VisibleForTesting - class PerDisplay extends IDisplayWindowInsetsController.Stub { + class PerDisplay extends DisplayImeController.PerDisplay { int mDisplayId; InsetsController mInsetsController; @@ -98,6 +100,8 @@ public class DisplaySystemBarsController extends DisplayImeController { String mPackageName; PerDisplay(int displayId) { + super(displayId, + mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()); mDisplayId = displayId; mInsetsController = new InsetsController( new DisplaySystemBarsInsetsControllerHost(mHandler, this)); @@ -105,6 +109,7 @@ public class DisplaySystemBarsController extends DisplayImeController { @Override public void insetsChanged(InsetsState insetsState) { + super.insetsChanged(insetsState); if (mInsetsState.equals(insetsState)) { return; } @@ -118,24 +123,33 @@ public class DisplaySystemBarsController extends DisplayImeController { @Override public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls) { + super.insetsControlChanged(insetsState, activeControls); mInsetsController.onControlsChanged(activeControls); } @Override public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { - mInsetsController.hide(types); + if ((types & WindowInsets.Type.ime()) == 0) { + mInsetsController.hide(types); + } else { + super.hideInsets(types, fromIme); + } + } @Override public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) { - mInsetsController.show(types); + if ((types & WindowInsets.Type.ime()) == 0) { + mInsetsController.show(types); + } else { + super.showInsets(types, fromIme); + } + } @Override public void topFocusedWindowChanged(String packageName) { - // If both package names are null or both package names are equal, return. - if (mPackageName == packageName - || (mPackageName != null && mPackageName.equals(packageName))) { + if (Objects.equals(mPackageName, packageName)) { return; } mPackageName = packageName; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java index 0b164a2e1a51..84c840477302 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java @@ -48,6 +48,10 @@ import org.mockito.MockitoAnnotations; @SmallTest public class CarNavigationBarControllerTest extends SysuiTestCase { + private static final String TOP_NOTIFICATION_PANEL = + "com.android.systemui.car.notification.TopNotificationPanelViewMediator"; + private static final String BOTTOM_NOTIFICATION_PANEL = + "com.android.systemui.car.notification.BottomNotificationPanelViewMediator"; private CarNavigationBarController mCarNavigationBar; private NavigationBarViewFactory mNavigationBarViewFactory; private TestableResources mTestableResources; @@ -117,6 +121,11 @@ public class CarNavigationBarControllerTest extends SysuiTestCase { @Test public void testGetTopWindow_topDisabled_returnsNull() { mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, false); + mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true); + // If Top Notification Panel is used but top navigation bar is not enabled, SystemUI is + // expected to crash. + mTestableResources.addOverride(R.string.config_notificationPanelViewMediator, + BOTTOM_NOTIFICATION_PANEL); mCarNavigationBar = createNavigationBarController(); ViewGroup window = mCarNavigationBar.getTopWindow(); @@ -148,6 +157,11 @@ public class CarNavigationBarControllerTest extends SysuiTestCase { @Test public void testGetBottomWindow_bottomDisabled_returnsNull() { mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false); + mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true); + // If Bottom Notification Panel is used but bottom navigation bar is not enabled, + // SystemUI is expected to crash. + mTestableResources.addOverride(R.string.config_notificationPanelViewMediator, + TOP_NOTIFICATION_PANEL); mCarNavigationBar = createNavigationBarController(); ViewGroup window = mCarNavigationBar.getBottomWindow(); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java index 7311cdb68a3c..23e21e4cbed6 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java @@ -224,18 +224,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase { } @Test - public void animateCollapsePanel_removesWindowFocus() { - mOverlayPanelViewController.inflate(mBaseLayout); - mOverlayPanelViewController.setShouldAnimateCollapsePanel(true); - mOverlayPanelViewController.setPanelExpanded(true); - mOverlayPanelViewController.setPanelVisible(true); - - mOverlayPanelViewController.animateCollapsePanel(); - - verify(mOverlayViewGlobalStateController).setWindowFocusable(false); - } - - @Test public void animateExpandPanel_shouldNotAnimateExpandPanel_doesNotExpand() { mOverlayPanelViewController.inflate(mBaseLayout); mOverlayPanelViewController.setShouldAnimateExpandPanel(false); @@ -365,14 +353,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase { } @Test - public void setPanelVisible_setTrue_setWindowFocusable() { - mOverlayPanelViewController.inflate(mBaseLayout); - mOverlayPanelViewController.setPanelVisible(true); - - verify(mOverlayViewGlobalStateController).setWindowFocusable(true); - } - - @Test public void setPanelVisible_setFalse_windowVisible_setsWindowNotVisible() { mOverlayPanelViewController.inflate(mBaseLayout); when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true); @@ -404,15 +384,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase { } @Test - public void setPanelVisible_setFalse_setWindowNotFocusable() { - mOverlayPanelViewController.inflate(mBaseLayout); - - mOverlayPanelViewController.setPanelVisible(false); - - verify(mOverlayViewGlobalStateController).setWindowFocusable(false); - } - - @Test public void dragOpenTouchListener_isNotInflated_inflatesView() { when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true); assertThat(mOverlayPanelViewController.isInflated()).isFalse(); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java index ff286650ea50..294aa0d3cf9b 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java @@ -108,9 +108,54 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { } @Test + public void showView_nothingVisible_windowNotFocusable_shouldShowNavBar_navBarsVisible() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true); + + mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); + + verify(mWindowInsetsController).show(navigationBars()); + } + + @Test + public void showView_nothingVisible_windowNotFocusable_shouldHideNavBar_notHidden() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false); + + mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); + + verify(mWindowInsetsController, never()).hide(navigationBars()); + } + + @Test + public void showView_nothingVisible_windowNotFocusable_shouldShowStatusBar_statusBarsVisible() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true); + + mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); + + verify(mWindowInsetsController).show(statusBars()); + } + + @Test + public void showView_nothingVisible_windowNotFocusable_shouldHideStatusBar_notHidden() { + setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false); + + mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); + + verify(mWindowInsetsController, never()).hide(statusBars()); + } + + @Test public void showView_nothingAlreadyShown_shouldShowNavBarFalse_navigationBarsHidden() { setupOverlayViewController1(); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -120,7 +165,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_nothingAlreadyShown_shouldShowNavBarTrue_navigationBarsShown() { setupOverlayViewController1(); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -130,7 +176,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() { setupOverlayViewController1(); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -140,7 +187,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() { setupOverlayViewController1(); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -201,9 +249,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_newHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable); @@ -214,9 +264,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_newHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable); @@ -226,9 +278,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable); @@ -239,9 +293,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable); @@ -289,9 +345,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() { setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -302,9 +360,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() { setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -314,9 +374,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() { setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -327,9 +389,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() { setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable); @@ -512,10 +576,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_newHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable); @@ -526,10 +592,13 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_newHighestZOrder_shouldShowNavBarTrue_navigationBarShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true); + reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable); @@ -539,10 +608,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable); @@ -553,10 +624,13 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true); + reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable); @@ -593,10 +667,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -607,10 +683,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true); + when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -620,10 +698,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false); reset(mWindowInsetsController); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -634,10 +714,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() { setupOverlayViewController1(); - setOverlayViewControllerAsShowing(mOverlayViewController1); setupOverlayViewController2(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); + when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true); + setOverlayViewControllerAsShowing(mOverlayViewController1); setOverlayViewControllerAsShowing(mOverlayViewController2); - when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true); + when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -673,6 +755,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_viewControllerOnlyShown_navigationBarShown() { setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController1); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); @@ -683,6 +766,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase { @Test public void hideView_viewControllerOnlyShown_statusBarShown() { setupOverlayViewController1(); + when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true); setOverlayViewControllerAsShowing(mOverlayViewController1); mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable); diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index f42bf1982b36..11d1b0a9ef2a 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -483,6 +483,13 @@ public class ExternalStorageProvider extends FileSystemProvider { } @Override + protected void onDocIdDeleted(String docId) { + Uri uri = DocumentsContract.buildDocumentUri(AUTHORITY, docId); + getContext().revokeUriPermission(uri, ~0); + } + + + @Override public Cursor queryRoots(String[] projection) throws FileNotFoundException { final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); synchronized (mRootsLock) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index e77d1a2ccea1..44c920c7c0a7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -13,6 +13,7 @@ package com.android.settingslib.wifi; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import android.content.Context; import android.content.Intent; @@ -247,6 +248,10 @@ public class WifiStatusTracker { statusLabel = mContext.getString(R.string.wifi_status_no_internet); } return; + } else if (!isDefaultNetwork && mDefaultNetworkCapabilities != null + && mDefaultNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + statusLabel = mContext.getString(R.string.wifi_connected_low_quality); + return; } } diff --git a/packages/Shell/res/values-bn/strings.xml b/packages/Shell/res/values-bn/strings.xml new file mode 100644 index 000000000000..ede125bce935 --- /dev/null +++ b/packages/Shell/res/values-bn/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"শেল"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"ত্রুটির প্রতিবেদন"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"ত্রুটির প্রতিবেদন <xliff:g id="ID">#%d</xliff:g> তৈরি করা হচ্ছে"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"ত্রুটির প্রতিবেদন <xliff:g id="ID">#%d</xliff:g> ক্যাপচার করা হয়েছে"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"ত্রুটির প্রতিবেদনে বিশদ বিবরণ যোগ করা হচ্ছে"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"অনুগ্রহ করে অপেক্ষা করুন..."</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ফোনে শীঘ্রই ত্রুটির প্রতিবেদন দেখা যাবে"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"ত্রুটির প্রতিবেদনটি শেয়ার করতে এটি বেছে নিন"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"আপনার ত্রুটির প্রতিবেদন শেয়ার করতে আলতো চাপ দিন"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"কোনো স্ক্রিনশট ছাড়াই ত্রুটির প্রতিবেদনটি শেয়ার করতে এটি বেছে নিন, বা স্ক্রিনশটের জন্য অপেক্ষা করুন"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"কোনও স্ক্রিনশট ছাড়াই ত্রুটির প্রতিবেদন শেয়ার করতে আলতো চাপ দিন বা সম্পন্ন করতে স্ক্রিনশটের জন্য অপেক্ষা করুন"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"কোনও স্ক্রিনশট ছাড়াই ত্রুটির প্রতিবেদন শেয়ার করতে আলতো চাপ দিন বা সম্পন্ন করতে স্ক্রিনশটের জন্য অপেক্ষা করুন"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"ত্রুটির প্রতিবেদনগুলিতে থাকা ডেটা, সিস্টেমের বিভিন্ন লগ ফাইলগুলি থেকে আসে, যাতে আপনার বিবেচনা অনুযায়ী সংবেদনশীল ডেটা (যেমন, অ্যাপ্লিকেশানের ব্যবহার এবং লোকেশন ডেটা) থাকতে পারে৷ আপনি বিশ্বাস করেন শুধুমাত্র এমন অ্যাপ্লিকেশান এবং ব্যক্তিদের সাথেই ত্রুটির প্রতিবেদনগুলিকে শেয়ার করুন৷"</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"আর দেখাবেন না"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"ত্রুটির প্রতিবেদনগুলি"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"ত্রুটির প্রতিবেদনের ফাইলটি পড়া যায়নি"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"জিপ ফাইলে ত্রুটি প্রতিবেদনের বিশদ বিবরণ যোগ করা যায়নি"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"নামবিহীন"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"বিশদ বিবরণ"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"স্ক্রিনশট নিন"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"স্ক্রিনশট সফলভাবে নেওয়া হয়েছে৷"</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"স্ক্রিনশট নেওয়া যায়নি৷"</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"ত্রুটির প্রতিবেদন <xliff:g id="ID">#%d</xliff:g> এর বিশদ বিবরণ"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"ফাইলের নাম"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"ত্রুটির শীর্ষক"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"ত্রুটির সারাংশ"</string> + <string name="save" msgid="4781509040564835759">"সেভ করুন"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"ত্রুটির প্রতিবেদন শেয়ার করুন"</string> +</resources> diff --git a/packages/Shell/res/values-eu/strings.xml b/packages/Shell/res/values-eu/strings.xml new file mode 100644 index 000000000000..5d32cabd4ba5 --- /dev/null +++ b/packages/Shell/res/values-eu/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"Akatsen txostenak"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Akatsen <xliff:g id="ID">#%d</xliff:g> txostena egiten ari gara"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"Akatsen <xliff:g id="ID">#%d</xliff:g> txostena egin da"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"Akatsen txostenean xehetasunak gehitzen"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"Itxaron, mesedez…"</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Akatsen txostena telefonoan agertuko da laster"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Hautatu hau akatsen txostena partekatzeko"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Sakatu akatsen txostena partekatzeko"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Hautatu hau akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Sakatu akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Sakatu akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"Errore-txostenek sistemaren erregistro-fitxategietako datuak dauzkate, eta, haietan, kontuzkotzat jotzen duzun informazioa ager daiteke (adibidez, aplikazioen erabilera eta kokapen-datuak). Errore-txostenak partekatzen badituzu, partekatu soilik pertsona eta aplikazio fidagarriekin."</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Ez erakutsi berriro"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"Akatsen txostenak"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"Ezin izan da irakurri akatsen txostena"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Ezin izan dira gehitu akatsen txostenaren xehetasunak ZIP fitxategian"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"izengabea"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"Xehetasunak"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Pantaila-argazkia"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Atera da pantaila-argazkia."</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Ezin izan da atera pantaila-argazkia."</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Akatsen <xliff:g id="ID">#%d</xliff:g> txostenaren xehetasunak"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"Fitxategi-izena"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"Akatsaren izena"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"Akatsaren laburpena"</string> + <string name="save" msgid="4781509040564835759">"Gorde"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Partekatu akatsen txostena"</string> +</resources> diff --git a/packages/Shell/res/values-gl/strings.xml b/packages/Shell/res/values-gl/strings.xml new file mode 100644 index 000000000000..912dc85e15e4 --- /dev/null +++ b/packages/Shell/res/values-gl/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"Informes de erros"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Estase xerando o informe de erros <xliff:g id="ID">#%d</xliff:g>"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"Rexistrouse o informe de erros <xliff:g id="ID">#%d</xliff:g>"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"Engadindo detalles ao informe de erro"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"Agarda..."</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O informe de erros aparecerá no teléfono en breve"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecciona para compartir o teu informe de erros"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toca para compartir o teu informe de erros"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecciona para compartir o informe de erros sen captura de pantalla ou agarda a que acabe a captura"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toca para compartir o informe de erros sen captura de pantalla ou agarda a que finalice a captura"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toca para compartir o informe de erros sen captura de pantalla ou agarda a que finalice a captura"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"Os informes de erros conteñen datos dos distintos ficheiros de rexistro do sistema, os cales poden incluír datos que consideres confidenciais (coma o uso de aplicacións e os datos de localización). Comparte os informes de erros só coas persoas e aplicacións nas que confíes."</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Non mostrar outra vez"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de erros"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"Non se puido ler o ficheiro de informe de erros"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Non se puideron engadir os detalles do informe de erro ao ficheiro zip"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"sen nome"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"Detalles"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Captura de pantalla"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"A captura de pantalla realizouse correctamente."</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Non se puido realizar a captura de pantalla."</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Detalles do informe de erros <xliff:g id="ID">#%d</xliff:g>"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"Nome do ficheiro"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"Título do informe de erros"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"Resumo do informe de erros"</string> + <string name="save" msgid="4781509040564835759">"Gardar"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartir informe de erros"</string> +</resources> diff --git a/packages/Shell/res/values-is/strings.xml b/packages/Shell/res/values-is/strings.xml new file mode 100644 index 000000000000..4989e8763b4d --- /dev/null +++ b/packages/Shell/res/values-is/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"Skipanalína"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"Villutilkynningar"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Verið er að búa til villutilkynningu <xliff:g id="ID">#%d</xliff:g>"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"Villutilkynning <xliff:g id="ID">#%d</xliff:g> búin til"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"Bætir upplýsingum við villutilkynningu"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"Augnablik..."</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Villuskýrslan birtist brátt í símanum"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Veldu að deila villutilkynningunni"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Ýttu til að deila villutilkynningunni"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Veldu að deila villutilkynningunni án skjámyndar eða hinkraðu þangað til skjámyndin er tilbúin"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Ýttu til að deila villutilkynningunni án skjámyndar eða hinkraðu þangað til skjámyndin er tilbúin"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Ýttu til að deila villutilkynningunni án skjámyndar eða hinkraðu þangað til skjámyndin er tilbúin"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"Villutilkynningar innihalda gögn úr ýmsum annálaskrám kerfisins sem gætu innihaldið upplýsingar sem þú telur viðkvæmar (til dæmis notkun forrita og staðsetningarupplýsingar). Deildu villutilkynningum bara með fólki og forritum sem þú treystir."</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Ekki sýna þetta aftur"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"Villutilkynningar"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"Ekki var hægt að lesa úr villuskýrslunni"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Ekki tókst að bæta upplýsingum um villutilkynningu við ZIP-skrá"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"án heitis"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"Nánar"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Skjámynd"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Skjámynd tekin."</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Ekki tókst að taka skjámynd."</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Upplýsingar villutilkynningar <xliff:g id="ID">#%d</xliff:g>"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"Skráarheiti"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"Heiti villu"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"Villusamantekt"</string> + <string name="save" msgid="4781509040564835759">"Vista"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Deila villutilkynningu"</string> +</resources> diff --git a/packages/Shell/res/values-ky/strings.xml b/packages/Shell/res/values-ky/strings.xml new file mode 100644 index 000000000000..3567ac276e63 --- /dev/null +++ b/packages/Shell/res/values-ky/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"Кабык"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"Мүчүлүштүк тууралуу кабар берүүлөр"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Мүчүлүштүк тууралуу билдирүү <xliff:g id="ID">#%d</xliff:g> түзүлүүдө"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"Мүчүлүштүк тууралуу билдирүү <xliff:g id="ID">#%d</xliff:g> жаздырылды"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"Мүчүлүштүк жөнүндө кабардын чоо-жайы кошулууда"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"Күтө туруңуз…"</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Мүчүлүштүктөр жөнүндө кабар жакында телефонго чыгат"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Мүчүлүштүк тууралуу кабарды жөнөтүү үчүн таптап коюңуз"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Мүчүлүштүк тууралуу билдирүүңүздү бөлүшүү үчүн таптап коюңуз"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Мүчүлүштүк тууралуу кабарды скриншотсуз жөнөтүү үчүн солго серпиңиз же скриншот даяр болгуча күтүңүз"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Мүчүлүштүк тууралуу билдирүүңүздү скриншотсуз бөлүшүү үчүн таптап коюңуз же скриншот даяр болгуча күтө туруңуз"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Мүчүлүштүк тууралуу билдирүүңүздү скриншотсуз бөлүшүү үчүн таптап коюңуз же скриншот даяр болгуча күтө туруңуз"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"Мүчүлүштүктөр тууралуу билдирүүлөрдө тутумдун ар кандай таржымалдарынан алынган дайындар, ошондой эле купуя маалымат камтылышы мүмкүн (мисалы, жайгашкан жер сыяктуу). Мындай билдирүүлөрдү бир гана ишеничтүү адамдар жана колдонмолор менен бөлүшүңүз."</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Экинчи көрүнбөсүн"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"Мүчүлүштүктөрдү кабарлоо"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"Мүчүлүштүк тууралуу кабарлаган файл окулбай койду"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Мүчүлүштүктөр жөнүндө кабардын чоо-жайы zip файлына кошулбай койду"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"аталышы жок"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"Чоо-жайы"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Скриншот"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Скриншот ийгиликтүү тартылды."</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Скриншот тартылбай койду."</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Мүчүлүштүк тууралуу билдирүүнүн <xliff:g id="ID">#%d</xliff:g> чоо-жайы"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"Файлдын аталышы"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"Мүчүлүштүктүн аталышы"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"Мүчүлүштүк корутундусу"</string> + <string name="save" msgid="4781509040564835759">"Сактоо"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Мүчүлүштүк тууралуу кабарлоо"</string> +</resources> diff --git a/packages/Shell/res/values-mk/strings.xml b/packages/Shell/res/values-mk/strings.xml new file mode 100644 index 000000000000..3d18d30715c2 --- /dev/null +++ b/packages/Shell/res/values-mk/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"Извештаи за грешки"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Се генерира извештајот за грешки <xliff:g id="ID">#%d</xliff:g>"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"Извештајот за грешки <xliff:g id="ID">#%d</xliff:g> е снимен"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"Се додаваат детали на извештајот за грешка"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"Почекајте..."</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Извештајот за грешки наскоро ќе се појави на телефонот"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Изберете да го споделите извештајот за грешки"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Допрете за да го споделите извештајот за грешки"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Изберете да го спод. извештајот за грешки без слика од екранот или почекајте да се подготви сликата"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Допрете за споделување извештај за грешки без слика од екранот или почекајте да се подготви сликата"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Допрете за споделување извештај за грешки без слика од екранот или почекајте да се подготви сликата"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"Извештаите за грешка содржат податоци од разни датотеки за евиденција на системот, вклучувајќи и податоци што можеби ги сметате за чувствителни (како што се користење на апликациите и податоци за локацијата). Извештаите за грешки споделувајте ги само со апликации и луѓе во кои имате доверба."</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Не покажувај повторно"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаи за грешки"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"Датотеката со извештај за грешка не можеше да се прочита"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Не можевме да ги додадеме деталите на извештајот за грешки во zip-датотеката"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"неименувани"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"Детали"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Слика од екранот"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Успешно е направена слика од екранот."</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Не може да се направи слика од екранот."</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Детали за извештајот за грешки <xliff:g id="ID">#%d</xliff:g>"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"Име на датотека"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"Наслов на грешката"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"Преглед на грешката"</string> + <string name="save" msgid="4781509040564835759">"Зачувај"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Споделете извештај за грешки"</string> +</resources> diff --git a/packages/Shell/res/values-ml/strings.xml b/packages/Shell/res/values-ml/strings.xml new file mode 100644 index 000000000000..78b43bbe3d7b --- /dev/null +++ b/packages/Shell/res/values-ml/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"ഷെൽ"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"ബഗ് റിപ്പോർട്ടുകൾ"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"ബഗ് റിപ്പോർട്ട് <xliff:g id="ID">#%d</xliff:g> സൃഷ്ടിക്കുന്നു"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"ബഗ് റിപ്പോർട്ട് <xliff:g id="ID">#%d</xliff:g> ക്യാപ്ചർ ചെയ്തു"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"ബഗ് റിപ്പോർട്ടിലേക്ക് വിശദാംശങ്ങൾ ചേർക്കുന്നു"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"കാത്തിരിക്കുക..."</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ബഗ് റിപ്പോർട്ട് താമസിയാതെ ഫോണിൽ ദൃശ്യമാകും"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടുന്നതിന് തിരഞ്ഞെടുക്കുക"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ ടാപ്പുചെയ്യുക"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"സ്ക്രീൻഷോട്ട് കൂടാതെയോ സ്ക്രീൻഷോട്ട് പൂർത്തിയാകുന്നതിന് കാക്കാതെയോ ബഗ് റിപ്പോർട്ട് പങ്കിടുക"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"സ്ക്രീൻഷോട്ട് കൂടാതെയോ സ്ക്രീൻഷോട്ട് പൂർത്തിയാകുന്നതിന് കാക്കാതെയോ നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ ടാപ്പുചെയ്യുക"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"സ്ക്രീൻഷോട്ട് കൂടാതെയോ സ്ക്രീൻഷോട്ട് പൂർത്തിയാകുന്നതിന് കാക്കാതെയോ നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ ടാപ്പുചെയ്യുക"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"ബഗ് റിപ്പോർട്ടുകളിൽ സിസ്റ്റത്തിന്റെ നിരവധി ലോഗ് ഫയലുകളിൽ നിന്നുള്ള വിവരങ്ങൾ അടങ്ങിയിരിക്കുന്നു, ഇതിൽ നിങ്ങൾ രഹസ്യ സ്വഭാവമുള്ളവയായി പരിഗണിക്കുന്ന വിവരങ്ങളും (ആപ്പ് ഉപയോഗ വിവരങ്ങൾ, ലൊക്കേഷൻ വിവരങ്ങൾ എന്നിവ പോലെ) ഉൾപ്പെടാം. നിങ്ങൾ വിശ്വസിക്കുന്ന ആപ്പുകൾക്കും ആളുകൾക്കും മാത്രം ബഗ് റിപ്പോർട്ടുകൾ പങ്കിടുക."</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"വീണ്ടും കാണിക്കരുത്"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"ബഗ് റിപ്പോർട്ടുകൾ"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"ബഗ് റിപ്പോർട്ട് ഫയൽ വായിക്കാനായില്ല"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"സിപ്പ് ഫയലിലേക്ക് ബഗ് റിപ്പോർട്ട് വിശദാംശങ്ങൾ ചേർക്കാൻ കഴിഞ്ഞില്ല"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"പേരില്ലാത്തവർ"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"വിശദാംശങ്ങൾ"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"സ്ക്രീൻഷോട്ട്"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"സ്ക്രീൻഷോട്ട് എടുത്തു."</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"സ്ക്രീൻഷോട്ട് എടുക്കാൻ കഴിഞ്ഞില്ല."</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"ബഗ് റിപ്പോർട്ട് <xliff:g id="ID">#%d</xliff:g> വിശദാംശങ്ങൾ"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"ഫയല്നാമം"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"ബഗിന്റെ പേര്"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"ബഗ് സംഗ്രഹം"</string> + <string name="save" msgid="4781509040564835759">"സംരക്ഷിക്കുക"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"ബഗ് റിപ്പോർട്ട് പങ്കിടുക"</string> +</resources> diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml new file mode 100644 index 000000000000..9595e28eb6e2 --- /dev/null +++ b/packages/Shell/res/values-mr/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"शेल"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"बग रीपोर्ट"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"बग रीपोर्ट <xliff:g id="ID">#%d</xliff:g> तयार केला जात आहे"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"बग रीपोर्ट <xliff:g id="ID">#%d</xliff:g> कॅप्चर केला"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"दोष अहवालामध्ये तपशील जोडत आहे"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा करा..."</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"फोनवर बग रीपोर्ट लवकरच दिसेल"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"तुमचा बग रीपोर्ट शेअर करण्यासाठी निवडा"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तुमचा बग रीपोर्ट स्क्रीनशॉटशिवाय शेअर करण्यासाठी टॅप करा किंवा स्क्रीनशॉट पूर्ण होण्याची प्रतीक्षा करा"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाईल वाचणे शक्य झाले नाही"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"झिप फाईल मध्ये बग रीपोर्ट तपशील जोडणे शक्य झाले नाही"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"अनामित"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"तपशील"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"स्क्रीनशॉट"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"स्क्रीनशॉट यशस्वीरित्या घेतला."</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"स्क्रीनशॉट घेणे शक्य झाले नाही."</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"बग रीपोर्ट <xliff:g id="ID">#%d</xliff:g> तपशील"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"फाईलनाव"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"दोष शीर्षक"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"दोष सारांश"</string> + <string name="save" msgid="4781509040564835759">"सेव्ह करा"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"बग रीपोर्ट शेअर करा"</string> +</resources> diff --git a/packages/Shell/res/values-my/strings.xml b/packages/Shell/res/values-my/strings.xml new file mode 100644 index 000000000000..2376ffd2b2cf --- /dev/null +++ b/packages/Shell/res/values-my/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"အခွံ"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"ချွတ်ယွင်းမှု အစီရင်ခံစာများ"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"ချွတ်ယွင်းမှုအစီရင်ခံချက် <xliff:g id="ID">#%d</xliff:g> ကိုထုတ်နေပါသည်"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"ချွတ်ယွင်းမှုအစီရင်ခံချက် <xliff:g id="ID">#%d</xliff:g> ကိုရယူထားပြီးပါပြီ"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"ချွတ်ယွင်းချက်အစီရင်ခံချက်သို့ အသေးစိတ်များပေါင်းထည့်ရန်"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"ခေတ္တစောင့်ပါ..."</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ချွတ်ယွင်းချက်အစီရင်ခံစာကို မကြာခင် ဖုန်းထဲတွင် မြင်တွေ့ရပါလိမ့်မည်"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"ချွတ်ယွင်းချက်အစီရင်ခံစာကို မျှဝေရန် ရွေးချယ်ပါ"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"သင့်ချွတ်ယွင်းမှုအစီရင်ခံချက်ကို မျှဝေရန် တို့ပါ"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"ချွတ်ယွင်းမှုအစီရင်ခံစာကို ဖန်သားပြင်ပုံ မပါဘဲမျှဝေပါ (သို့) ဖန်သားပြင်ပုံ ရိုက်ပြီးသည်အထိ စောင့်ပါ"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ချွတ်ယွင်းချက်အစီရင်ခံစာကို ဖန်သားပြင်ဓာတ်ပုံမှတ်တမ်းမပါဘဲ မျှဝေရန် တို့ပါ သို့မဟုတ် ဖန်သားပြင်ဓာတ်ပုံမှတ်တမ်းတင်ခြင်း ပြီးဆုံးသည်အထိ စောင့်ပါ"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ချွတ်ယွင်းချက်အစီရင်ခံစာကို ဖန်သားပြင်ဓာတ်ပုံမှတ်တမ်းမပါဘဲ မျှဝေရန် တို့ပါ သို့မဟုတ် ဖန်သားပြင်ဓာတ်ပုံမှတ်တမ်းတင်ခြင်း ပြီးဆုံးသည်အထိ စောင့်ပါ"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"ချွတ်ယွင်းချက်အစီရင်ခံစာများတွင် သင့်အတွက် အရေးကြီးသည့် ဒေတာများ (အက်ပ်အသုံးပြုမှုနှင့် တည်နေရာအချက်အလက် ကဲ့သို့) ပါဝင်သည့် စနစ်၏မှတ်တမ်းဖိုင်မျိုးစုံပါဝင်ပါသည်။ ချွတ်ယွင်းချက်အစီရင်ခံစာများကို သင်ယုံကြည်စိတ်ချရသည့်လူများ၊ အက်ပ်များနှင့်သာ မျှဝေပါ။"</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"နောက်ထပ်မပြပါနှင့်"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"ချွတ်ယွင်းမှု အစီရင်ခံစာများ"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"ချွတ်ယွင်းချက် အစီရင်ခံစာကို ဖတ်၍မရပါ"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"ဇစ်ဖိုင်သို့ ချွတ်ယွင်းချက် အစီရင်ခံစာအသေးစိတ် အချက်အလက်များကို ထည့်၍မရပါ"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"အမည်မဲ့"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"အသေးစိတ်များ"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"မျက်နှာပြင် လျှပ်တစ်ပြက်ပုံ"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"ဖန်သားပြင်ဓာတ်ပုံ အောင်မြင်စွာရိုက်ပြီးပါပြီ။"</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"မျက်နှာပြင် လျှပ်တစ်ပြက်ပုံ မရိုက်နိုင်ပါ"</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"ချွတ်ယွင်းမှုအစီရင်ခံချက် <xliff:g id="ID">#%d</xliff:g> အသေးစိတ်များ"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"ဖိုင်အမည်"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"ချွတ်ယွင်းချက် ခေါင်းစဉ်"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"ချွတ်ယွင်းချက် အကျဉ်းချုပ်"</string> + <string name="save" msgid="4781509040564835759">"သိမ်းရန်"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"ချွတ်ယွင်းချက်မှတ်တမ်း မျှဝေပါ"</string> +</resources> diff --git a/packages/Shell/res/values-ta/strings.xml b/packages/Shell/res/values-ta/strings.xml new file mode 100644 index 000000000000..a906abede3fd --- /dev/null +++ b/packages/Shell/res/values-ta/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2013 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="3701846017049540910">"ஷெல்"</string> + <string name="bugreport_notification_channel" msgid="2574150205913861141">"பிழை அறிக்கைகள்"</string> + <string name="bugreport_in_progress_title" msgid="4311705936714972757">"பிழை அறிக்கை <xliff:g id="ID">#%d</xliff:g> உருவாக்கப்படுகிறது"</string> + <string name="bugreport_finished_title" msgid="4429132808670114081">"பிழை அறிக்கை <xliff:g id="ID">#%d</xliff:g> எடுக்கப்பட்டது"</string> + <string name="bugreport_updating_title" msgid="4423539949559634214">"பிழை அறிக்கையில் விவரங்களைச் சேர்க்கிறது"</string> + <string name="bugreport_updating_wait" msgid="3322151947853929470">"காத்திருக்கவும்…"</string> + <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"பிழை அறிக்கை சிறிது நேரத்தில் மொபைலில் தோன்றும்"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"பிழை அறிக்கையைப் பகிர, தேர்ந்தெடுக்கவும்"</string> + <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"பிழை அறிக்கையைப் பகிர, தட்டவும்"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"ஸ்கிரீன்ஷாட் இன்றி பிழை அறிக்கையை பகிர தேர்ந்தெடுக்கவும்/ஸ்கிரீன்ஷாட் முடியும்வரை காத்திருக்கவும்"</string> + <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ஸ்கிரீன்ஷாட் இல்லாமல் பிழை அறிக்கையைப் பகிர, தட்டவும் அல்லது ஸ்கிரீன்ஷாட் முடியும்வரை காத்திருக்கவும்"</string> + <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ஸ்கிரீன்ஷாட் இல்லாமல் பிழை அறிக்கையைப் பகிர, தட்டவும் அல்லது ஸ்கிரீன்ஷாட் முடியும்வரை காத்திருக்கவும்"</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"பிழை அறிக்கைகளில் முறைமையின் பல்வேறு பதிவுக் கோப்புகளின் தரவு (இதில் முக்கியமானவை என நீங்கள் கருதும் பயன்பாடின் உபயோகம், இருப்பிடத் தரவு போன்றவை அடங்கும்) இருக்கும். நீங்கள் நம்பும் நபர்கள் மற்றும் பயன்பாடுகளுடன் மட்டும் பிழை அறிக்கைகளைப் பகிரவும்."</string> + <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"மீண்டும் காட்டாதே"</string> + <string name="bugreport_storage_title" msgid="5332488144740527109">"பிழை அறிக்கைகள்"</string> + <string name="bugreport_unreadable_text" msgid="586517851044535486">"பிழை அறிக்கையைப் படிக்க முடியவில்லை"</string> + <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"பிழை அறிக்கை விவரங்களை ஜிப் கோப்பில் சேர்க்க முடியவில்லை"</string> + <string name="bugreport_unnamed" msgid="2800582406842092709">"பெயரிடப்படாதது"</string> + <string name="bugreport_info_action" msgid="2158204228510576227">"விவரங்கள்"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"ஸ்கிரீன்ஷாட்"</string> + <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"ஸ்கிரீன்ஷாட் எடுக்கப்பட்டது."</string> + <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"ஸ்கிரீன் ஷாட்டை எடுக்க முடியவில்லை."</string> + <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"பிழை அறிக்கை <xliff:g id="ID">#%d</xliff:g> இன் விவரங்கள்"</string> + <string name="bugreport_info_name" msgid="4414036021935139527">"கோப்புப்பெயர்"</string> + <string name="bugreport_info_title" msgid="2306030793918239804">"பிழை தலைப்பு"</string> + <string name="bugreport_info_description" msgid="5072835127481627722">"பிழை குறித்த சுருக்க விவரம்"</string> + <string name="save" msgid="4781509040564835759">"சேமி"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"பிழை அறிக்கையைப் பகிர்"</string> +</resources> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 7b8aba451e27..b55854131709 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -720,10 +720,9 @@ <service android:name=".controls.controller.AuxiliaryPersistenceWrapper$DeletionJobService" android:permission="android.permission.BIND_JOB_SERVICE"/> - <!-- started from ControlsFavoritingActivity --> + <!-- started from ControlsRequestReceiver --> <activity android:name=".controls.management.ControlsRequestDialog" - android:exported="true" android:theme="@style/Theme.ControlsRequestDialog" android:finishOnCloseSystemDialogs="true" android:showForAllUsers="true" diff --git a/packages/SystemUI/res-product/values-in/strings.xml b/packages/SystemUI/res-product/values-in/strings.xml index 2e0580f568f9..1451e2c063c9 100644 --- a/packages/SystemUI/res-product/values-in/strings.xml +++ b/packages/SystemUI/res-product/values-in/strings.xml @@ -26,10 +26,10 @@ <string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"Tidak ada kartu SIM dalam tablet."</string> <string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"Tidak ada kartu SIM dalam ponsel."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"Kode PIN tidak cocok"</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, tablet ini akan disetel ulang, sehingga semua datanya akan dihapus."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, ponsel ini akan disetel ulang, sehingga semua datanya akan dihapus."</string> - <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Tablet ini akan disetel ulang, sehingga semua datanya akan dihapus."</string> - <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Ponsel ini akan disetel ulang, sehingga semua datanya akan dihapus."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, tablet ini akan direset, sehingga semua datanya akan dihapus."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, ponsel ini akan direset, sehingga semua datanya akan dihapus."</string> + <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Tablet ini akan direset, sehingga semua datanya akan dihapus."</string> + <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Ponsel ini akan direset, sehingga semua datanya akan dihapus."</string> <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string> <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string> <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string> diff --git a/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml new file mode 100644 index 000000000000..998db3b44b19 --- /dev/null +++ b/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml @@ -0,0 +1,36 @@ +<!-- + 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="22" + android:viewportHeight="17" + android:width="22dp" + android:height="17dp"> + <group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/> + <path android:fillColor="#FF000000" + android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/> + </group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/> + </group> + <path android:fillColor="#FF000000" + android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/> + </group> +</vector> + diff --git a/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml new file mode 100644 index 000000000000..998db3b44b19 --- /dev/null +++ b/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml @@ -0,0 +1,36 @@ +<!-- + 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="22" + android:viewportHeight="17" + android:width="22dp" + android:height="17dp"> + <group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/> + <path android:fillColor="#FF000000" + android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/> + </group> + <group> + <path android:fillColor="#FF000000" + android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/> + </group> + <path android:fillColor="#FF000000" + android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/> + </group> +</vector> + diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index f906cf2a8f93..5fa5dc332c7a 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -232,7 +232,7 @@ <string name="not_default_data_content_description" msgid="6757881730711522517">"لم يتم الضبط على استخدام البيانات"</string> <string name="cell_data_off" msgid="4886198950247099526">"غير مفعّلة"</string> <string name="accessibility_bluetooth_tether" msgid="6327291292208790599">"التوصيل عبر البلوتوث"</string> - <string name="accessibility_airplane_mode" msgid="1899529214045998505">"وضع الطائرة."</string> + <string name="accessibility_airplane_mode" msgid="1899529214045998505">"وضع الطيران."</string> <string name="accessibility_vpn_on" msgid="8037549696057288731">"الشبكة الافتراضية الخاصة (VPN) قيد التفعيل."</string> <string name="accessibility_no_sims" msgid="5711270400476534667">"ليس هناك شريحة SIM."</string> <string name="carrier_network_change_mode" msgid="5174141476991149918">"جارٍ تغيير شبكة مشغِّل شبكة الجوّال."</string> @@ -267,10 +267,10 @@ <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"تم تفعيل Wifi."</string> <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"الجوّال <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string> <string name="accessibility_quick_settings_battery" msgid="533594896310663853">"البطارية <xliff:g id="STATE">%s</xliff:g>."</string> - <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"إيقاف وضع الطائرة."</string> - <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"تفعيل وضع الطائرة."</string> - <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"تم إيقاف وضع الطائرة."</string> - <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"تم تفعيل وضع الطائرة."</string> + <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"إيقاف وضع الطيران."</string> + <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"تفعيل وضع الطيران."</string> + <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"تم إيقاف وضع الطيران."</string> + <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"تم تفعيل وضع الطيران."</string> <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"كتم الصوت تمامًا"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"المنبِّهات فقط"</string> <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"عدم الإزعاج"</string> @@ -664,7 +664,7 @@ <string name="status_bar_ethernet" msgid="5690979758988647484">"إيثرنت"</string> <string name="status_bar_alarm" msgid="87160847643623352">"المنبّه"</string> <string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string> - <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطائرة"</string> + <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string> <string name="add_tile" msgid="6239678623873086686">"إضافة فئة"</string> <string name="broadcast_tile" msgid="5224010633596487481">"إرسال فئة"</string> <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g> إلا إذا أوقفت هذا قبل الموعد"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 32c2e9c2d45e..933bb044b5dc 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -403,7 +403,7 @@ <item quantity="one">%d cihaz</item> </plurals> <string name="quick_settings_notifications_label" msgid="3379631363952582758">"Bildirişlər"</string> - <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"İşartı"</string> + <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Fənər"</string> <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera istifadə olunur"</string> <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobil data"</string> <string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Data istifadəsi"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 78ab320b6556..529e37a59e07 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -707,7 +707,7 @@ <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactiva les notificacions"</string> <string name="inline_keep_showing_app" msgid="4393429060390649757">"Vols continuar rebent notificacions d\'aquesta aplicació?"</string> <string name="notification_silence_title" msgid="8608090968400832335">"Silenci"</string> - <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string> + <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminat"</string> <string name="notification_bubble_title" msgid="8330481035191903164">"Bombolla"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sense so ni vibració"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sense so ni vibració i es mostra més avall a la secció de converses"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 4b1e733751ff..9d661badd48e 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -95,7 +95,7 @@ <string name="screenrecord_description" msgid="1123231719680353736">"Při nahrávání může systém Android zaznamenávat citlivé údaje, které jsou viditelné na obrazovce nebo které jsou přehrávány na zařízení. Týká se to hesel, údajů o platbě, fotek, zpráv a zvuků."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrát zvuk"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk zařízení"</string> - <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk ze zařízení, například hudba, hovory a vyzváněcí tóny"</string> + <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk ze zařízení, například hudba, hovory a vyzvánění"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Mikrofon"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Zvuk a mikrofon zařízení"</string> <string name="screenrecord_start" msgid="330991441575775004">"Spustit"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 32b335ec480a..4137f8e75a26 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -691,7 +691,7 @@ <string name="notification_channel_minimized" msgid="6892672757877552959">"Αυτές οι ειδοποιήσεις θα ελαχιστοποιηθούν"</string> <string name="notification_channel_silenced" msgid="1995937493874511359">"Αυτές οι ειδοποιήσεις θα εμφανίζονται σιωπηλά"</string> <string name="notification_channel_unsilenced" msgid="94878840742161152">"Αυτές οι ειδοποιήσεις θα σας ενημερώνουν"</string> - <string name="inline_blocking_helper" msgid="2891486013649543452">"Συνήθως απορρίπτετε αυτές τις ειδοποιήσεις. \nΝα εξακολουθήσουν να εμφανίζονται;"</string> + <string name="inline_blocking_helper" msgid="2891486013649543452">"Συνήθως παραβλέπετε αυτές τις ειδοποιήσεις. \nΝα εξακολουθήσουν να εμφανίζονται;"</string> <string name="inline_done_button" msgid="6043094985588909584">"Τέλος"</string> <string name="inline_ok_button" msgid="603075490581280343">"Εφαρμογή"</string> <string name="inline_keep_showing" msgid="8736001253507073497">"Να συνεχίσουν να εμφανίζονται αυτές οι ειδοποιήσεις;"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 3cbf9a174fcf..aa4adaaf2fc5 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -98,7 +98,7 @@ <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sonido de tu dispositivo, como música, llamadas y tonos de llamada"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Micrófono"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Audio y micrófono del dispositivo"</string> - <string name="screenrecord_start" msgid="330991441575775004">"Empezar"</string> + <string name="screenrecord_start" msgid="330991441575775004">"Iniciar"</string> <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Grabando pantalla"</string> <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Grabando pantalla y audio"</string> <string name="screenrecord_taps_label" msgid="1595690528298857649">"Mostrar toques en la pantalla"</string> @@ -395,7 +395,7 @@ <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Conectado (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería)"</string> <string name="quick_settings_connecting" msgid="2381969772953268809">"Conectando..."</string> <string name="quick_settings_tethering_label" msgid="5257299852322475780">"Compartir conexión"</string> - <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Zona Wi-Fi"</string> + <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Punto de acceso"</string> <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Activando…"</string> <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Ahorro de datos activado"</string> <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976"> @@ -660,7 +660,7 @@ <string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template_far" msgid="3561752195856839456">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"Ajustes rápidos, <xliff:g id="TITLE">%s</xliff:g>."</string> - <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Zona Wi-Fi"</string> + <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Punto de acceso"</string> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunos"</string> <string name="tuner_warning" msgid="1861736288458481650">"El configurador de UI del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index e4b63c8dfd87..9598ddddb8f7 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -552,7 +552,7 @@ <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta zaude eta hark sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> eta <xliff:g id="VPN_APP_1">%2$s</xliff:g> aplikazioetara konektatuta zaude, eta haiek sareko jarduerak gainbegira ditzakete, mezu elektronikoak, aplikazioak eta webguneak barne."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora dago konektatuta laneko profila, eta aplikazio horrek sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string> - <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta duzu profil pertsonala, eta aplikazio horrek sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string> + <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta daukazu profil pertsonala, eta aplikazio horrek sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string> <string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> aplikazioak kudeatzen du gailu hau."</string> <string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundeak <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> erabiltzen du gailua kudeatzeko."</string> <string name="monitoring_description_do_body" msgid="7700878065625769970">"Gailuko ezarpenak, enpresa-sarbidea, aplikazioak eta datuak gainbegira eta kudea ditzake administratzaileak, baita gailuaren kokapen-informazioa ere."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 703d5ec070eb..410edfb74eb7 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -800,7 +800,7 @@ <string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"برگشت"</string> <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"اعلانها"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"میانبرهای صفحهکلید"</string> - <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تغییر طرحبندی صفحهکلید"</string> + <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تغییر جانمایی صفحهکلید"</string> <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"برنامهها"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"دستیار"</string> <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"مرورگر"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index f01ac1da64bd..05cf98431759 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -428,7 +428,7 @@ <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Az NFC ki van kapcsolva"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Az NFC be van kapcsolva"</string> <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Képernyő rögzítése"</string> - <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Kezdés"</string> + <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Indítás"</string> <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Leállítás"</string> <string name="media_seamless_remote_device" msgid="177033467332920464">"Eszköz"</string> <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Váltás az alkalmazások között felfelé csúsztatással"</string> diff --git a/packages/SystemUI/res/values-mcc310-mnc004/strings.xml b/packages/SystemUI/res/values-mcc310-mnc004/strings.xml new file mode 100644 index 000000000000..f8ed0c01fa83 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc004/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * 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. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] --> + <string name="data_connection_5g_plus" translatable="false">5G UW</string> +</resources> diff --git a/packages/SystemUI/res/values-mcc311-mnc480/strings.xml b/packages/SystemUI/res/values-mcc311-mnc480/strings.xml new file mode 100644 index 000000000000..f8ed0c01fa83 --- /dev/null +++ b/packages/SystemUI/res/values-mcc311-mnc480/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * 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. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] --> + <string name="data_connection_5g_plus" translatable="false">5G UW</string> +</resources> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 789525450c23..b16427624cc6 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -403,7 +403,7 @@ <item quantity="one">%d peranti</item> </plurals> <string name="quick_settings_notifications_label" msgid="3379631363952582758">"Pemberitahuan"</string> - <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampu suluh"</string> + <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampu Suluh"</string> <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera sedang digunakan"</string> <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Data mudah alih"</string> <string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Penggunaan data"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 7b84193632dc..f003d270549c 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -500,8 +500,8 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"ब्याट्री सेभर अन छ"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"प्रदर्शन र पृष्ठभूमि डेटा घटाउँनुहोस्"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ब्याट्री सेभर अफ गर्नुहोस्"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, तस्बिर, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"यो कार्य प्रदान गर्ने सेवाले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, तस्बिर, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, फोटो, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"यो कार्य प्रदान गर्ने सेवाले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, फोटो, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मार्फत रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"फेरि नदेखाउनुहोस्"</string> @@ -714,7 +714,7 @@ <string name="notification_channel_summary_default" msgid="3282930979307248890">"फोनको सेटिङका आधारमा घन्टी बज्न वा कम्पन हुन सक्छ"</string> <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फोनको सेटिङका आधारमा घन्टी बज्न वा कम्पन हुन सक्छ। <xliff:g id="APP_NAME">%1$s</xliff:g> का वार्तालापहरू पूर्वनिर्धारित रूपमा बबलमा देखाइन्छन्।"</string> <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"फ्लोटिङ सर्टकटमार्फत यो सामग्रीतर्फ तपाईंको ध्यान आकर्षित गर्दछ।"</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"वार्तालाप खण्डको सिरानमा देखा पर्छ, तैरने बबलका रूपमा देखा पर्छ, लक स्क्रिनमा प्रोफाइल तस्बिर देखाइन्छ"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"वार्तालाप खण्डको सिरानमा देखा पर्छ, तैरने बबलका रूपमा देखा पर्छ, लक स्क्रिनमा प्रोफाइल फोटो देखाइन्छ"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"सेटिङ"</string> <string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा वार्तालापसम्बन्धी सुविधा प्रयोग गर्न मिल्दैन"</string> @@ -1024,7 +1024,7 @@ <string name="priority_onboarding_title" msgid="2893070698479227616">"वार्तालापको प्राथमिकता निर्धारण गरी \"महत्त्वपूर्ण\" बनाइयो"</string> <string name="priority_onboarding_behavior" msgid="5342816047020432929">"महत्वपूर्ण वार्तालापहरू:"</string> <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"वार्तालाप खण्डको सिरानमा देखिने छन्"</string> - <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"लक स्क्रिनमा प्रोफाइल तस्बिर देखाउने छन्"</string> + <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"लक स्क्रिनमा प्रोफाइल फोटो देखाउने छन्"</string> <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"एपहरूमाथि तैरिने बबलका रूपमा देखाइयोस्"</string> <string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"बाधा नपुऱ्याउनुहोस् मोडलाई बेवास्ता गरियोस्"</string> <string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"बुझेँ"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 679be33181ba..4631559ad5f8 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -390,7 +390,7 @@ <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Kleuren omkeren"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"Modus voor kleurcorrectie"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellingen"</string> - <string name="quick_settings_done" msgid="2163641301648855793">"Gereed"</string> + <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string> <string name="quick_settings_connected" msgid="3873605509184830379">"Verbonden"</string> <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Verbonden, batterij <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="quick_settings_connecting" msgid="2381969772953268809">"Verbinding maken…"</string> @@ -692,7 +692,7 @@ <string name="notification_channel_silenced" msgid="1995937493874511359">"Deze meldingen worden zonder geluid weergegeven"</string> <string name="notification_channel_unsilenced" msgid="94878840742161152">"Deze meldingen stellen je op de hoogte"</string> <string name="inline_blocking_helper" msgid="2891486013649543452">"Meestal sluit je deze meldingen. \nWil je ze blijven weergeven?"</string> - <string name="inline_done_button" msgid="6043094985588909584">"Gereed"</string> + <string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string> <string name="inline_ok_button" msgid="603075490581280343">"Toepassen"</string> <string name="inline_keep_showing" msgid="8736001253507073497">"Deze meldingen blijven weergeven?"</string> <string name="inline_stop_button" msgid="2453460935438696090">"Meldingen stoppen"</string> @@ -739,7 +739,7 @@ <string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Meldingen van dit kanaal toestaan"</string> <string name="notification_more_settings" msgid="4936228656989201793">"Meer instellingen"</string> <string name="notification_app_settings" msgid="8963648463858039377">"Aanpassen"</string> - <string name="notification_done" msgid="6215117625922713976">"Gereed"</string> + <string name="notification_done" msgid="6215117625922713976">"Klaar"</string> <string name="inline_undo" msgid="9026953267645116526">"Ongedaan maken"</string> <string name="demote" msgid="6225813324237153980">"Deze melding markeren als geen gesprek"</string> <string name="notification_conversation_favorite" msgid="1905240206975921907">"Belangrijk gesprek"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 443c8f15f400..5d7a6a18acb4 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -1021,7 +1021,7 @@ <string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Przenieś w lewy dolny róg"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Przenieś w prawy dolny róg"</string> <string name="bubble_dismiss_text" msgid="1314082410868930066">"Zamknij dymek"</string> - <string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nie wyświetlaj rozmowy jako dymku"</string> + <string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nie wyświetlaj rozmowy jako dymka"</string> <string name="bubbles_user_education_title" msgid="5547017089271445797">"Czatuj, korzystając z dymków"</string> <string name="bubbles_user_education_description" msgid="1160281719576715211">"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."</string> <string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Zarządzaj dymkami w dowolnym momencie"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 97fb0323c947..60ef3a2561b2 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -480,7 +480,7 @@ <string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string> - <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo!"</string> + <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo"</string> <string name="guest_notification_title" msgid="4434456703930764167">"Përdorues vizitor"</string> <string name="guest_notification_text" msgid="4202692942089571351">"Për të fshirë aplikacionet dhe të dhënat, hiqe përdoruesin vizitor"</string> <string name="guest_notification_remove_action" msgid="4153019027696868099">"HIQ VIZITORIN"</string> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 1e556a3ed402..58d5776543a9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -301,7 +301,7 @@ public class BubbleExpandedView extends LinearLayout { mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */, true /* singleTaskInstance */, false /* usePublicVirtualDisplay*/, - true /* disableSurfaceViewBackgroundLayer */); + true /* disableSurfaceViewBackgroundLayer */, true /* useTrustedDisplay */); // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt index 1bda841d4a63..d930c98cabe1 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt @@ -87,6 +87,10 @@ class ControlsFavoritePersistenceWrapper( * @param list a list of favorite controls. The list will be stored in the same order. */ fun storeFavorites(structures: List<StructureInfo>) { + if (structures.isEmpty() && !file.exists()) { + // Do not create a new file to store nothing + return + } executor.execute { Log.d(TAG, "Saving data to file: $file") val atomicFile = AtomicFile(file) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 15c60921cca5..865b11f7b738 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -11,6 +11,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter @@ -22,6 +23,7 @@ import com.android.systemui.util.Utils import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.concurrency.DelayableExecutor +import java.util.TreeMap import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton @@ -103,9 +105,7 @@ class MediaCarouselController @Inject constructor( private val mediaCarousel: MediaScrollView private val mediaCarouselScrollHandler: MediaCarouselScrollHandler val mediaFrame: ViewGroup - val mediaPlayers: MutableMap<String, MediaControlPanel> = mutableMapOf() private lateinit var settingsButton: View - private val mediaData: MutableMap<String, MediaData> = mutableMapOf() private val mediaContent: ViewGroup private val pageIndicator: PageIndicator private val visualStabilityCallback: VisualStabilityManager.Callback @@ -123,7 +123,7 @@ class MediaCarouselController @Inject constructor( set(value) { if (field != value) { field = value - for (player in mediaPlayers.values) { + for (player in MediaPlayerData.players()) { player.setListening(field) } } @@ -168,20 +168,17 @@ class MediaCarouselController @Inject constructor( true /* persistent */) mediaManager.addListener(object : MediaDataManager.Listener { override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - oldKey?.let { mediaData.remove(it) } if (!data.active && !Utils.useMediaResumption(context)) { // This view is inactive, let's remove this! This happens e.g when dismissing / // timing out a view. We still have the data around because resumption could // be on, but we should save the resources and release this. onMediaDataRemoved(key) } else { - mediaData.put(key, data) addOrUpdatePlayer(key, oldKey, data) } } override fun onMediaDataRemoved(key: String) { - mediaData.remove(key) removePlayer(key) } }) @@ -224,53 +221,36 @@ class MediaCarouselController @Inject constructor( } private fun reorderAllPlayers() { - for (mediaPlayer in mediaPlayers.values) { - val view = mediaPlayer.view?.player - if (mediaPlayer.isPlaying && mediaContent.indexOfChild(view) != 0) { - mediaContent.removeView(view) - mediaContent.addView(view, 0) + mediaContent.removeAllViews() + for (mediaPlayer in MediaPlayerData.players()) { + mediaPlayer.view?.let { + mediaContent.addView(it.player) } } mediaCarouselScrollHandler.onPlayersChanged() } private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) { - // If the key was changed, update entry - val oldData = mediaPlayers[oldKey] - if (oldData != null) { - val oldData = mediaPlayers.remove(oldKey) - mediaPlayers.put(key, oldData!!)?.let { - Log.wtf(TAG, "new key $key already exists when migrating from $oldKey") - } - } - var existingPlayer = mediaPlayers[key] + val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey) if (existingPlayer == null) { - existingPlayer = mediaControlPanelFactory.get() - existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), - mediaContent)) - existingPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions - mediaPlayers[key] = existingPlayer + var newPlayer = mediaControlPanelFactory.get() + newPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) + newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions + MediaPlayerData.addMediaPlayer(key, data, newPlayer) val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - existingPlayer.view?.player?.setLayoutParams(lp) - existingPlayer.bind(data) - existingPlayer.setListening(currentlyExpanded) - updatePlayerToState(existingPlayer, noAnimation = true) - if (existingPlayer.isPlaying) { - mediaContent.addView(existingPlayer.view?.player, 0) - } else { - mediaContent.addView(existingPlayer.view?.player) - } + newPlayer.view?.player?.setLayoutParams(lp) + newPlayer.bind(data) + newPlayer.setListening(currentlyExpanded) + updatePlayerToState(newPlayer, noAnimation = true) + reorderAllPlayers() } else { existingPlayer.bind(data) - if (existingPlayer.isPlaying && - mediaContent.indexOfChild(existingPlayer.view?.player) != 0) { - if (visualStabilityManager.isReorderingAllowed) { - mediaContent.removeView(existingPlayer.view?.player) - mediaContent.addView(existingPlayer.view?.player, 0) - } else { - needsReordering = true - } + MediaPlayerData.addMediaPlayer(key, data, existingPlayer) + if (visualStabilityManager.isReorderingAllowed) { + reorderAllPlayers() + } else { + needsReordering = true } } updatePageIndicator() @@ -278,13 +258,13 @@ class MediaCarouselController @Inject constructor( mediaCarousel.requiresRemeasuring = true // Check postcondition: mediaContent should have the same number of children as there are // elements in mediaPlayers. - if (mediaPlayers.size != mediaContent.childCount) { + if (MediaPlayerData.players().size != mediaContent.childCount) { Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync") } } private fun removePlayer(key: String) { - val removed = mediaPlayers.remove(key) + val removed = MediaPlayerData.removeMediaPlayer(key) removed?.apply { mediaCarouselScrollHandler.onPrePlayerRemoved(removed) mediaContent.removeView(removed.view?.player) @@ -295,12 +275,7 @@ class MediaCarouselController @Inject constructor( } private fun recreatePlayers() { - // Note that this will scramble the order of players. Actively playing sessions will, at - // least, still be put in the front. If we want to maintain order, then more work is - // needed. - mediaData.forEach { - key, data -> - removePlayer(key) + MediaPlayerData.mediaData().forEach { (key, data) -> addOrUpdatePlayer(key = key, oldKey = null, data = data) } } @@ -338,7 +313,7 @@ class MediaCarouselController @Inject constructor( currentStartLocation = startLocation currentEndLocation = endLocation currentTransitionProgress = progress - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { updatePlayerToState(mediaPlayer, immediately) } maybeResetSettingsCog() @@ -387,7 +362,7 @@ class MediaCarouselController @Inject constructor( private fun updateCarouselDimensions() { var width = 0 var height = 0 - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { val controller = mediaPlayer.mediaViewController // When transitioning the view to gone, the view gets smaller, but the translation // Doesn't, let's add the translation @@ -449,7 +424,7 @@ class MediaCarouselController @Inject constructor( this.desiredLocation = desiredLocation this.desiredHostState = it currentlyExpanded = it.expansion > 0 - for (mediaPlayer in mediaPlayers.values) { + for (mediaPlayer in MediaPlayerData.players()) { if (animate) { mediaPlayer.mediaViewController.animatePendingStateChange( duration = duration, @@ -471,7 +446,7 @@ class MediaCarouselController @Inject constructor( } fun closeGuts() { - mediaPlayers.values.forEach { + MediaPlayerData.players().forEach { it.closeGuts(true) } } @@ -498,3 +473,50 @@ class MediaCarouselController @Inject constructor( } } } + +@VisibleForTesting +internal object MediaPlayerData { + private data class MediaSortKey( + val data: MediaData, + val updateTime: Long = 0, + val isPlaying: Boolean = false + ) + + private val comparator = + compareByDescending<MediaSortKey> { it.isPlaying } + .thenByDescending { it.data.isLocalSession } + .thenByDescending { !it.data.resumption } + .thenByDescending { it.updateTime } + + private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator) + private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf() + + fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel) { + removeMediaPlayer(key) + val sortKey = MediaSortKey(data, System.currentTimeMillis(), player.isPlaying()) + mediaData.put(key, sortKey) + mediaPlayers.put(sortKey, player) + } + + fun getMediaPlayer(key: String, oldKey: String?): MediaControlPanel? { + // If the key was changed, update entry + oldKey?.let { + if (it != key) { + mediaData.remove(it)?.let { sortKey -> mediaData.put(key, sortKey) } + } + } + return mediaData.get(key)?.let { mediaPlayers.get(it) } + } + + fun removeMediaPlayer(key: String) = mediaData.remove(key)?.let { mediaPlayers.remove(it) } + + fun mediaData() = mediaData.entries.map { e -> Pair(e.key, e.value.data) } + + fun players() = mediaPlayers.values + + @VisibleForTesting + fun clear() { + mediaData.clear() + mediaPlayers.clear() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index dafc52ad8025..d6a02687c905 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -82,6 +82,10 @@ data class MediaData( */ var resumeAction: Runnable?, /** + * Local or remote playback + */ + var isLocalSession: Boolean = true, + /** * Indicates that this player is a resumption player (ie. It only shows a play actions which * will start the app and start playing). */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 33475aca0bfb..7e246c803254 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -31,6 +31,7 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.media.MediaDescription import android.media.MediaMetadata +import android.media.session.MediaController import android.media.session.MediaSession import android.net.Uri import android.os.UserHandle @@ -337,7 +338,8 @@ class MediaDataManager( ) { val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) as MediaSession.Token? - val metadata = mediaControllerFactory.create(token).metadata + val mediaController = mediaControllerFactory.create(token) + val metadata = mediaController.metadata // Foreground and Background colors computed from album art val notif: Notification = sbn.notification @@ -429,6 +431,9 @@ class MediaDataManager( } } + val isLocalSession = mediaController.playbackInfo?.playbackType == + MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL ?: true + foregroundExecutor.execute { val resumeAction: Runnable? = mediaEntries[key]?.resumeAction val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true @@ -436,8 +441,8 @@ class MediaDataManager( onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null, - active, resumeAction = resumeAction, notificationKey = key, - hasCheckedForResume = hasCheckedForResume)) + active, resumeAction = resumeAction, isLocalSession = isLocalSession, + notificationKey = key, hasCheckedForResume = hasCheckedForResume)) } } @@ -481,7 +486,10 @@ class MediaDataManager( decoder, info, source -> decoder.isMutableRequired = true } } catch (e: IOException) { - e.printStackTrace() + Log.e(TAG, "Unable to load bitmap", e) + null + } catch (e: RuntimeException) { + Log.e(TAG, "Unable to load bitmap", e) null } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index d19fc013bd8d..8d4ca5e46fae 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -40,7 +40,6 @@ import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; -import android.content.res.Configuration; import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; @@ -104,7 +103,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements private final Rect mLastReportedBounds = new Rect(); private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; - private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); + private final Map<IBinder, PipWindowConfigurationCompact> mCompactState = new HashMap<>(); private final Divider mSplitDivider; // These callbacks are called on the update thread @@ -202,6 +201,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements */ private boolean mShouldDeferEnteringPip; + private @ActivityInfo.ScreenOrientation int mRequestedOrientation; + @Inject public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @@ -281,11 +282,13 @@ public class PipTaskOrganizer extends TaskOrganizer implements mPipUiEventLoggerLogger.log( PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); - final Configuration initialConfig = mInitialState.remove(mToken.asBinder()); - final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() + final PipWindowConfigurationCompact config = mCompactState.remove(mToken.asBinder()); + config.syncWithScreenOrientation(mRequestedOrientation, + mPipBoundsHandler.getDisplayRotation()); + final boolean orientationDiffers = config.getRotation() != mPipBoundsHandler.getDisplayRotation(); final WindowContainerTransaction wct = new WindowContainerTransaction(); - final Rect destinationBounds = initialConfig.windowConfiguration.getBounds(); + final Rect destinationBounds = config.getBounds(); final int direction = syncWithSplitScreenBounds(destinationBounds) ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; @@ -351,7 +354,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .start()); - mInitialState.remove(mToken.asBinder()); + mCompactState.remove(mToken.asBinder()); mExitingPip = true; } @@ -377,8 +380,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements mInPip = true; mExitingPip = false; mLeash = leash; - mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); + mCompactState.put(mToken.asBinder(), + new PipWindowConfigurationCompact(mTaskInfo.configuration.windowConfiguration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; + mRequestedOrientation = info.requestedOrientation; mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); @@ -521,6 +526,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); + mRequestedOrientation = info.requestedOrientation; + // check PictureInPictureParams for aspect ratio change. final PictureInPictureParams newParams = info.pictureInPictureParams; if (newParams == null || !applyPictureInPictureParams(newParams)) { Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams); @@ -558,8 +565,6 @@ public class PipTaskOrganizer extends TaskOrganizer implements } /** - * TODO(b/152809058): consolidate the display info handling logic in SysUI - * * @param destinationBoundsOut the current destination bounds will be populated to this param */ @SuppressWarnings("unchecked") @@ -963,9 +968,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds); pw.println(innerPrefix + "mInitialState:"); - for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) { + for (Map.Entry<IBinder, PipWindowConfigurationCompact> e : mCompactState.entrySet()) { pw.println(innerPrefix + " binder=" + e.getKey() - + " winConfig=" + e.getValue().windowConfiguration); + + " config=" + e.getValue()); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipWindowConfigurationCompact.java b/packages/SystemUI/src/com/android/systemui/pip/PipWindowConfigurationCompact.java new file mode 100644 index 000000000000..ba104d676cd2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipWindowConfigurationCompact.java @@ -0,0 +1,80 @@ +/* + * 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 com.android.systemui.pip; + +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import android.app.WindowConfiguration; +import android.content.pm.ActivityInfo; +import android.graphics.Rect; +import android.view.Surface; + +/** + * Compact {@link WindowConfiguration} for PiP usage and supports operations such as rotate. + */ +class PipWindowConfigurationCompact { + private @Surface.Rotation int mRotation; + private Rect mBounds; + + PipWindowConfigurationCompact(WindowConfiguration windowConfiguration) { + mRotation = windowConfiguration.getRotation(); + mBounds = windowConfiguration.getBounds(); + } + + @Surface.Rotation int getRotation() { + return mRotation; + } + + Rect getBounds() { + return mBounds; + } + + void syncWithScreenOrientation(@ActivityInfo.ScreenOrientation int screenOrientation, + @Surface.Rotation int displayRotation) { + if (mBounds.top != 0 || mBounds.left != 0) { + // Supports fullscreen bounds like (0, 0, width, height) only now. + return; + } + boolean rotateNeeded = false; + if (ActivityInfo.isFixedOrientationPortrait(screenOrientation) + && (mRotation == ROTATION_90 || mRotation == ROTATION_270)) { + mRotation = ROTATION_0; + rotateNeeded = true; + } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation) + && (mRotation == ROTATION_0 || mRotation == ROTATION_180)) { + mRotation = ROTATION_90; + rotateNeeded = true; + } else if (screenOrientation == SCREEN_ORIENTATION_UNSPECIFIED + && mRotation != displayRotation) { + mRotation = displayRotation; + rotateNeeded = true; + } + if (rotateNeeded) { + mBounds.set(0, 0, mBounds.height(), mBounds.width()); + } + } + + @Override + public String toString() { + return "PipWindowConfigurationCompact(rotation=" + mRotation + + " bounds=" + mBounds + ")"; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 8a2e4ae11878..9f0b1de21b52 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -26,9 +26,10 @@ import android.os.Debug; import android.util.Log; import android.view.Choreographer; +import androidx.dynamicanimation.animation.AnimationHandler; +import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler; import androidx.dynamicanimation.animation.SpringForce; -import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.util.FloatingContentCoordinator; @@ -74,9 +75,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** The region that all of PIP must stay within. */ private final Rect mFloatingAllowedArea = new Rect(); - private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider = - new SfVsyncFrameCallbackProvider(); - /** * Temporary bounds used when PIP is being dragged or animated. These bounds are applied to PIP * using {@link PipTaskOrganizer#scheduleUserResizePip}, so that we can animate shrinking into @@ -94,8 +92,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** Coordinator instance for resolving conflicts with other floating content. */ private FloatingContentCoordinator mFloatingContentCoordinator; - /** Callback that re-sizes PIP to the animated bounds. */ - private final Choreographer.FrameCallback mResizePipVsyncCallback; + private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = + ThreadLocal.withInitial(() -> { + FrameCallbackScheduler scheduler = runnable -> + Choreographer.getSfInstance().postFrameCallback(t -> runnable.run()); + AnimationHandler handler = new AnimationHandler(scheduler); + return handler; + }); /** * PhysicsAnimator instance for animating {@link #mTemporaryBounds} using physics animations. @@ -171,16 +174,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mSnapAlgorithm = snapAlgorithm; mFloatingContentCoordinator = floatingContentCoordinator; mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback); + mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler( + mSfAnimationHandlerThreadLocal.get()); - mResizePipVsyncCallback = l -> { + mResizePipUpdateListener = (target, values) -> { if (!mTemporaryBounds.isEmpty()) { mPipTaskOrganizer.scheduleUserResizePip( mBounds, mTemporaryBounds, null); } }; - - mResizePipUpdateListener = (target, values) -> - mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback); } @NonNull diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index ef73aa7cbbfe..badd8835cdd4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -80,6 +80,7 @@ public class PipResizeGestureHandler { private final Context mContext; private final PipBoundsHandler mPipBoundsHandler; private final PipMotionHelper mMotionHelper; + private final PipMenuActivityController mMenuController; private final int mDisplayId; private final Executor mMainExecutor; private final SysUiState mSysUiState; @@ -117,13 +118,14 @@ public class PipResizeGestureHandler { public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler, PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig, - PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, - Runnable updateMovementBoundsRunnable, SysUiState sysUiState, - PipUiEventLogger pipUiEventLogger) { + PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController pipMenuController, + Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, + SysUiState sysUiState, PipUiEventLogger pipUiEventLogger) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); mPipBoundsHandler = pipBoundsHandler; + mMenuController = pipMenuController; mMotionHelper = motionHelper; mPipTaskOrganizer = pipTaskOrganizer; mMovementBoundsSupplier = movementBoundsSupplier; @@ -322,6 +324,10 @@ public class PipResizeGestureHandler { mInputMonitor.pilferPointers(); } if (mThresholdCrossed) { + if (mMenuController.isMenuActivityVisible()) { + mMenuController.hideMenuWithoutResize(); + mMenuController.hideMenu(); + } final Rect currentPipBounds = mMotionHelper.getBounds(); mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y, mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 0127ff310b78..11e609b2ffef 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -232,7 +232,7 @@ public class PipTouchHandler { mSnapAlgorithm, floatingContentCoordinator); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, - deviceConfig, pipTaskOrganizer, this::getMovementBounds, + deviceConfig, pipTaskOrganizer, menuController, this::getMovementBounds, this::updateMovementBounds, sysUiState, pipUiEventLogger); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(), diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 853897fc54ae..c3c947bc40a8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -566,7 +566,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, Insets screenInsets, boolean showFlash) { if (mScreenshotLayout.isAttachedToWindow()) { - if (!mDismissAnimation.isRunning()) { // if we didn't already dismiss for another reason + // if we didn't already dismiss for another reason + if (mDismissAnimation == null || !mDismissAnimation.isRunning()) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED); } dismissScreenshot("new screenshot requested", true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 739d30c2a707..8cd82cc77530 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_W import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; import android.annotation.MainThread; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; @@ -57,6 +58,7 @@ import com.android.systemui.statusbar.dagger.StatusBarModule; 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.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; @@ -234,8 +236,17 @@ public class NotificationMediaManager implements Dumpable { NotificationVisibility visibility, boolean removedByUser, int reason) { - onNotificationRemoved(entry.getKey()); - mediaDataManager.onNotificationRemoved(entry.getKey()); + removeEntry(entry); + } + }); + + // Pending entries are never inflated, and will never generate a call to onEntryRemoved(). + // This can happen when notifications are added and canceled before inflation. Add this + // separate listener for cleanup, since media inflation occurs onPendingEntryAdded(). + notificationEntryManager.addCollectionListener(new NotifCollectionListener() { + @Override + public void onEntryCleanUp(@NonNull NotificationEntry entry) { + removeEntry(entry); } }); @@ -248,6 +259,11 @@ public class NotificationMediaManager implements Dumpable { mPropertiesChangedListener); } + private void removeEntry(NotificationEntry entry) { + onNotificationRemoved(entry.getKey()); + mMediaDataManager.onNotificationRemoved(entry.getKey()); + } + /** * Check if a state should be considered actively playing * @param state a PlaybackState diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt index 016f4de724b6..2a5424ce4ef7 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt @@ -20,6 +20,7 @@ import android.os.Looper import android.util.ArrayMap import android.util.Log import android.view.View +import androidx.dynamicanimation.animation.AnimationHandler import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FlingAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat @@ -124,6 +125,12 @@ class PhysicsAnimator<T> private constructor (target: T) { private var defaultFling: FlingConfig = globalDefaultFling /** + * AnimationHandler to use if it need custom AnimationHandler, if this is null, it will use + * the default AnimationHandler in the DynamicAnimation. + */ + private var customAnimationHandler: AnimationHandler? = null + + /** * Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to * the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add * just one permanent update and end listener to the DynamicAnimations. @@ -447,6 +454,14 @@ class PhysicsAnimator<T> private constructor (target: T) { this.defaultFling = defaultFling } + /** + * Set the custom AnimationHandler for all aniatmion in this animator. Set this with null for + * restoring to default AnimationHandler. + */ + fun setCustomAnimationHandler(handler: AnimationHandler) { + this.customAnimationHandler = handler + } + /** Starts the animations! */ fun start() { startAction() @@ -501,10 +516,13 @@ class PhysicsAnimator<T> private constructor (target: T) { // springs) on this property before flinging. cancel(animatedProperty) + // Apply the custom animation handler if it not null + val flingAnim = getFlingAnimation(animatedProperty, target) + flingAnim.animationHandler = + customAnimationHandler ?: flingAnim.animationHandler + // Apply the configuration and start the animation. - getFlingAnimation(animatedProperty, target) - .also { flingConfig.applyToAnimation(it) } - .start() + flingAnim.also { flingConfig.applyToAnimation(it) }.start() } } @@ -516,6 +534,21 @@ class PhysicsAnimator<T> private constructor (target: T) { if (flingConfig == null) { // Apply the configuration and start the animation. val springAnim = getSpringAnimation(animatedProperty, target) + + // If customAnimationHander is exist and has not been set to the animation, + // it should set here. + if (customAnimationHandler != null && + springAnim.animationHandler != customAnimationHandler) { + // Cancel the animation before set animation handler + if (springAnim.isRunning) { + cancel(animatedProperty) + } + // Apply the custom animation handler if it not null + springAnim.animationHandler = + customAnimationHandler ?: springAnim.animationHandler + } + + // Apply the configuration and start the animation. springConfig.applyToAnimation(springAnim) animationStartActions.add(springAnim::start) } else { @@ -570,10 +603,13 @@ class PhysicsAnimator<T> private constructor (target: T) { } } + // Apply the custom animation handler if it not null + val springAnim = getSpringAnimation(animatedProperty, target) + springAnim.animationHandler = + customAnimationHandler ?: springAnim.animationHandler + // Apply the configuration and start the spring animation. - getSpringAnimation(animatedProperty, target) - .also { springConfig.applyToAnimation(it) } - .start() + springAnim.also { springConfig.applyToAnimation(it) }.start() } } }) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt index 861c6207f5b0..690b9a7248be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -78,4 +79,15 @@ class ControlsFavoritePersistenceWrapperTest : SysuiTestCase() { assertEquals(list, wrapper.readFavorites()) } + + @Test + fun testSaveEmptyOnNonExistingFile() { + if (file.exists()) { + file.delete() + } + + wrapper.storeFavorites(emptyList()) + + assertFalse(file.exists()) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index 492b33e3c4a6..2e794a40d238 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -83,8 +83,8 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { mManager.addListener(mListener); mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, - new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, false, - KEY, false); + new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true, + false, KEY, false); mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt new file mode 100644 index 000000000000..118cffc2d5b8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt @@ -0,0 +1,122 @@ +/* + * 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 com.android.systemui.media + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +public class MediaPlayerDataTest : SysuiTestCase() { + + companion object { + val LOCAL = true + val RESUMPTION = true + } + + @Before + fun setup() { + MediaPlayerData.clear() + } + + @Test + fun addPlayingThenRemote() { + val playerIsPlaying = mock(MediaControlPanel::class.java) + whenever(playerIsPlaying.isPlaying).thenReturn(true) + val dataIsPlaying = createMediaData(LOCAL, !RESUMPTION) + + val playerIsRemote = mock(MediaControlPanel::class.java) + whenever(playerIsRemote.isPlaying).thenReturn(false) + val dataIsRemote = createMediaData(!LOCAL, !RESUMPTION) + + MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) + MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(2) + assertThat(players).containsExactly(playerIsPlaying, playerIsRemote).inOrder() + } + + @Test + fun switchPlayersPlaying() { + val playerIsPlaying1 = mock(MediaControlPanel::class.java) + whenever(playerIsPlaying1.isPlaying).thenReturn(true) + val dataIsPlaying1 = createMediaData(LOCAL, !RESUMPTION) + + val playerIsPlaying2 = mock(MediaControlPanel::class.java) + whenever(playerIsPlaying2.isPlaying).thenReturn(false) + val dataIsPlaying2 = createMediaData(LOCAL, !RESUMPTION) + + MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1) + MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2) + + whenever(playerIsPlaying1.isPlaying).thenReturn(false) + whenever(playerIsPlaying2.isPlaying).thenReturn(true) + + MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1) + MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(2) + assertThat(players).containsExactly(playerIsPlaying2, playerIsPlaying1).inOrder() + } + + @Test + fun fullOrderTest() { + val playerIsPlaying = mock(MediaControlPanel::class.java) + whenever(playerIsPlaying.isPlaying).thenReturn(true) + val dataIsPlaying = createMediaData(LOCAL, !RESUMPTION) + + val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java) + whenever(playerIsPlayingAndRemote.isPlaying).thenReturn(true) + val dataIsPlayingAndRemote = createMediaData(!LOCAL, !RESUMPTION) + + val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java) + whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false) + val dataIsStoppedAndLocal = createMediaData(LOCAL, !RESUMPTION) + + val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java) + whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false) + val dataIsStoppedAndRemote = createMediaData(!LOCAL, !RESUMPTION) + + val playerCanResume = mock(MediaControlPanel::class.java) + whenever(playerCanResume.isPlaying).thenReturn(false) + val dataCanResume = createMediaData(LOCAL, RESUMPTION) + + MediaPlayerData.addMediaPlayer("3", dataIsStoppedAndLocal, playerIsStoppedAndLocal) + MediaPlayerData.addMediaPlayer("5", dataIsStoppedAndRemote, playerIsStoppedAndRemote) + MediaPlayerData.addMediaPlayer("4", dataCanResume, playerCanResume) + MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying) + MediaPlayerData.addMediaPlayer("2", dataIsPlayingAndRemote, playerIsPlayingAndRemote) + + val players = MediaPlayerData.players() + assertThat(players).hasSize(5) + assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote, + playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote).inOrder() + } + + private fun createMediaData(isLocalSession: Boolean, resumption: Boolean) = + MediaData(0, false, 0, null, null, null, null, null, emptyList(), emptyList<Int>(), "", + null, null, null, true, null, isLocalSession, resumption, null, false) +} diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 4fab067d1d3b..651f941be0b1 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -3109,6 +3109,14 @@ class AlarmManagerService extends SystemService { mPendingBackgroundAlarms.removeAt(i); } } + for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) { + final Alarm a = mPendingNonWakeupAlarms.get(i); + if (a.matches(operation, directReceiver)) { + // Don't set didRemove, since this doesn't impact the scheduled alarms. + mPendingNonWakeupAlarms.remove(i); + decrementAlarmCount(a.uid, 1); + } + } if (didRemove) { if (DEBUG_BATCH) { Slog.v(TAG, "remove(operation) changed bounds; rebatching"); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 678387c540ed..391923324d0c 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -4745,15 +4745,20 @@ class StorageManagerService extends IStorageManager.Stub } } - public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode) { + public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode, + int previousMode) { final long token = Binder.clearCallingIdentity(); try { if (mIsFuseEnabled) { // When using FUSE, we may need to kill the app if the op changes switch(code) { case OP_REQUEST_INSTALL_PACKAGES: - // Always kill regardless of op change, to remount apps /storage - killAppForOpChange(code, uid); + if (previousMode == MODE_ALLOWED || mode == MODE_ALLOWED) { + // If we transition to/from MODE_ALLOWED, kill the app to make + // sure it has the correct view of /storage. Changing between + // MODE_DEFAULT / MODE_ERRORED is a no-op + killAppForOpChange(code, uid); + } return; case OP_MANAGE_EXTERNAL_STORAGE: if (mode != MODE_ALLOWED) { diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index be080e5cce62..b5aea75d78a5 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -485,6 +485,9 @@ final class UiModeManagerService extends SystemService { * @return True if the new value is different from the old value. False otherwise. */ private boolean updateNightModeFromSettingsLocked(Context context, Resources res, int userId) { + if (mCarModeEnabled || mCar) { + return false; + } int oldNightMode = mNightMode; if (mSetupWizardComplete) { mNightMode = Secure.getIntForUser(context.getContentResolver(), @@ -1015,7 +1018,7 @@ final class UiModeManagerService extends SystemService { private void persistNightMode(int user) { // Only persist setting if not in car mode - if (mCarModeEnabled) return; + if (mCarModeEnabled || mCar) return; Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, mNightMode, user); Secure.putLongForUser(getContext().getContentResolver(), @@ -1028,7 +1031,7 @@ final class UiModeManagerService extends SystemService { private void persistNightModeOverrides(int user) { // Only persist setting if not in car mode - if (mCarModeEnabled) return; + if (mCarModeEnabled || mCar) return; Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE_OVERRIDE_ON, mOverrideNightModeOn ? 1 : 0, user); Secure.putIntForUser(getContext().getContentResolver(), @@ -1079,7 +1082,7 @@ final class UiModeManagerService extends SystemService { } // Override night mode in power save mode if not in car mode - if (mPowerSave && !mCarModeEnabled) { + if (mPowerSave && !mCarModeEnabled && !mCar) { uiMode &= ~Configuration.UI_MODE_NIGHT_NO; uiMode |= Configuration.UI_MODE_NIGHT_YES; } else { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7d88a8a4ec01..e1bb4cda8dc2 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1837,11 +1837,13 @@ public final class ActiveServices { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service + " type=" + resolvedType + " conn=" + connection.asBinder() + " flags=0x" + Integer.toHexString(flags)); + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller); if (callerApp == null) { throw new SecurityException( "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() + + " (pid=" + callingPid + ") when binding service " + service); } @@ -1881,19 +1883,19 @@ public final class ActiveServices { } if ((flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0 && !isCallerSystem) { - throw new SecurityException("Non-system caller (pid=" + Binder.getCallingPid() + throw new SecurityException("Non-system caller (pid=" + callingPid + ") set BIND_SCHEDULE_LIKE_TOP_APP when binding service " + service); } if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) { throw new SecurityException( - "Non-system caller " + caller + " (pid=" + Binder.getCallingPid() + "Non-system caller " + caller + " (pid=" + callingPid + ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service); } if ((flags & Context.BIND_ALLOW_INSTANT) != 0 && !isCallerSystem) { throw new SecurityException( - "Non-system caller " + caller + " (pid=" + Binder.getCallingPid() + "Non-system caller " + caller + " (pid=" + callingPid + ") set BIND_ALLOW_INSTANT when binding service " + service); } @@ -1909,7 +1911,7 @@ public final class ActiveServices { ServiceLookupResult res = retrieveServiceLocked(service, instanceName, resolvedType, callingPackage, - Binder.getCallingPid(), Binder.getCallingUid(), userId, true, + callingPid, callingUid, userId, true, callerFg, isBindExternal, allowInstant); if (res == null) { return 0; @@ -2069,7 +2071,7 @@ public final class ActiveServices { if (!s.mAllowWhileInUsePermissionInFgs) { s.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, - Binder.getCallingPid(), Binder.getCallingUid(), + callingPid, callingUid, service, s, false); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 44e3bbf91565..491579a5a294 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2218,17 +2218,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { try { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "meminfo", pw)) return; PriorityDump.dump(mPriorityDumper, fd, pw, args); } finally { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } } } @@ -2242,17 +2238,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { try { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "gfxinfo", pw)) return; mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args); } finally { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } } } @@ -2266,17 +2258,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { try { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "dbinfo", pw)) return; mActivityManagerService.dumpDbInfo(fd, pw, args); } finally { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } } } @@ -2322,9 +2310,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { try { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "cacheinfo", pw)) { @@ -2333,9 +2319,7 @@ public class ActivityManagerService extends IActivityManager.Stub mActivityManagerService.dumpBinderCacheContents(fd, pw, args); } finally { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } } } @@ -18630,14 +18614,14 @@ public class ActivityManagerService extends IActivityManager.Stub } } - Process.enableFreezer(false); + mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); final RemoteCallback intermediateCallback = new RemoteCallback( new RemoteCallback.OnResultListener() { @Override public void onResult(Bundle result) { finishCallback.sendResult(result); - Process.enableFreezer(true); + mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } }, null); @@ -20399,4 +20383,16 @@ public class ActivityManagerService extends IActivityManager.Stub Binder.restoreCallingIdentity(token); } } + + @Override + public boolean enableAppFreezer(boolean enable) { + int callerUid = Binder.getCallingUid(); + + // Only system can toggle the freezer state + if (callerUid == SYSTEM_UID) { + return mOomAdjuster.mCachedAppOptimizer.enableFreezer(enable); + } else { + throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state "); + } + } } diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index d9fde0f6728a..966038986791 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -208,6 +208,8 @@ public final class CachedAppOptimizer { @GuardedBy("mPhenotypeFlagLock") private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION; private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER; + @GuardedBy("this") + private int mFreezerDisableCount = 1; // Freezer is initially disabled, until enabled private final Random mRandom = new Random(); @GuardedBy("mPhenotypeFlagLock") @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE; @@ -420,25 +422,97 @@ public final class CachedAppOptimizer { } /** - * Determines whether the freezer is correctly supported by this system + * Enables or disabled the app freezer. + * @param enable Enables the freezer if true, disables it if false. + * @return true if the operation completed successfully, false otherwise. + */ + public synchronized boolean enableFreezer(boolean enable) { + if (!mUseFreezer) { + return false; + } + + if (enable) { + mFreezerDisableCount--; + + if (mFreezerDisableCount > 0) { + return true; + } else if (mFreezerDisableCount < 0) { + Slog.e(TAG_AM, "unbalanced call to enableFreezer, ignoring"); + mFreezerDisableCount = 0; + return false; + } + } else { + mFreezerDisableCount++; + + if (mFreezerDisableCount > 1) { + return true; + } + } + + try { + enableFreezerInternal(enable); + return true; + } catch (java.lang.RuntimeException e) { + if (enable) { + mFreezerDisableCount = 0; + } else { + mFreezerDisableCount = 1; + } + + Slog.e(TAG_AM, "Exception handling freezer state (enable: " + enable + "): " + + e.toString()); + } + + return false; + } + + /** + * Enable or disable the freezer. When enable == false all frozen processes are unfrozen, + * but aren't removed from the freezer. While in this state, processes can be added or removed + * by using Process.setProcessFrozen(), but they wouldn't be actually frozen until the freezer + * is enabled. If enable == true all processes in the freezer are frozen. + * + * @param enable Specify whether to enable (true) or disable (false) the freezer. + * + * @hide + */ + private static native void enableFreezerInternal(boolean enable); + + /** + * Informs binder that a process is about to be frozen. If freezer is enabled on a process via + * this method, this method will synchronously dispatch all pending transactions to the + * specified pid. This method will not add significant latencies when unfreezing. + * After freezing binder calls, binder will block all transaction to the frozen pid, and return + * an error to the sending process. + * + * @param pid the target pid for which binder transactions are to be frozen + * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze + * binder for the specificed pid. + * + * @throws RuntimeException in case a flush/freeze operation could not complete successfully. + */ + private static native void freezeBinder(int pid, boolean freeze); + + /** + * Determines whether the freezer is supported by this system */ public static boolean isFreezerSupported() { boolean supported = false; FileReader fr = null; try { - fr = new FileReader("/dev/freezer/frozen/freezer.killable"); - int i = fr.read(); + fr = new FileReader("/sys/fs/cgroup/freezer/cgroup.freeze"); + char state = (char) fr.read(); - if ((char) i == '1') { + if (state == '1' || state == '0') { supported = true; } else { - Slog.w(TAG_AM, "Freezer killability is turned off, disabling freezer"); + Slog.e(TAG_AM, "unexpected value in cgroup.freeze"); } } catch (java.io.FileNotFoundException e) { - Slog.d(TAG_AM, "Freezer.killable not present, disabling freezer"); + Slog.d(TAG_AM, "cgroup.freeze not present"); } catch (Exception e) { - Slog.d(TAG_AM, "Unable to read freezer.killable, disabling freezer: " + e.toString()); + Slog.d(TAG_AM, "unable to read cgroup.freeze: " + e.toString()); } if (fr != null) { @@ -471,6 +545,8 @@ public final class CachedAppOptimizer { if (mUseFreezer && mFreezeHandler == null) { Slog.d(TAG_AM, "Freezer enabled"); + enableFreezer(true); + if (!mCachedAppOptimizerThread.isAlive()) { mCachedAppOptimizerThread.start(); } @@ -479,6 +555,8 @@ public final class CachedAppOptimizer { Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), Process.THREAD_GROUP_SYSTEM); + } else { + enableFreezer(false); } } @@ -664,6 +742,13 @@ public final class CachedAppOptimizer { } if (!app.frozen) { + try { + freezeBinder(app.pid, false); + } catch (RuntimeException e) { + // TODO: it might be preferable to kill the target pid in this case + Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName); + } + if (DEBUG_FREEZER) { Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName); } @@ -976,6 +1061,14 @@ public final class CachedAppOptimizer { return; } + try { + freezeBinder(pid, true); + } catch (RuntimeException e) { + // TODO: it might be preferable to kill the target pid in this case + Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); + return; + } + if (pid == 0 || proc.frozen) { // Already frozen or not a real process, either one being // launched or one being killed diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c5bdb9edd069..6cecb6aedd45 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -39,6 +39,7 @@ import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD; import static android.app.AppOpsManager.OpEventProxyInfo; import static android.app.AppOpsManager.RestrictionBypass; import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING; @@ -2198,6 +2199,7 @@ public class AppOpsService extends IAppOpsService.Stub { updatePermissionRevokedCompat(uid, code, mode); } + int previousMode; synchronized (this) { final int defaultMode = AppOpsManager.opToDefaultMode(code); @@ -2206,12 +2208,14 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == defaultMode) { return; } + previousMode = AppOpsManager.MODE_DEFAULT; uidState = new UidState(uid); uidState.opModes = new SparseIntArray(); uidState.opModes.put(code, mode); mUidStates.put(uid, uidState); scheduleWriteLocked(); } else if (uidState.opModes == null) { + previousMode = AppOpsManager.MODE_DEFAULT; if (mode != defaultMode) { uidState.opModes = new SparseIntArray(); uidState.opModes.put(code, mode); @@ -2221,6 +2225,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) { return; } + previousMode = uidState.opModes.get(code); if (mode == defaultMode) { uidState.opModes.delete(code); if (uidState.opModes.size() <= 0) { @@ -2235,7 +2240,7 @@ public class AppOpsService extends IAppOpsService.Stub { } notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback); - notifyOpChangedSync(code, uid, null, mode); + notifyOpChangedSync(code, uid, null, mode, previousMode); } /** @@ -2414,11 +2419,12 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) { + private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode, + int previousMode) { final StorageManagerInternal storageManagerInternal = LocalServices.getService(StorageManagerInternal.class); if (storageManagerInternal != null) { - storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode); + storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode, previousMode); } } @@ -2450,11 +2456,13 @@ public class AppOpsService extends IAppOpsService.Stub { return; } + int previousMode = AppOpsManager.MODE_DEFAULT; synchronized (this) { UidState uidState = getUidStateLocked(uid, false); Op op = getOpLocked(code, uid, packageName, null, bypass, true); if (op != null) { if (op.mode != mode) { + previousMode = op.mode; op.mode = mode; if (uidState != null) { uidState.evalForegroundOps(mOpModeWatchers); @@ -2491,7 +2499,7 @@ public class AppOpsService extends IAppOpsService.Stub { this, repCbs, code, uid, packageName)); } - notifyOpChangedSync(code, uid, packageName, mode); + notifyOpChangedSync(code, uid, packageName, mode, previousMode); } private void notifyOpChanged(ArraySet<ModeCallback> callbacks, int code, @@ -2534,7 +2542,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private static ArrayList<ChangeRec> addChange(ArrayList<ChangeRec> reports, - int op, int uid, String packageName) { + int op, int uid, String packageName, int previousMode) { boolean duplicate = false; if (reports == null) { reports = new ArrayList<>(); @@ -2549,7 +2557,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (!duplicate) { - reports.add(new ChangeRec(op, uid, packageName)); + reports.add(new ChangeRec(op, uid, packageName, previousMode)); } return reports; @@ -2557,7 +2565,7 @@ public class AppOpsService extends IAppOpsService.Stub { private static HashMap<ModeCallback, ArrayList<ChangeRec>> addCallbacks( HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks, - int op, int uid, String packageName, ArraySet<ModeCallback> cbs) { + int op, int uid, String packageName, int previousMode, ArraySet<ModeCallback> cbs) { if (cbs == null) { return callbacks; } @@ -2568,7 +2576,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i=0; i<N; i++) { ModeCallback cb = cbs.valueAt(i); ArrayList<ChangeRec> reports = callbacks.get(cb); - ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName); + ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName, previousMode); if (changed != reports) { callbacks.put(cb, changed); } @@ -2580,11 +2588,13 @@ public class AppOpsService extends IAppOpsService.Stub { final int op; final int uid; final String pkg; + final int previous_mode; - ChangeRec(int _op, int _uid, String _pkg) { + ChangeRec(int _op, int _uid, String _pkg, int _previous_mode) { op = _op; uid = _uid; pkg = _pkg; + previous_mode = _previous_mode; } } @@ -2620,18 +2630,19 @@ public class AppOpsService extends IAppOpsService.Stub { for (int j = uidOpCount - 1; j >= 0; j--) { final int code = opModes.keyAt(j); if (AppOpsManager.opAllowsReset(code)) { + int previousMode = opModes.valueAt(j); opModes.removeAt(j); if (opModes.size() <= 0) { uidState.opModes = null; } for (String packageName : getPackagesForUid(uidState.uid)) { callbacks = addCallbacks(callbacks, code, uidState.uid, packageName, - mOpModeWatchers.get(code)); + previousMode, mOpModeWatchers.get(code)); callbacks = addCallbacks(callbacks, code, uidState.uid, packageName, - mPackageModeWatchers.get(packageName)); + previousMode, mPackageModeWatchers.get(packageName)); allChanges = addChange(allChanges, code, uidState.uid, - packageName); + packageName, previousMode); } } } @@ -2662,16 +2673,18 @@ public class AppOpsService extends IAppOpsService.Stub { Op curOp = pkgOps.valueAt(j); if (AppOpsManager.opAllowsReset(curOp.op) && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) { + int previousMode = curOp.mode; curOp.mode = AppOpsManager.opToDefaultMode(curOp.op); changed = true; uidChanged = true; final int uid = curOp.uidState.uid; callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, - mOpModeWatchers.get(curOp.op)); + previousMode, mOpModeWatchers.get(curOp.op)); callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, - mPackageModeWatchers.get(packageName)); + previousMode, mPackageModeWatchers.get(packageName)); - allChanges = addChange(allChanges, curOp.op, uid, packageName); + allChanges = addChange(allChanges, curOp.op, uid, packageName, + previousMode); curOp.removeAttributionsWithNoTime(); if (curOp.mAttributions.isEmpty()) { pkgOps.removeAt(j); @@ -2712,7 +2725,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i = 0; i < numChanges; i++) { ChangeRec change = allChanges.get(i); notifyOpChangedSync(change.op, change.uid, change.pkg, - AppOpsManager.opToDefaultMode(change.op)); + AppOpsManager.opToDefaultMode(change.op), change.previous_mode); } } } @@ -3400,7 +3413,19 @@ public class AppOpsService extends IAppOpsService.Stub { verifyIncomingOp(code); String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { - return AppOpsManager.MODE_IGNORED; + return AppOpsManager.MODE_IGNORED; + } + + // As a special case for OP_RECORD_AUDIO_HOTWORD, which we use only for attribution + // purposes and not as a check, also make sure that the caller is allowed to access + // the data gated by OP_RECORD_AUDIO. + // + // TODO: Revert this change before Android 12. + if (code == OP_RECORD_AUDIO_HOTWORD) { + int result = checkOperation(OP_RECORD_AUDIO, uid, packageName); + if (result != AppOpsManager.MODE_ALLOWED) { + return result; + } } RestrictionBypass bypass; diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 45f95fd3f663..22b0aebca6c4 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -476,8 +476,8 @@ import java.util.concurrent.atomic.AtomicBoolean; sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); } - /*package*/ void postSetModeOwnerPid(int pid) { - sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid); + /*package*/ void postSetModeOwnerPid(int pid, int mode) { + sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode); } /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { @@ -949,7 +949,9 @@ import java.util.concurrent.atomic.AtomicBoolean; synchronized (mDeviceStateLock) { if (mModeOwnerPid != msg.arg1) { mModeOwnerPid = msg.arg1; - updateSpeakerphoneOn("setNewModeOwner"); + if (msg.arg2 != AudioSystem.MODE_RINGTONE) { + updateSpeakerphoneOn("setNewModeOwner"); + } if (mModeOwnerPid != 0) { mBtHelper.disconnectBluetoothSco(mModeOwnerPid); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c4eca605206d..fe619690ae34 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3680,13 +3680,15 @@ public class AudioService extends IAudioService.Stub private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; - private String mPackage; + private final boolean mIsPrivileged; + private final String mPackage; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client - SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) { + SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { mCb = cb; mPid = pid; mUid = uid; + mIsPrivileged = isPrivileged; mPackage = caller; } @@ -3698,12 +3700,13 @@ public class AudioService extends IAudioService.Stub if (index < 0) { Log.w(TAG, "unregistered setMode() client died"); } else { - newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG); + newModeOwnerPid = setModeInt( + AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG); } } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); + mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode()); } public int getPid() { @@ -3729,6 +3732,10 @@ public class AudioService extends IAudioService.Stub public String getPackage() { return mPackage; } + + public boolean isPrivileged() { + return mIsPrivileged; + } } /** @see AudioManager#setMode(int) */ @@ -3780,18 +3787,19 @@ public class AudioService extends IAudioService.Stub + " without permission or being mode owner"); return; } - newModeOwnerPid = setModeInt( - mode, cb, callingPid, Binder.getCallingUid(), callingPackage); + newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(), + hasModifyPhoneStatePermission, callingPackage); } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); + mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode()); } // setModeInt() returns a valid PID if the audio mode was successfully set to // any mode other than NORMAL. @GuardedBy("mDeviceBroker.mSetModeLock") - private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) { + private int setModeInt( + int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", uid=" + uid + ", caller=" + caller + ")"); @@ -3843,7 +3851,7 @@ public class AudioService extends IAudioService.Stub } } else { if (hdlr == null) { - hdlr = new SetModeDeathHandler(cb, pid, uid, caller); + hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller); } // Register for client death notification try { @@ -3902,7 +3910,8 @@ public class AudioService extends IAudioService.Stub // change of mode may require volume to be re-applied on some devices updateAbsVolumeMultiModeDevices(oldMode, actualMode); - if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) { + if (actualMode == AudioSystem.MODE_IN_COMMUNICATION + && !hdlr.isPrivileged()) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, SENDMSG_QUEUE, @@ -6419,8 +6428,8 @@ public class AudioService extends IAudioService.Stub CHECK_MODE_FOR_UID_PERIOD_MS); break; } - // For now just log the fact that an app is hogging the audio mode. - // TODO(b/160260850): remove abusive app from audio mode stack. + setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(), + h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID"); mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 24661d69a78e..852868616afd 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -86,6 +86,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; +import android.util.EventLog; import android.util.IntArray; import android.util.Pair; import android.util.Slog; @@ -2191,10 +2192,16 @@ public final class DisplayManagerService extends SystemService { } } - if (callingUid == Process.SYSTEM_UID - || checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { - flags |= VIRTUAL_DISPLAY_FLAG_TRUSTED; - } else { + if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { + if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { + EventLog.writeEvent(0x534e4554, "162627132", callingUid, + "Attempt to create a trusted display without holding permission!"); + throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to " + + "create a trusted virtual display."); + } + } + + if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) { flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 817902d9d566..b61c6a7ca569 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -205,8 +205,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format("Successfully pushed rule set: %s", version)); + Slog.i( + TAG, + String.format( + "Successfully pushed rule set to version '%s' from '%s'", + version, ruleProvider)); } + FrameworkStatsLog.write( FrameworkStatsLog.INTEGRITY_RULES_PUSHED, success, @@ -324,13 +329,12 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { + getAllowedInstallers(packageInfo)); } IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata); - if (DEBUG_INTEGRITY_COMPONENT) { + if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) { Slog.i( TAG, - "Integrity check result: " - + result.getEffect() - + " due to " - + result.getMatchedRules()); + String.format( + "Integrity check of %s result: %s due to %s", + packageName, result.getEffect(), result.getMatchedRules())); } FrameworkStatsLog.write( @@ -673,8 +677,10 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages. List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps(); if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format( - "Rule provider system app list contains: %s", allowedRuleProviders)); + Slog.i( + TAG, + String.format( + "Rule provider system app list contains: %s", allowedRuleProviders)); } // Identify the package names in the caller list. @@ -730,9 +736,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private boolean integrityCheckIncludesRuleProvider() { return Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, - 0) + mContext.getContentResolver(), + Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, + 0) == 1; } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 9b356f0e8eef..803eb862e0f1 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -1937,7 +1937,8 @@ public class MediaSessionService extends SystemService implements Monitor { // Context#getPackageName() for getting package name that matches with the PID/UID, // but it doesn't tell which package has created the MediaController, so useless. return hasMediaControlPermission(controllerPid, controllerUid) - || hasEnabledNotificationListener(userId, controllerPackageName); + || hasEnabledNotificationListener( + userId, controllerPackageName, controllerUid); } finally { Binder.restoreCallingIdentity(token); } @@ -2001,29 +2002,29 @@ public class MediaSessionService extends SystemService implements Monitor { return resolvedUserId; } - private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName) - throws RemoteException { - // You may not access another user's content as an enabled listener. - final int userId = UserHandle.getUserId(resolvedUserId); - if (resolvedUserId != userId) { + private boolean hasEnabledNotificationListener(int callingUserId, + String controllerPackageName, int controllerUid) throws RemoteException { + int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier(); + if (callingUserId != controllerUserId) { + // Enabled notification listener only works within the same user. return false; } // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener( // String pkgName) to notification team for optimization final List<ComponentName> enabledNotificationListeners = - mNotificationManager.getEnabledNotificationListeners(userId); + mNotificationManager.getEnabledNotificationListeners(controllerUserId); if (enabledNotificationListeners != null) { for (int i = 0; i < enabledNotificationListeners.size(); i++) { - if (TextUtils.equals(packageName, + if (TextUtils.equals(controllerPackageName, enabledNotificationListeners.get(i).getPackageName())) { return true; } } } if (DEBUG) { - Log.d(TAG, packageName + " (uid=" + resolvedUserId + ") doesn't have an enabled " - + "notification listener"); + Log.d(TAG, controllerPackageName + " (uid=" + controllerUid + + ") doesn't have an enabled notification listener"); } return false; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f8d54adbeb5b..e42dd359dad1 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1939,6 +1939,13 @@ public class NotificationManagerService extends SystemService { } @Override + void onConsolidatedPolicyChanged() { + Binder.withCleanCallingIdentity(() -> { + mRankingHandler.requestSort(); + }); + } + + @Override void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) { Binder.withCleanCallingIdentity(() -> { Intent intent = new Intent(ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED); diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index c3c2e5e65103..069a00f03a1d 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -547,9 +547,9 @@ public class AppsFilter { final boolean newIsForceQueryable = mForceQueryable.contains(newPkgSetting.appId) /* shared user that is already force queryable */ - || newPkg.isForceQueryable() - || newPkgSetting.forceQueryableOverride + || newPkgSetting.forceQueryableOverride /* adb override */ || (newPkgSetting.isSystem() && (mSystemAppsQueryable + || newPkg.isForceQueryable() || ArrayUtils.contains(mForceQueryableByDevicePackageNames, newPkg.getPackageName()))); if (newIsForceQueryable diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 994cec2b1e59..ea53132ae409 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -267,6 +267,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** Uid of the creator of this session. */ private final int mOriginalInstallerUid; + /** Package name of the app that created the installation session. */ + private final String mOriginalInstallerPackageName; + /** Uid of the owner of the installer session */ @GuardedBy("mLock") private int mInstallerUid; @@ -556,6 +559,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mOriginalInstallerUid = installerUid; mInstallerUid = installerUid; mInstallSource = Objects.requireNonNull(installSource); + mOriginalInstallerPackageName = mInstallSource.installerPackageName; this.params = params; this.createdMillis = createdMillis; this.updatedMillis = createdMillis; @@ -1666,11 +1670,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new IllegalArgumentException("Package is not valid", e); } - if (!mPackageName.equals(mInstallSource.installerPackageName)) { - throw new SecurityException("Can only transfer sessions that update the original " - + "installer"); - } - mInstallerUid = newOwnerAppInfo.uid; mInstallSource = InstallSource.create(packageName, null, packageName); } @@ -2157,6 +2156,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + if (mInstallerUid != mOriginalInstallerUid) { + // Session has been transferred, check package name. + if (TextUtils.isEmpty(mPackageName) || !mPackageName.equals( + mOriginalInstallerPackageName)) { + throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, + "Can only transfer sessions that update the original installer"); + } + } + if (params.mode == SessionParams.MODE_FULL_INSTALL) { // Full installs must include a base package if (!stagedSplits.contains(null)) { @@ -3182,6 +3190,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { pw.printPair("userId", userId); pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid); + pw.printPair("mOriginalInstallerPackageName", mOriginalInstallerPackageName); pw.printPair("installerPackageName", mInstallSource.installerPackageName); pw.printPair("installInitiatingPackageName", mInstallSource.initiatingPackageName); pw.printPair("installOriginatingPackageName", mInstallSource.originatingPackageName); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index eefad1c8eed2..5c3644e5b262 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2376,9 +2376,12 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUserId = UserHandle.getUserId(callingUid); for (String packageName : packages) { - PackageSetting setting = mSettings.mPackages.get(packageName); - if (setting != null - && !shouldFilterApplicationLocked(setting, callingUid, callingUserId)) { + final boolean filterApp; + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(packageName); + filterApp = shouldFilterApplicationLocked(ps, callingUid, callingUserId); + } + if (!filterApp) { notifyInstallObserver(packageName); } } @@ -4198,13 +4201,9 @@ public class PackageManagerService extends IPackageManager.Stub Iterator<ResolveInfo> iter = matches.iterator(); while (iter.hasNext()) { final ResolveInfo rInfo = iter.next(); - final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName); - if (ps != null) { - final PermissionsState permissionsState = ps.getPermissionsState(); - if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0) - || Build.IS_ENG) { - continue; - } + if (checkPermission(Manifest.permission.INSTALL_PACKAGES, + rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || Build.IS_ENG) { + continue; } iter.remove(); } @@ -4380,8 +4379,24 @@ public class PackageManagerService extends IPackageManager.Stub final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId); // Compute granted permissions only if package has requested permissions - final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions()) + Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions()) ? Collections.emptySet() : permissionsState.getPermissions(userId); + if (state.instantApp) { + permissions = new ArraySet<>(permissions); + permissions.removeIf(permissionName -> { + BasePermission permission = mPermissionManager.getPermissionTEMP( + permissionName); + if (permission == null) { + return true; + } + if (!permission.isInstant()) { + EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId, + ps.appId), permissionName); + return true; + } + return false; + }); + } PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags, ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps); @@ -8579,10 +8594,9 @@ public class PackageManagerService extends IPackageManager.Stub private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; - final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i<permissions.length; i++) { final String permission = permissions[i]; - if (permissionsState.hasPermission(permission, userId)) { + if (checkPermission(permission, ps.name, userId) == PERMISSION_GRANTED) { tmp[i] = true; numMatch++; } else { @@ -8914,10 +8928,10 @@ public class PackageManagerService extends IPackageManager.Stub if (providerInfo == null) { return null; } - if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { - return null; - } synchronized (mLock) { + if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { + return null; + } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); @@ -9004,9 +9018,11 @@ public class PackageManagerService extends IPackageManager.Stub String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); - final PackageSetting ps = mSettings.mPackages.get(targetPackage); - if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { - return ParceledListSlice.emptyList(); + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(targetPackage); + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { + return ParceledListSlice.emptyList(); + } } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags, callingUserId)); @@ -14501,7 +14517,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting ps; int appId = -1; long ceDataInode = -1; - synchronized (mSettings) { + synchronized (mLock) { ps = mSettings.getPackageLPr(packageName); if (ps != null) { appId = ps.appId; @@ -19185,6 +19201,14 @@ public class PackageManagerService extends IPackageManager.Stub final int flags = action.flags; final boolean systemApp = isSystemApp(ps); + // We need to get the permission state before package state is (potentially) destroyed. + final SparseBooleanArray hadSuspendAppsPermission = new SparseBooleanArray(); + // allUserHandles could be null, so call mUserManager.getUserIds() directly which is cached anyway. + for (int userId : mUserManager.getUserIds()) { + hadSuspendAppsPermission.put(userId, checkPermission(Manifest.permission.SUSPEND_APPS, + packageName, userId) == PERMISSION_GRANTED); + } + final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) @@ -19251,8 +19275,7 @@ public class PackageManagerService extends IPackageManager.Stub affectedUserIds = resolveUserIds(userId); } for (final int affectedUserId : affectedUserIds) { - if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, - affectedUserId)) { + if (hadSuspendAppsPermission.get(affectedUserId)) { unsuspendForSuspendingPackage(packageName, affectedUserId); removeAllDistractingPackageRestrictions(affectedUserId); } @@ -19356,9 +19379,11 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "clear application data"); - final PackageSetting ps = mSettings.getPackageLPr(packageName); - final boolean filterApp = - (ps != null && shouldFilterApplicationLocked(ps, callingUid, userId)); + final boolean filterApp; + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(packageName); + filterApp = shouldFilterApplicationLocked(ps, callingUid, userId); + } if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) { throw new SecurityException("Cannot clear data for a protected package: " + packageName); @@ -19638,11 +19663,13 @@ public class PackageManagerService extends IPackageManager.Stub if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (getUidTargetSdkVersionLockedLPr(callingUid) - < Build.VERSION_CODES.FROYO) { - Slog.w(TAG, "Ignoring addPreferredActivity() from uid " - + callingUid); - return; + synchronized (mLock) { + if (getUidTargetSdkVersionLockedLPr(callingUid) + < Build.VERSION_CODES.FROYO) { + Slog.w(TAG, "Ignoring addPreferredActivity() from uid " + + callingUid); + return; + } } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); @@ -19814,8 +19841,9 @@ public class PackageManagerService extends IPackageManager.Stub /** This method takes a specific user id as well as UserHandle.USER_ALL. */ private void clearPackagePreferredActivities(String packageName, int userId) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); - - clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId); + synchronized (mLock) { + clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId); + } if (changedUsers.size() > 0) { updateDefaultHomeNotLocked(changedUsers); postPreferredActivityChangedBroadcast(userId); @@ -19937,7 +19965,9 @@ public class PackageManagerService extends IPackageManager.Stub // writer try { final SparseBooleanArray changedUsers = new SparseBooleanArray(); - clearPackagePreferredActivitiesLPw(null, changedUsers, userId); + synchronized (mLock) { + clearPackagePreferredActivitiesLPw(null, changedUsers, userId); + } if (changedUsers.size() > 0) { postPreferredActivityChangedBroadcast(userId); } @@ -20942,15 +20972,19 @@ public class PackageManagerService extends IPackageManager.Stub // Limit who can change which apps if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) { // Don't allow apps that don't have permission to modify other apps - if (!allowedByPermission - || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { + final boolean filterApp; + synchronized (mLock) { + filterApp = (!allowedByPermission + || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)); + } + if (filterApp) { throw new SecurityException( "Attempt to change component state; " - + "pid=" + Binder.getCallingPid() - + ", uid=" + callingUid - + (className == null + + "pid=" + Binder.getCallingPid() + + ", uid=" + callingUid + + (className == null ? ", package=" + packageName - : ", component=" + packageName + "/" + className)); + : ", component=" + packageName + "/" + className)); } // Don't allow changing protected packages. if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { @@ -21017,8 +21051,8 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting.setEnabled(newState, userId, callingPackage); if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER || newState == COMPONENT_ENABLED_STATE_DISABLED) - && pkgSetting.getPermissionsState().hasPermission( - Manifest.permission.SUSPEND_APPS, userId)) { + && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId) + == PERMISSION_GRANTED) { // This app should not generally be allowed to get disabled by the UI, but if it // ever does, we don't want to end up with some of the user's apps permanently // suspended. diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 27924a68ff48..01a3d14e05df 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -114,7 +114,6 @@ import com.android.server.SystemService; import com.android.server.am.UserState; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.utils.TimingsTraceAndSlog; -import com.android.server.wm.ActivityTaskManagerInternal; import libcore.io.IoUtils; @@ -134,6 +133,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -406,6 +406,10 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private int[] mUserIds; + + @GuardedBy("mUsersLock") + private int[] mUserIdsIncludingPreCreated; + @GuardedBy("mPackagesLock") private int mNextSerialNumber; private int mUserVersion = 0; @@ -2480,16 +2484,35 @@ public class UserManagerService extends IUserManager.Stub { } /** - * Returns an array of user ids. This array is cached here for quick access, so do not modify or - * cache it elsewhere. + * Returns an array of user ids. + * + * <p>This array is cached here for quick access, so do not modify or cache it elsewhere. + * * @return the array of user ids. */ - public int[] getUserIds() { + public @NonNull int[] getUserIds() { synchronized (mUsersLock) { return mUserIds; } } + /** + * Returns an array of user ids, including pre-created users. + * + * <p>This method should only used for the specific cases that need to handle pre-created users; + * most callers should call {@link #getUserIds()} instead. + * + * <p>This array is cached here for quick access, so do not modify or + * cache it elsewhere. + * + * @return the array of user ids. + */ + public @NonNull int[] getUserIdsIncludingPreCreated() { + synchronized (mUsersLock) { + return mUserIdsIncludingPreCreated; + } + } + @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) private void readUserListLP() { if (!mUserListFile.exists()) { @@ -4327,23 +4350,43 @@ public class UserManagerService extends IUserManager.Stub { */ private void updateUserIds() { int num = 0; + int numIncludingPreCreated = 0; synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { - UserInfo userInfo = mUsers.valueAt(i).info; - if (!userInfo.partial && !userInfo.preCreated) { - num++; + final UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial) { + numIncludingPreCreated++; + if (!userInfo.preCreated) { + num++; + } } } + if (DBG) { + Slog.d(LOG_TAG, "updateUserIds(): numberUsers= " + num + + " includingPreCreated=" + numIncludingPreCreated); + } final int[] newUsers = new int[num]; + final int[] newUsersIncludingPreCreated = new int[numIncludingPreCreated]; + int n = 0; + int nIncludingPreCreated = 0; for (int i = 0; i < userSize; i++) { - UserInfo userInfo = mUsers.valueAt(i).info; - if (!userInfo.partial && !userInfo.preCreated) { - newUsers[n++] = mUsers.keyAt(i); + final UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial) { + final int userId = mUsers.keyAt(i); + newUsersIncludingPreCreated[nIncludingPreCreated++] = userId; + if (!userInfo.preCreated) { + newUsers[n++] = userId; + } } } mUserIds = newUsers; + mUserIdsIncludingPreCreated = newUsersIncludingPreCreated; + if (DBG) { + Slog.d(LOG_TAG, "updateUserIds(): userIds= " + Arrays.toString(mUserIds) + + " includingPreCreated=" + Arrays.toString(mUserIdsIncludingPreCreated)); + } } } @@ -4791,6 +4834,13 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mUserStates) { pw.println(" Started users state: " + mUserStates); } + synchronized (mUsersLock) { + pw.print(" Cached user IDs: "); + pw.println(Arrays.toString(mUserIds)); + pw.print(" Cached user IDs (including pre-created): "); + pw.println(Arrays.toString(mUserIdsIncludingPreCreated)); + } + } // synchronized (mPackagesLock) // Dump some capabilities diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index a4e8b1c01620..2533aa7c4302 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -83,7 +83,6 @@ import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.UserInfo; import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.permission.SplitPermissionInfoParcelable; @@ -121,7 +120,6 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.TimingsTraceLog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -3078,17 +3076,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @return user ids for created users and pre-created users */ private int[] getAllUserIds() { - final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER); - t.traceBegin("getAllUserIds"); - List<UserInfo> users = UserManagerService.getInstance().getUsers( - /*excludePartial=*/ true, /*excludeDying=*/ true, /*excludePreCreated=*/ false); - int size = users.size(); - final int[] userIds = new int[size]; - for (int i = 0; i < size; i++) { - userIds[i] = users.get(i).id; - } - t.traceEnd(); - return userIds; + return UserManagerService.getInstance().getUserIdsIncludingPreCreated(); } /** diff --git a/services/core/java/com/android/server/telecom/InternalServiceRepository.java b/services/core/java/com/android/server/telecom/InternalServiceRepository.java new file mode 100644 index 000000000000..76ea5c788bd7 --- /dev/null +++ b/services/core/java/com/android/server/telecom/InternalServiceRepository.java @@ -0,0 +1,63 @@ +/* + * 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 com.android.server.telecom; + +import android.content.Context; +import android.os.Binder; +import android.os.Process; + +import com.android.internal.telecom.IDeviceIdleControllerAdapter; +import com.android.internal.telecom.IInternalServiceRetriever; +import com.android.server.DeviceIdleInternal; + +/** + * The Telecom APK can not access services stored in LocalService directly and since it is in the + * SYSTEM process, it also can not use the *Manager interfaces + * (see {@link Context#enforceCallingPermission(String, String)}). Instead, we must wrap these local + * services in binder interfaces to allow Telecom access. + */ +public class InternalServiceRepository extends IInternalServiceRetriever.Stub { + + private final IDeviceIdleControllerAdapter.Stub mDeviceIdleControllerAdapter = + new IDeviceIdleControllerAdapter.Stub() { + @Override + public void exemptAppTemporarilyForEvent(String packageName, long duration, int userHandle, + String reason) { + mDeviceIdleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName, + duration, userHandle, true /*sync*/, reason); + } + }; + + private final DeviceIdleInternal mDeviceIdleController; + + public InternalServiceRepository(DeviceIdleInternal deviceIdleController) { + mDeviceIdleController = deviceIdleController; + } + + @Override + public IDeviceIdleControllerAdapter getDeviceIdleController() { + ensureSystemProcess(); + return mDeviceIdleControllerAdapter; + } + + private void ensureSystemProcess() { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + // Correctness check - this should never happen. + throw new SecurityException("SYSTEM ONLY API."); + } + } +} diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java index a853529f49e4..52ad893a9ace 100644 --- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java +++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java @@ -35,7 +35,10 @@ import android.util.IntArray; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.telecom.ITelecomLoader; +import com.android.internal.telecom.ITelecomService; import com.android.internal.telephony.SmsApplication; +import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerService; @@ -53,16 +56,13 @@ public class TelecomLoaderService extends SystemService { @Override public void onServiceConnected(ComponentName name, IBinder service) { // Normally, we would listen for death here, but since telecom runs in the same process - // as this loader (process="system") thats redundant here. + // as this loader (process="system") that's redundant here. try { - service.linkToDeath(new IBinder.DeathRecipient() { - @Override - public void binderDied() { - connectToTelecom(); - } - }, 0); + ITelecomLoader telecomLoader = ITelecomLoader.Stub.asInterface(service); + ITelecomService telecomService = telecomLoader.createTelecomService(mServiceRepo); + SmsApplication.getDefaultMmsApplication(mContext, false); - ServiceManager.addService(Context.TELECOM_SERVICE, service); + ServiceManager.addService(Context.TELECOM_SERVICE, telecomService.asBinder()); synchronized (mLock) { final PermissionManagerServiceInternal permissionManager = @@ -114,6 +114,8 @@ public class TelecomLoaderService extends SystemService { @GuardedBy("mLock") private TelecomServiceConnection mServiceConnection; + private InternalServiceRepository mServiceRepo; + public TelecomLoaderService(Context context) { super(context); mContext = context; @@ -129,6 +131,8 @@ public class TelecomLoaderService extends SystemService { if (phase == PHASE_ACTIVITY_MANAGER_READY) { registerDefaultAppNotifier(); registerCarrierConfigChangedReceiver(); + // core services will have already been loaded. + setupServiceRepository(); connectToTelecom(); } } @@ -154,6 +158,11 @@ public class TelecomLoaderService extends SystemService { } } + private void setupServiceRepository() { + DeviceIdleInternal deviceIdleInternal = getLocalService(DeviceIdleInternal.class); + mServiceRepo = new InternalServiceRepository(deviceIdleInternal); + } + private void registerDefaultAppProviders() { final PermissionManagerServiceInternal permissionManager = diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 2394bafc09de..26103d5d6c8c 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -123,6 +123,8 @@ public class TrustManagerService extends SystemService { private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser"; private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000; + private static final String PRIV_NAMESPACE = "http://schemas.android.com/apk/prv/res/android"; + private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>(); private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>(); private final Receiver mReceiver = new Receiver(); @@ -808,8 +810,8 @@ public class TrustManagerService extends SystemService { TypedArray sa = res .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent); cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity); - canUnlockProfile = sa.getBoolean( - com.android.internal.R.styleable.TrustAgent_unlockProfile, false); + canUnlockProfile = attrs.getAttributeBooleanValue( + PRIV_NAMESPACE, "unlockProfile", false); sa.recycle(); } catch (PackageManager.NameNotFoundException e) { caughtException = e; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fcbc7564cabb..abfb9c99de6b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -43,14 +43,14 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; +import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; -import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; -import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; @@ -1573,7 +1573,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // the heavy operations. This also benefits that the states of multiple activities // are handled together. r.linkFixedRotationTransform(prevRotatedLaunchingApp); - setFixedRotationLaunchingAppUnchecked(r, rotation); + if (r != mFixedRotationTransitionListener.mAnimatingRecents) { + // Only update the record for normal activity so the display orientation can be + // updated when the transition is done if it becomes the top. And the case of + // recents can be handled when the recents animation is finished. + setFixedRotationLaunchingAppUnchecked(r, rotation); + } return; } @@ -4978,7 +4983,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } // Apply restriction if necessary. - if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) { + if (needsGestureExclusionRestrictions(w, false /* ignoreRequest */)) { // Processes the region along the left edge. remainingLeftRight[0] = addToGlobalAndConsumeLimit(local, outExclusion, leftEdge, @@ -4995,7 +5000,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo outExclusion.op(middle, Op.UNION); middle.recycle(); } else { - boolean loggable = needsGestureExclusionRestrictions(w, 0 /* lastSysUiVis */); + boolean loggable = needsGestureExclusionRestrictions(w, true /* ignoreRequest */); if (loggable) { addToGlobalAndConsumeLimit(local, outExclusion, leftEdge, Integer.MAX_VALUE, w, EXCLUSION_LEFT); @@ -5017,17 +5022,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } /** - * @return Whether gesture exclusion area should be restricted from the window depending on the - * current SystemUI visibility flags. + * Returns whether gesture exclusion area should be restricted from the window depending on the + * window/activity types and the requested navigation bar visibility and the behavior. + * + * @param win The target window. + * @param ignoreRequest If this is {@code true}, only the window/activity types are considered. + * @return {@code true} if the gesture exclusion restrictions are needed. */ - private static boolean needsGestureExclusionRestrictions(WindowState win, int sysUiVisibility) { + private static boolean needsGestureExclusionRestrictions(WindowState win, + boolean ignoreRequest) { final int type = win.mAttrs.type; - final int stickyHideNavFlags = - SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY; final boolean stickyHideNav = - (sysUiVisibility & stickyHideNavFlags) == stickyHideNavFlags; - return !stickyHideNav && type != TYPE_INPUT_METHOD && type != TYPE_NOTIFICATION_SHADE - && win.getActivityType() != ACTIVITY_TYPE_HOME; + !win.getRequestedInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) + && win.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + return (!stickyHideNav || ignoreRequest) && type != TYPE_INPUT_METHOD + && type != TYPE_NOTIFICATION_SHADE && win.getActivityType() != ACTIVITY_TYPE_HOME; } /** @@ -5043,7 +5052,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo && type != TYPE_APPLICATION_STARTING && type != TYPE_NAVIGATION_BAR && (attrs.flags & FLAG_NOT_TOUCHABLE) == 0 - && needsGestureExclusionRestrictions(win, 0 /* sysUiVisibility */) + && needsGestureExclusionRestrictions(win, true /* ignoreRequest */) && win.getDisplayContent().mDisplayPolicy.hasSideGestures(); } @@ -5642,6 +5651,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ private ActivityRecord mAnimatingRecents; + /** Whether {@link #mAnimatingRecents} is going to be the top activity. */ + private boolean mRecentsWillBeTop; + /** * If the recents activity has a fixed orientation which is different from the current top * activity, it will be rotated before being shown so we avoid a screen rotation animation @@ -5667,10 +5679,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * If {@link #mAnimatingRecents} still has fixed rotation, it should be moved to top so we * don't clear {@link #mFixedRotationLaunchingApp} that will be handled by transition. */ - void onFinishRecentsAnimation(boolean moveRecentsToBack) { + void onFinishRecentsAnimation() { final ActivityRecord animatingRecents = mAnimatingRecents; + final boolean recentsWillBeTop = mRecentsWillBeTop; mAnimatingRecents = null; - if (!moveRecentsToBack) { + mRecentsWillBeTop = false; + if (recentsWillBeTop) { // The recents activity will be the top, such as staying at recents list or // returning to home (if home and recents are the same activity). return; @@ -5693,6 +5707,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + void notifyRecentsWillBeTop() { + mRecentsWillBeTop = true; + } + /** * Return {@code true} if there is an ongoing animation to the "Recents" activity and this * activity as a fixed orientation so shouldn't be rotated. @@ -5713,6 +5731,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (r == null || r == mAnimatingRecents) { return; } + if (mAnimatingRecents != null && mRecentsWillBeTop) { + // The activity is not the recents and it should be moved to back later, so it is + // better to keep its current appearance for the next transition. Otherwise the + // display orientation may be updated too early and the layout procedures at the + // end of finishing recents animation is skipped. That causes flickering because + // the surface of closing app hasn't updated to invisible. + return; + } if (mFixedRotationLaunchingApp == null) { // In most cases this is a no-op if the activity doesn't have fixed rotation. // Otherwise it could be from finishing recents animation while the display has diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 5c5ec358d004..dbc579940e14 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -29,6 +29,7 @@ import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -71,6 +72,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; @@ -1538,7 +1540,7 @@ public class DisplayPolicy { if (mInputConsumer == null) { return; } - showNavigationBar(); + showSystemBars(); // Any user activity always causes us to show the // navigation controls, if they had been hidden. // We also clear the low profile and only content @@ -1573,13 +1575,13 @@ public class DisplayPolicy { } } - private void showNavigationBar() { + private void showSystemBars() { final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() .peekSourceProvider(ITYPE_NAVIGATION_BAR); final InsetsControlTarget target = provider != null ? provider.getControlTarget() : null; if (target != null) { - target.showInsets(Type.navigationBars(), false /* fromIme */); + target.showInsets(Type.systemBars(), false /* fromIme */); } } } @@ -2191,6 +2193,13 @@ public class DisplayPolicy { df.set(left, top, dfu.right - right, dfu.bottom - bottom); if (attached == null) { pf.set(df); + if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { + final InsetsSource source = mDisplayContent.getInsetsPolicy() + .getInsetsForDispatch(win).peekSource(ITYPE_IME); + if (source != null) { + pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */)); + } + } vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING ? displayFrames.mCurrent : displayFrames.mDock); } else { diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 77bd4a47a884..c2c04d9f70ac 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -280,6 +280,7 @@ class InsetsStateController { } if (changed) { notifyInsetsChanged(); + mDisplayContent.updateSystemGestureExclusion(); mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw(); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index a2b295a609cd..65db23c714a9 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -702,6 +702,11 @@ public class RecentsAnimationController implements DeathRecipient { "cleanupAnimation(): Notify animation finished mPendingAnimations=%d " + "reorderMode=%d", mPendingAnimations.size(), reorderMode); + if (reorderMode != REORDER_MOVE_TO_ORIGINAL_POSITION) { + // Notify the state at the beginning because the removeAnimation may notify the + // transition is finished. This is a signal that there will be a next transition. + mDisplayContent.mFixedRotationTransitionListener.notifyRecentsWillBeTop(); + } for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { @@ -742,8 +747,7 @@ public class RecentsAnimationController implements DeathRecipient { mTargetActivityRecord.token); } } - mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation( - reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION /* moveRecentsToBack */); + mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(); // Notify that the animation has ended if (mStatusBar != null) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 76927e277412..954b7a026bf5 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3627,6 +3627,9 @@ class Task extends WindowContainer<WindowContainer> { info.topActivityInfo = mReuseActivitiesReport.top != null ? mReuseActivitiesReport.top.info : null; + info.requestedOrientation = mReuseActivitiesReport.base != null + ? mReuseActivitiesReport.base.getRequestedOrientation() + : SCREEN_ORIENTATION_UNSET; } /** diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index f70bf18cdea5..e153befde8ad 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -458,7 +458,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { || mTmpTaskInfo.topActivityType != lastInfo.topActivityType || mTmpTaskInfo.isResizeable != lastInfo.isResizeable || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams - || !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription); + || !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription) + || mTmpTaskInfo.requestedOrientation != lastInfo.requestedOrientation; if (!changed) { int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration); final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 86aacf308068..1716dcd5ee16 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -531,7 +531,7 @@ class WindowToken extends WindowContainer<WindowState> { void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, Configuration config) { if (mFixedRotationTransformState != null) { - return; + cleanUpFixedRotationTransformState(true /* replacing */); } mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, new Configuration(config), mDisplayContent.getRotation()); @@ -548,13 +548,13 @@ class WindowToken extends WindowContainer<WindowState> { * one. This takes the same effect as {@link #applyFixedRotationTransform}. */ void linkFixedRotationTransform(WindowToken other) { - if (mFixedRotationTransformState != null) { - return; - } final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState; - if (fixedRotationState == null) { + if (fixedRotationState == null || mFixedRotationTransformState == fixedRotationState) { return; } + if (mFixedRotationTransformState != null) { + cleanUpFixedRotationTransformState(true /* replacing */); + } mFixedRotationTransformState = fixedRotationState; fixedRotationState.mAssociatedTokens.add(this); onConfigurationChanged(getParent().getConfiguration()); @@ -609,11 +609,17 @@ class WindowToken extends WindowContainer<WindowState> { // The state is cleared at the end, because it is used to indicate that other windows can // use seamless rotation when applying rotation to display. for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { - state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(); + state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState( + false /* replacing */); } } - private void cleanUpFixedRotationTransformState() { + private void cleanUpFixedRotationTransformState(boolean replacing) { + if (replacing && mFixedRotationTransformState.mAssociatedTokens.size() > 1) { + // The state is not only used by self. Make sure to leave the influence by others. + mFixedRotationTransformState.mAssociatedTokens.remove(this); + mFixedRotationTransformState.mRotatedContainers.remove(this); + } mFixedRotationTransformState = null; notifyFixedRotationTransform(false /* enabled */); } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 925ad0f57f19..460842e56764 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -106,6 +106,7 @@ cc_defaults { "libkeystore_binder", "libmtp", "libnativehelper", + "libprocessgroup", "libutils", "libui", "libinput", diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 6a6da0e2b395..95d4ba7c199a 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -29,7 +29,9 @@ #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> +#include <binder/IPCThreadState.h> #include <jni.h> +#include <processgroup/processgroup.h> using android::base::StringPrintf; using android::base::WriteStringToFile; @@ -74,9 +76,35 @@ static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, job } } +static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal( + JNIEnv *env, jobject clazz, jboolean enable) { + bool success = true; + + if (enable) { + success = SetTaskProfiles(0, {"FreezerEnabled"}, true); + } else { + success = SetTaskProfiles(0, {"FreezerDisabled"}, true); + } + + if (!success) { + jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); + } +} + +static void com_android_server_am_CachedAppOptimizer_freezeBinder( + JNIEnv *env, jobject clazz, jint pid, jboolean freeze) { + + if (IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */) != 0) { + jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder"); + } +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, + {"enableFreezerInternal", "(Z)V", + (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal}, + {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder} }; int register_android_server_am_CachedAppOptimizer(JNIEnv* env) diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index ca8e50aa19c9..7bd0201b997a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -1053,6 +1053,49 @@ public class AlarmManagerServiceTest { } } + @Test + public void nonWakeupAlarmsDeferred() throws Exception { + final int numAlarms = 10; + final PendingIntent[] pis = new PendingIntent[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + pis[i] = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]); + } + doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong()); + // Advance time past all expirations. + mNowElapsedTest += numAlarms + 5; + mTestTimer.expire(); + assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size()); + + // These alarms should be sent on interactive state change to true + mService.interactiveStateChangedLocked(false); + mService.interactiveStateChangedLocked(true); + + for (int i = 0; i < numAlarms; i++) { + verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(), + any(Handler.class), isNull(), any()); + } + } + + @Test + public void alarmCountOnPendingNonWakeupAlarmsRemoved() throws Exception { + final int numAlarms = 10; + final PendingIntent[] pis = new PendingIntent[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + pis[i] = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]); + } + doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong()); + // Advance time past all expirations. + mNowElapsedTest += numAlarms + 5; + mTestTimer.expire(); + assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size()); + for (int i = 0; i < numAlarms; i++) { + mService.removeLocked(pis[i], null); + assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0)); + } + } + @After public void tearDown() { if (mMockingSession != null) { diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 26230949cda6..37aedac8f28e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -365,14 +365,15 @@ public class AppsFilterTest { } @Test - public void testForceQueryable_DoesntFilter() throws Exception { + public void testForceQueryable_SystemDoesntFilter() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID); + pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID, + setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_APPID); @@ -380,6 +381,23 @@ public class AppsFilterTest { SYSTEM_USER)); } + + @Test + public void testForceQueryable_NonSystemFilters() throws Exception { + final AppsFilter appsFilter = + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, + pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package"), DUMMY_CALLING_APPID); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + @Test public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception { final AppsFilter appsFilter = 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 cbccfb3725e9..dc838f1bb288 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1282,7 +1282,7 @@ public class DisplayContentTests extends WindowTestsBase { assertFalse(displayRotation.updateRotationUnchecked(false)); // Rotation can be updated if the recents animation is finished. - mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(false); + mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(); assertTrue(displayRotation.updateRotationUnchecked(false)); // Rotation can be updated if the recents animation is animating but it is not on top, e.g. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 2f3afbcb6d72..2d3b74b75826 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -349,6 +350,24 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_insetParentFrameByIme() { + final InsetsState state = + mDisplayContent.getInsetsStateController().getRawInsetsState(); + state.getSource(InsetsState.ITYPE_IME).setVisible(true); + state.getSource(InsetsState.ITYPE_IME).setFrame( + 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); + mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + mWindow.mBehindIme = true; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, IME_HEIGHT); + } + + @Test public void layoutWindowLw_fitDisplayCutout() { addDisplayCutout(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 7ba3fd815b2d..c87014a8a27f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -62,7 +62,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static final int STATUS_BAR_HEIGHT = 10; static final int NAV_BAR_HEIGHT = 15; static final int DISPLAY_CUTOUT_HEIGHT = 8; - static final int INPUT_METHOD_WINDOW_TOP = 585; + static final int IME_HEIGHT = 415; DisplayPolicy mDisplayPolicy; 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 8e85e7b96d1f..f2771175b523 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -363,12 +363,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertFalse(homeActivity.hasFixedRotationTransform()); } - @Test - public void testClearFixedRotationLaunchingAppAfterCleanupAnimation() { + private ActivityRecord prepareFixedRotationLaunchingAppWithRecentsAnim() { final ActivityRecord homeActivity = createHomeActivity(); homeActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); final ActivityRecord activity = createActivityRecord(mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + // Add a window so it can be animated by the recents. + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); + activity.addWindow(win); // Assume an activity is launching to different rotation. mDefaultDisplay.setFixedRotationLaunchingApp(activity, (mDefaultDisplay.getRotation() + 1) % 4); @@ -379,6 +381,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { // Before the transition is done, the recents animation is triggered. initializeRecentsAnimationController(mController, homeActivity); assertFalse(homeActivity.hasFixedRotationTransform()); + assertTrue(mController.isAnimatingTask(activity.getTask())); + + return activity; + } + + @Test + public void testClearFixedRotationLaunchingAppAfterCleanupAnimation() { + final ActivityRecord activity = prepareFixedRotationLaunchingAppWithRecentsAnim(); // Simulate giving up the swipe up gesture to keep the original activity as top. mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION); @@ -388,6 +398,21 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } @Test + public void testKeepFixedRotationWhenMovingRecentsToTop() { + final ActivityRecord activity = prepareFixedRotationLaunchingAppWithRecentsAnim(); + // Assume a transition animation has started running before recents animation. Then the + // activity will receive onAnimationFinished that notifies app transition finished when + // removing the recents animation of task. + activity.getTask().getAnimationSources().add(activity); + + // Simulate swiping to home/recents before the transition is done. + mController.cleanupAnimation(REORDER_MOVE_TO_TOP); + // The rotation transform should be preserved. In real case, it will be cleared by the next + // move-to-top transition. + assertTrue(activity.hasFixedRotationTransform()); + } + + @Test public void testWallpaperHasFixedRotationApplied() { mWm.setRecentsAnimationController(mController); 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 23a097eb0c7c..0896db4b5532 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -139,6 +140,8 @@ public class WindowTokenTests extends WindowTestsBase { public void testFinishFixedRotationTransform() { final WindowToken appToken = mAppWindow.mToken; final WindowToken wallpaperToken = mWallpaperWindow.mToken; + final WindowToken testToken = + WindowTestUtils.createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); final Configuration config = new Configuration(mDisplayContent.getConfiguration()); final int originalRotation = config.windowConfiguration.getRotation(); final int targetRotation = (originalRotation + 1) % 4; @@ -151,11 +154,20 @@ public class WindowTokenTests extends WindowTestsBase { assertEquals(targetRotation, appToken.getWindowConfiguration().getRotation()); assertEquals(targetRotation, wallpaperToken.getWindowConfiguration().getRotation()); - // The display doesn't rotate, the transformation will be canceled. - mAppWindow.mToken.finishFixedRotationTransform(); + testToken.applyFixedRotationTransform(mDisplayInfo, mDisplayContent.mDisplayFrames, config); + // The wallpaperToken was linked to appToken, this should make it link to testToken. + wallpaperToken.linkFixedRotationTransform(testToken); - // The window tokens should restore to the original rotation. + // Assume the display doesn't rotate, the transformation will be canceled. + appToken.finishFixedRotationTransform(); + + // The appToken should restore to the original rotation. assertEquals(originalRotation, appToken.getWindowConfiguration().getRotation()); + // The wallpaperToken is linked to testToken, it should keep the target rotation. + assertNotEquals(originalRotation, wallpaperToken.getWindowConfiguration().getRotation()); + + testToken.finishFixedRotationTransform(); + // The rotation of wallpaperToken should be restored because its linked state is finished. assertEquals(originalRotation, wallpaperToken.getWindowConfiguration().getRotation()); } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 6c13cd799bc2..fe0f7b80997e 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -697,7 +697,7 @@ public class SoundTriggerService extends SystemService { synchronized (mLock) { ModuleProperties properties = mSoundTriggerHelper.getModuleProperties(); sEventLogger.log(new SoundTriggerLogger.StringEvent( - "getModuleProperties(): " + properties.toString())); + "getModuleProperties(): " + properties)); return properties; } } @@ -1284,32 +1284,25 @@ public class SoundTriggerService extends SystemService { * @return The initialized AudioRecord */ private @NonNull AudioRecord createAudioRecordForEvent( - @NonNull SoundTrigger.GenericRecognitionEvent event) { + @NonNull SoundTrigger.GenericRecognitionEvent event) + throws IllegalArgumentException, UnsupportedOperationException { AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder(); attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD); AudioAttributes attributes = attributesBuilder.build(); - // Use same AudioFormat processing as in RecognitionEvent.fromParcel AudioFormat originalFormat = event.getCaptureFormat(); - AudioFormat captureFormat = (new AudioFormat.Builder()) - .setChannelMask(originalFormat.getChannelMask()) - .setEncoding(originalFormat.getEncoding()) - .setSampleRate(originalFormat.getSampleRate()) - .build(); - - int bufferSize = AudioRecord.getMinBufferSize( - captureFormat.getSampleRate() == AudioFormat.SAMPLE_RATE_UNSPECIFIED - ? AudioFormat.SAMPLE_RATE_HZ_MAX - : captureFormat.getSampleRate(), - captureFormat.getChannelCount() == 2 - ? AudioFormat.CHANNEL_IN_STEREO - : AudioFormat.CHANNEL_IN_MONO, - captureFormat.getEncoding()); sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent")); - return new AudioRecord(attributes, captureFormat, bufferSize, - event.getCaptureSession()); + return (new AudioRecord.Builder()) + .setAudioAttributes(attributes) + .setAudioFormat((new AudioFormat.Builder()) + .setChannelMask(originalFormat.getChannelMask()) + .setEncoding(originalFormat.getEncoding()) + .setSampleRate(originalFormat.getSampleRate()) + .build()) + .setSessionId(event.getCaptureSession()) + .build(); } @Override @@ -1335,12 +1328,16 @@ public class SoundTriggerService extends SystemService { // execute if throttled: () -> { if (event.isCaptureAvailable()) { - AudioRecord capturedData = createAudioRecordForEvent(event); - - // Currently we need to start and release the audio record to reset - // the DSP even if we don't want to process the event - capturedData.startRecording(); - capturedData.release(); + try { + // Currently we need to start and release the audio record to reset + // the DSP even if we don't want to process the eve + AudioRecord capturedData = createAudioRecordForEvent(event); + capturedData.startRecording(); + capturedData.release(); + } catch (IllegalArgumentException | UnsupportedOperationException e) { + Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event + + "), failed to create AudioRecord"); + } } })); } diff --git a/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl b/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl new file mode 100644 index 000000000000..50bbf4c41284 --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl @@ -0,0 +1,26 @@ +/* + * 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 com.android.internal.telecom; + +/* + * Adapter interface for using DeviceIdleController, since the PowerWhitelistManager is not + * directly accessible in the SYSTEM process. + */ +interface IDeviceIdleControllerAdapter { + void exemptAppTemporarilyForEvent(String packageName, long duration, int userHandle, + String reason); +}
\ No newline at end of file diff --git a/telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl b/telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl new file mode 100644 index 000000000000..b56010696361 --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl @@ -0,0 +1,27 @@ +/* + * 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 com.android.internal.telecom; + +import com.android.internal.telecom.IDeviceIdleControllerAdapter; + +/* + * Interface used to retrieve services that are only accessible via LocalService in the SYSTEM + * process. + */ +interface IInternalServiceRetriever { + IDeviceIdleControllerAdapter getDeviceIdleController(); +} diff --git a/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl new file mode 100644 index 000000000000..eda0f5b24958 --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl @@ -0,0 +1,28 @@ +/* + * 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 com.android.internal.telecom; + +import com.android.internal.telecom.ITelecomService; +import com.android.internal.telecom.IInternalServiceRetriever; + +/* + * Internal interface for getting an instance of the ITelecomService for external publication. + * Allows the TelecomLoaderService to pass additional dependencies required for creation. + */ +interface ITelecomLoader { + ITelecomService createTelecomService(IInternalServiceRetriever retriever); +} diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 52f1e37e69f4..5d79775f686e 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1642,6 +1642,15 @@ public class CarrierConfigManager { "hide_lte_plus_data_icon_bool"; /** + * The combined channel bandwidth threshold (non-inclusive) in KHz required to display the + * LTE+ data icon. It is 20000 by default, meaning the LTE+ icon will be shown if the device is + * using carrier aggregation and the combined channel bandwidth is strictly greater than 20 MHz. + * @hide + */ + public static final String KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT = + "lte_plus_threshold_bandwidth_khz_int"; + + /** * The string is used to filter redundant string from PLMN Network Name that's supplied by * specific carrier. * @@ -4211,6 +4220,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, ""); sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, ""); sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true); + sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000); sDefaults.putBoolean(KEY_NR_ENABLED_BOOL, true); sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false); diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 77fa673f1960..90edc4523b7b 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -30,6 +30,9 @@ import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECParameterSpec; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -1442,4 +1445,50 @@ public class WifiEnterpriseConfig implements Parcelable { } return TextUtils.isEmpty(getCaPath()); } + + /** + * Check if a given certificate Get the Suite-B cipher from the certificate + * + * @param x509Certificate Certificate to process + * @return true if the certificate OID matches the Suite-B requirements for RSA or ECDSA + * certificates, or false otherwise. + * @hide + */ + public static boolean isSuiteBCipherCert(@Nullable X509Certificate x509Certificate) { + if (x509Certificate == null) { + return false; + } + final String sigAlgOid = x509Certificate.getSigAlgOID(); + + // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates + // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192 + // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term + // Suite-B was already coined in the IEEE 802.11-2016 specification for + // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates + // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally + // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments, + // we are supporting both types here. + if (sigAlgOid.equals("1.2.840.113549.1.1.12")) { + // sha384WithRSAEncryption + if (x509Certificate.getPublicKey() instanceof RSAPublicKey) { + final RSAPublicKey rsaPublicKey = (RSAPublicKey) x509Certificate.getPublicKey(); + if (rsaPublicKey.getModulus() != null + && rsaPublicKey.getModulus().bitLength() >= 3072) { + return true; + } + } + } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) { + // ecdsa-with-SHA384 + if (x509Certificate.getPublicKey() instanceof ECPublicKey) { + final ECPublicKey ecPublicKey = (ECPublicKey) x509Certificate.getPublicKey(); + final ECParameterSpec ecParameterSpec = ecPublicKey.getParams(); + + if (ecParameterSpec != null && ecParameterSpec.getOrder() != null + && ecParameterSpec.getOrder().bitLength() >= 384) { + return true; + } + } + } + return false; + } } diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index b0213b0ef502..e12bb9178235 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -78,12 +78,12 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc private @Nullable String mWpa3SaePassphrase; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the WPA-EAP networks. + * certificates and other settings associated with the WPA/WPA2-Enterprise networks. */ private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the SuiteB networks. + * certificates and other settings associated with the WPA3-Enterprise networks. */ private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig; /** @@ -243,7 +243,11 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc /** * Set the associated enterprise configuration for this network. Needed for authenticating - * to WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description. + * to WPA3-Enterprise networks (standard and 192-bit security). See + * {@link WifiEnterpriseConfig} for description. For 192-bit security networks, both the + * client and CA certificates must be provided, and must be of type of either + * sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384 + * (OID 1.2.840.10045.4.3.3). * * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. * @return Instance of {@link Builder} to enable chaining of the builder method. @@ -284,8 +288,25 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); configuration.enterpriseConfig = mWpa2EnterpriseConfig; - } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network - configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); + } else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise + if (mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS + && WifiEnterpriseConfig.isSuiteBCipherCert( + mWpa3EnterpriseConfig.getClientCertificate()) + && WifiEnterpriseConfig.isSuiteBCipherCert( + mWpa3EnterpriseConfig.getCaCertificate())) { + // WPA3-Enterprise in 192-bit security mode (Suite-B) + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); + } else { + // WPA3-Enterprise + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); + configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); + configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + configuration.allowedPairwiseCiphers.set( + WifiConfiguration.PairwiseCipher.GCMP_256); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); + configuration.requirePmf = true; + } configuration.enterpriseConfig = mWpa3EnterpriseConfig; } else if (mIsEnhancedOpen) { // OWE network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 68eb1bbd8a79..d8be1d2c853c 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -72,12 +72,12 @@ public final class WifiNetworkSuggestion implements Parcelable { private @Nullable String mWpa3SaePassphrase; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the WPA-EAP networks. + * certificates and other settings associated with the WPA/WPA2-Enterprise networks. */ private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig; /** * The enterprise configuration details specifying the EAP method, - * certificates and other settings associated with the SuiteB networks. + * certificates and other settings associated with the WPA3-Enterprise networks. */ private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig; /** @@ -276,7 +276,11 @@ public final class WifiNetworkSuggestion implements Parcelable { /** * Set the associated enterprise configuration for this network. Needed for authenticating - * to WPA3 enterprise networks. See {@link WifiEnterpriseConfig} for description. + * to WPA3-Enterprise networks (standard and 192-bit security). See + * {@link WifiEnterpriseConfig} for description. For 192-bit security networks, both the + * client and CA certificates must be provided, and must be of type of either + * sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384 + * (OID 1.2.840.10045.4.3.3). * * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}. * @return Instance of {@link Builder} to enable chaining of the builder method. @@ -522,8 +526,25 @@ public final class WifiNetworkSuggestion implements Parcelable { } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); configuration.enterpriseConfig = mWpa2EnterpriseConfig; - } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network - configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); + } else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise + if (mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS + && WifiEnterpriseConfig.isSuiteBCipherCert( + mWpa3EnterpriseConfig.getClientCertificate()) + && WifiEnterpriseConfig.isSuiteBCipherCert( + mWpa3EnterpriseConfig.getCaCertificate())) { + // WPA3-Enterprise in 192-bit security mode (Suite-B) + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); + } else { + // WPA3-Enterprise + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); + configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); + configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + configuration.allowedPairwiseCiphers.set( + WifiConfiguration.PairwiseCipher.GCMP_256); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); + configuration.requirePmf = true; + } configuration.enterpriseConfig = mWpa3EnterpriseConfig; } else if (mIsEnhancedOpen) { // OWE network configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); diff --git a/wifi/tests/src/android/net/wifi/FakeKeys.java b/wifi/tests/src/android/net/wifi/FakeKeys.java index 641b891a1f4d..c0d60c33f99c 100644 --- a/wifi/tests/src/android/net/wifi/FakeKeys.java +++ b/wifi/tests/src/android/net/wifi/FakeKeys.java @@ -214,35 +214,6 @@ public class FakeKeys { }; public static final PrivateKey RSA_KEY1 = loadPrivateRSAKey(FAKE_RSA_KEY_1); - private static final String CLIENT_SUITE_B_RSA3072_CERT_STRING = - "-----BEGIN CERTIFICATE-----\n" - + "MIIERzCCAq8CFDopjyNgaj+c2TN2k06h7okEWpHJMA0GCSqGSIb3DQEBDAUAMF4x\n" - + "CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDTVRWMRAwDgYDVQQK\n" - + "DAdBbmRyb2lkMQ4wDAYDVQQLDAVXaS1GaTESMBAGA1UEAwwJdW5pdGVzdENBMB4X\n" - + "DTIwMDcyMTAyMjkxMVoXDTMwMDUzMDAyMjkxMVowYjELMAkGA1UEBhMCVVMxCzAJ\n" - + "BgNVBAgMAkNBMQwwCgYDVQQHDANNVFYxEDAOBgNVBAoMB0FuZHJvaWQxDjAMBgNV\n" - + "BAsMBVdpLUZpMRYwFAYDVQQDDA11bml0ZXN0Q2xpZW50MIIBojANBgkqhkiG9w0B\n" - + "AQEFAAOCAY8AMIIBigKCAYEAwSK3C5K5udtCKTnE14e8z2cZvwmB4Xe+a8+7QLud\n" - + "Hooc/lQzClgK4MbVUC0D3FE+U32C78SxKoTaRWtvPmNm+UaFT8KkwyUno/dv+2XD\n" - + "pd/zARQ+3FwAfWopAhEyCVSxwsCa+slQ4juRIMIuUC1Mm0NaptZyM3Tj/ICQEfpk\n" - + "o9qVIbiK6eoJMTkY8EWfAn7RTFdfR1OLuO0mVOjgLW9/+upYv6hZ19nAMAxw4QTJ\n" - + "x7lLwALX7B+tDYNEZHDqYL2zyvQWAj2HClere8QYILxkvktgBg2crEJJe4XbDH7L\n" - + "A3rrXmsiqf1ZbfFFEzK9NFqovL+qGh+zIP+588ShJFO9H/RDnDpiTnAFTWXQdTwg\n" - + "szSS0Vw2PB+JqEABAa9DeMvXT1Oy+NY3ItPHyy63nQZVI2rXANw4NhwS0Z6DF+Qs\n" - + "TNrj+GU7e4SG/EGR8SvldjYfQTWFLg1l/UT1hOOkQZwdsaW1zgKyeuiFB2KdMmbA\n" - + "Sq+Ux1L1KICo0IglwWcB/8nnAgMBAAEwDQYJKoZIhvcNAQEMBQADggGBAMYwJkNw\n" - + "BaCviKFmReDTMwWPRy4AMNViEeqAXgERwDEKwM7efjsaj5gctWfKsxX6UdLzkhgg\n" - + "6S/T6PxVWKzJ6l7SoOuTa6tMQOZp+h3R1mdfEQbw8B5cXBxZ+batzAai6Fiy1FKS\n" - + "/ka3INbcGfYuIYghfTrb4/NJKN06ZaQ1bpPwq0e4gN7800T2nbawvSf7r+8ZLcG3\n" - + "6bGCjRMwDSIipNvOwoj3TG315XC7TccX5difQ4sKOY+d2MkVJ3RiO0Ciw2ZbEW8d\n" - + "1FH5vUQJWnBUfSFznosGzLwH3iWfqlP+27jNE+qB2igEwCRFgVAouURx5ou43xuX\n" - + "qf6JkdI3HTJGLIWxkp7gOeln4dEaYzKjYw+P0VqJvKVqQ0IXiLjHgE0J9p0vgyD6\n" - + "HVVcP7U8RgqrbIjL1QgHU4KBhGi+WSUh/mRplUCNvHgcYdcHi/gHpj/j6ubwqIGV\n" - + "z4iSolAHYTmBWcLyE0NgpzE6ntp+53r2KaUJA99l2iGVzbWTwqPSm0XAVw==\n" - + "-----END CERTIFICATE-----\n"; - public static final X509Certificate CLIENT_SUITE_B_RSA3072_CERT = - loadCertificate(CLIENT_SUITE_B_RSA3072_CERT_STRING); - private static X509Certificate loadCertificate(String blob) { try { final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java index d78c942d55e2..1a4427034756 100644 --- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -282,6 +282,12 @@ public class SoftApConfigurationTest { .build(); assertNull(band_6g_config.toWifiConfiguration()); + SoftApConfiguration sae_transition_config = new SoftApConfiguration.Builder() + .setPassphrase("secretsecret", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) + .build(); + + assertNull(sae_transition_config.toWifiConfiguration()); } @Test @@ -324,16 +330,5 @@ public class SoftApConfigurationTest { assertThat(wifiConfig_2g5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_ANY); assertThat(wifiConfig_2g5g.apChannel).isEqualTo(0); assertThat(wifiConfig_2g5g.hiddenSSID).isEqualTo(true); - - SoftApConfiguration softApConfig_sae_transition = new SoftApConfiguration.Builder() - .setPassphrase("secretsecret", - SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) - .build(); - - WifiConfiguration wifiConfig_sae_transition = - softApConfig_sae_transition.toWifiConfiguration(); - assertThat(wifiConfig_sae_transition.getAuthType()) - .isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK); - assertThat(wifiConfig_sae_transition.preSharedKey).isEqualTo("secretsecret"); } } diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index 8270d643ca65..638efb9f14ee 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -23,8 +23,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; -import android.net.wifi.EAPConstants; -import android.net.wifi.FakeKeys; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSp; import android.os.Parcel; @@ -34,11 +32,6 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -390,39 +383,19 @@ public class PasspointConfigurationTest { } /** - * Verify that the unique identifier generated is the same for two instances with different - * HomeSp node but same FQDN + * Verify that the unique identifier generated is different for two instances with different + * HomeSp node * * @throws Exception */ @Test - public void validateUniqueIdDifferentHomeSpSameFqdn() throws Exception { + public void validateUniqueIdDifferentHomeSp() throws Exception { PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - // Modify config2's RCOIs and friendly name to a different set of values + // Modify config2's RCOIs to a different set of values PasspointConfiguration config2 = PasspointTestUtils.createConfig(); HomeSp homeSp = config2.getHomeSp(); homeSp.setRoamingConsortiumOis(new long[] {0xaa, 0xbb}); - homeSp.setFriendlyName("Some other name"); - config2.setHomeSp(homeSp); - - assertEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Verify that the unique identifier generated is different for two instances with the same - * HomeSp node but different FQDN - * - * @throws Exception - */ - @Test - public void validateUniqueIdSameHomeSpDifferentFqdn() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - - // Modify config2's FQDN to a different value - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - HomeSp homeSp = config2.getHomeSp(); - homeSp.setFqdn("fqdn2.com"); config2.setHomeSp(homeSp); assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); @@ -430,15 +403,15 @@ public class PasspointConfigurationTest { /** * Verify that the unique identifier generated is different for two instances with different - * SIM Credential node + * Credential node * * @throws Exception */ @Test - public void validateUniqueIdDifferentSimCredential() throws Exception { + public void validateUniqueIdDifferentCredential() throws Exception { PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - // Modify config2's realm and SIM credential to a different set of values + // Modify config2's RCOIs to a different set of values PasspointConfiguration config2 = PasspointTestUtils.createConfig(); Credential credential = config2.getCredential(); credential.setRealm("realm2.example.com"); @@ -449,157 +422,6 @@ public class PasspointConfigurationTest { } /** - * Verify that the unique identifier generated is different for two instances with different - * Realm in the Credential node - * - * @throws Exception - */ - @Test - public void validateUniqueIdDifferentRealm() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - - // Modify config2's realm to a different set of values - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - Credential credential = config2.getCredential(); - credential.setRealm("realm2.example.com"); - config2.setCredential(credential); - - assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Verify that the unique identifier generated is the same for two instances with different - * password and same username in the User Credential node - * - * @throws Exception - */ - @Test - public void validateUniqueIdSameUserInUserCredential() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - Credential credential = createCredentialWithUserCredential("user", "passwd"); - config1.setCredential(credential); - - // Modify config2's Passpowrd to a different set of values - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - credential = createCredentialWithUserCredential("user", "newpasswd"); - config2.setCredential(credential); - - assertEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Verify that the unique identifier generated is different for two instances with different - * username in the User Credential node - * - * @throws Exception - */ - @Test - public void validateUniqueIdDifferentUserCredential() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - Credential credential = createCredentialWithUserCredential("user", "passwd"); - config1.setCredential(credential); - - // Modify config2's username to a different value - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - credential = createCredentialWithUserCredential("user2", "passwd"); - config2.setCredential(credential); - - assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Verify that the unique identifier generated is different for two instances with different - * Cert Credential node - * - * @throws Exception - */ - @Test - public void validateUniqueIdDifferentCertCredential() throws Exception { - PasspointConfiguration config1 = PasspointTestUtils.createConfig(); - Credential credential = createCredentialWithCertificateCredential(true, true); - config1.setCredential(credential); - - // Modify config2's cert credential to a different set of values - PasspointConfiguration config2 = PasspointTestUtils.createConfig(); - credential = createCredentialWithCertificateCredential(false, false); - config2.setCredential(credential); - - assertNotEquals(config1.getUniqueId(), config2.getUniqueId()); - } - - /** - * Helper function for generating certificate credential for testing. - * - * @return {@link Credential} - */ - private static Credential createCredentialWithCertificateCredential(Boolean useCaCert0, - Boolean useCert0) - throws NoSuchAlgorithmException, CertificateEncodingException { - Credential.CertificateCredential certCred = new Credential.CertificateCredential(); - certCred.setCertType("x509v3"); - if (useCert0) { - certCred.setCertSha256Fingerprint( - MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded())); - } else { - certCred.setCertSha256Fingerprint(MessageDigest.getInstance("SHA-256") - .digest(FakeKeys.CLIENT_SUITE_B_RSA3072_CERT.getEncoded())); - } - return createCredential(null, certCred, null, new X509Certificate[] {FakeKeys.CLIENT_CERT}, - FakeKeys.RSA_KEY1, useCaCert0 ? FakeKeys.CA_CERT0 : FakeKeys.CA_CERT1); - } - - /** - * Helper function for generating user credential for testing. - * - * @return {@link Credential} - */ - private static Credential createCredentialWithUserCredential(String username, String password) { - Credential.UserCredential userCred = new Credential.UserCredential(); - userCred.setUsername(username); - userCred.setPassword(password); - userCred.setMachineManaged(true); - userCred.setAbleToShare(true); - userCred.setSoftTokenApp("TestApp"); - userCred.setEapType(EAPConstants.EAP_TTLS); - userCred.setNonEapInnerMethod("MS-CHAP"); - return createCredential(userCred, null, null, null, null, FakeKeys.CA_CERT0); - } - - /** - * Helper function for generating Credential for testing. - * - * @param userCred Instance of UserCredential - * @param certCred Instance of CertificateCredential - * @param simCred Instance of SimCredential - * @param clientCertificateChain Chain of client certificates - * @param clientPrivateKey Client private key - * @param caCerts CA certificates - * @return {@link Credential} - */ - private static Credential createCredential(Credential.UserCredential userCred, - Credential.CertificateCredential certCred, - Credential.SimCredential simCred, - X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey, - X509Certificate... caCerts) { - Credential cred = new Credential(); - cred.setCreationTimeInMillis(123455L); - cred.setExpirationTimeInMillis(2310093L); - cred.setRealm("realm"); - cred.setCheckAaaServerCertStatus(true); - cred.setUserCredential(userCred); - cred.setCertCredential(certCred); - cred.setSimCredential(simCred); - if (caCerts != null && caCerts.length == 1) { - cred.setCaCertificate(caCerts[0]); - } else { - cred.setCaCertificates(caCerts); - } - cred.setClientCertificateChain(clientCertificateChain); - cred.setClientPrivateKey(clientPrivateKey); - return cred; - } - - /** * Verify that the unique identifier API generates an exception if HomeSP is not initialized. * * @throws Exception diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java index a44df40a8e97..829d8f0a9a3a 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java @@ -593,10 +593,10 @@ public class CredentialTest { } /** - * Verify that unique identifiers are different for a credential with different username + * Verify that unique identifiers are different for a credential with different values */ @Test - public void testUniqueIdDifferentForUserCredentialsWithDifferentUsername() throws Exception { + public void testUniqueIdDifferentForUserCredentialsWithDifferentValues() throws Exception { Credential userCred1 = createCredentialWithUserCredential(); Credential userCred2 = createCredentialWithUserCredential(); userCred2.getUserCredential().setUsername("anotheruser"); @@ -605,24 +605,7 @@ public class CredentialTest { } /** - * Verify that unique identifiers are different for a credential with different password and - * other values other than username - */ - @Test - public void testUniqueIdSameForUserCredentialsWithDifferentPassword() throws Exception { - Credential userCred1 = createCredentialWithUserCredential(); - Credential userCred2 = createCredentialWithUserCredential(); - userCred2.getUserCredential().setPassword("someotherpassword!"); - userCred2.getUserCredential().setMachineManaged(false); - userCred2.getUserCredential().setAbleToShare(false); - userCred2.getUserCredential().setSoftTokenApp("TestApp2"); - userCred2.getUserCredential().setNonEapInnerMethod("PAP"); - - assertEquals(userCred1.getUniqueId(), userCred2.getUniqueId()); - } - - /** - * Verify that unique identifiers are different for a cert credential with different values + * Verify that unique identifiers are different for a credential with different values */ @Test public void testUniqueIdDifferentForCertCredentialsWithDifferentValues() throws Exception { |