diff options
46 files changed, 1024 insertions, 268 deletions
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index b5b101773f15..a56965bdbd4d 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -30,7 +30,7 @@ oneway interface ITaskStackListener { void onTaskStackChanged(); /** Called whenever an Activity is moved to the pinned stack from another stack. */ - void onActivityPinned(String packageName, int taskId); + void onActivityPinned(String packageName, int userId, int taskId); /** Called whenever an Activity is moved from the pinned stack to another stack. */ void onActivityUnpinned(); diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index a52ca0a64cd2..4674c9cd2389 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -31,7 +31,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onActivityPinned(String packageName, int taskId) throws RemoteException { + public void onActivityPinned(String packageName, int userId, int taskId) + throws RemoteException { } @Override diff --git a/core/java/android/app/timezone/RulesUpdaterContract.java b/core/java/android/app/timezone/RulesUpdaterContract.java index 9c62f46b6e36..2a22eadd28f2 100644 --- a/core/java/android/app/timezone/RulesUpdaterContract.java +++ b/core/java/android/app/timezone/RulesUpdaterContract.java @@ -51,7 +51,7 @@ public final class RulesUpdaterContract { * applies. */ public static final String ACTION_TRIGGER_RULES_UPDATE_CHECK = - "android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK"; + "com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK"; /** * The extra containing the {@code byte[]} that should be passed to @@ -61,7 +61,7 @@ public final class RulesUpdaterContract { * {@link #ACTION_TRIGGER_RULES_UPDATE_CHECK} intent has been processed. */ public static final String EXTRA_CHECK_TOKEN = - "android.intent.extra.timezone.CHECK_TOKEN"; + "com.android.intent.extra.timezone.CHECK_TOKEN"; /** * Creates an intent that would trigger a time zone rules update check. diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index aa35a6610db7..931b5c913851 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -16,10 +16,11 @@ package android.hardware; -import android.app.ActivityThread; +import static android.system.OsConstants.*; + import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.app.job.JobInfo; +import android.app.ActivityThread; import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Point; @@ -34,11 +35,11 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.renderscript.Allocation; import android.renderscript.Element; -import android.renderscript.RenderScript; import android.renderscript.RSIllegalArgumentException; +import android.renderscript.RenderScript; import android.renderscript.Type; -import android.util.Log; import android.text.TextUtils; +import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; @@ -48,8 +49,6 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; -import static android.system.OsConstants.*; - /** * The Camera class is used to set image capture settings, start/stop preview, * snap pictures, and retrieve frames for encoding for video. This class is a @@ -243,12 +242,19 @@ public class Camera { /** * Returns the number of physical cameras available on this device. + * + * @return total number of accessible camera devices, or 0 if there are no + * cameras or an error was encountered enumerating them. */ public native static int getNumberOfCameras(); /** * Returns the information about a particular camera. * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1. + * + * @throws RuntimeException if an invalid ID is provided, or if there is an + * error retrieving the information (generally due to a hardware or other + * low-level failure). */ public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { _getCameraInfo(cameraId, cameraInfo); @@ -362,7 +368,10 @@ public class Camera { /** * Creates a new Camera object to access the first back-facing camera on the * device. If the device does not have a back-facing camera, this returns - * null. + * null. Otherwise acts like the {@link #open(int)} call. + * + * @return a new Camera object for the first back-facing camera, or null if there is no + * backfacing camera * @see #open(int) */ public static Camera open() { @@ -609,6 +618,8 @@ public class Camera { * * @throws IOException if a connection cannot be re-established (for * example, if the camera is still in use by another process). + * @throws RuntimeException if release() has been called on this Camera + * instance. */ public native final void reconnect() throws IOException; @@ -637,6 +648,8 @@ public class Camera { * or null to remove the preview surface * @throws IOException if the method fails (for example, if the surface * is unavailable or unsuitable). + * @throws RuntimeException if release() has been called on this Camera + * instance. */ public final void setPreviewDisplay(SurfaceHolder holder) throws IOException { if (holder != null) { @@ -684,6 +697,8 @@ public class Camera { * texture * @throws IOException if the method fails (for example, if the surface * texture is unavailable or unsuitable). + * @throws RuntimeException if release() has been called on this Camera + * instance. */ public native final void setPreviewTexture(SurfaceTexture surfaceTexture) throws IOException; @@ -733,12 +748,20 @@ public class Camera { * {@link #setPreviewCallbackWithBuffer(Camera.PreviewCallback)} were * called, {@link Camera.PreviewCallback#onPreviewFrame(byte[], Camera)} * will be called when preview data becomes available. + * + * @throws RuntimeException if starting preview fails; usually this would be + * because of a hardware or other low-level error, or because release() + * has been called on this Camera instance. */ public native final void startPreview(); /** * Stops capturing and drawing preview frames to the surface, and * resets the camera for a future call to {@link #startPreview()}. + * + * @throws RuntimeException if stopping preview fails; usually this would be + * because of a hardware or other low-level error, or because release() + * has been called on this Camera instance. */ public final void stopPreview() { _stopPreview(); @@ -777,6 +800,8 @@ public class Camera { * * @param cb a callback object that receives a copy of each preview frame, * or null to stop receiving callbacks. + * @throws RuntimeException if release() has been called on this Camera + * instance. * @see android.media.MediaActionSound */ public final void setPreviewCallback(PreviewCallback cb) { @@ -803,6 +828,8 @@ public class Camera { * * @param cb a callback object that receives a copy of the next preview frame, * or null to stop receiving callbacks. + * @throws RuntimeException if release() has been called on this Camera + * instance. * @see android.media.MediaActionSound */ public final void setOneShotPreviewCallback(PreviewCallback cb) { @@ -840,6 +867,8 @@ public class Camera { * * @param cb a callback object that receives a copy of the preview frame, * or null to stop receiving callbacks and clear the buffer queue. + * @throws RuntimeException if release() has been called on this Camera + * instance. * @see #addCallbackBuffer(byte[]) * @see android.media.MediaActionSound */ @@ -1259,6 +1288,9 @@ public class Camera { * success sound to the user.</p> * * @param cb the callback to run + * @throws RuntimeException if starting autofocus fails; usually this would + * be because of a hardware or other low-level error, or because + * release() has been called on this Camera instance. * @see #cancelAutoFocus() * @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean) * @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean) @@ -1279,6 +1311,9 @@ public class Camera { * this function will return the focus position to the default. * If the camera does not support auto-focus, this is a no-op. * + * @throws RuntimeException if canceling autofocus fails; usually this would + * be because of a hardware or other low-level error, or because + * release() has been called on this Camera instance. * @see #autoFocus(Camera.AutoFocusCallback) */ public final void cancelAutoFocus() @@ -1333,6 +1368,9 @@ public class Camera { * Sets camera auto-focus move callback. * * @param cb the callback to run + * @throws RuntimeException if enabling the focus move callback fails; + * usually this would be because of a hardware or other low-level error, + * or because release() has been called on this Camera instance. */ public void setAutoFocusMoveCallback(AutoFocusMoveCallback cb) { mAutoFocusMoveCallback = cb; @@ -1384,7 +1422,7 @@ public class Camera { }; /** - * Equivalent to takePicture(shutter, raw, null, jpeg). + * Equivalent to <pre>takePicture(Shutter, raw, null, jpeg)</pre>. * * @see #takePicture(ShutterCallback, PictureCallback, PictureCallback, PictureCallback) */ @@ -1422,6 +1460,9 @@ public class Camera { * @param raw the callback for raw (uncompressed) image data, or null * @param postview callback with postview image data, may be null * @param jpeg the callback for JPEG image data, or null + * @throws RuntimeException if starting picture capture fails; usually this + * would be because of a hardware or other low-level error, or because + * release() has been called on this Camera instance. */ public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback postview, PictureCallback jpeg) { @@ -1534,6 +1575,9 @@ public class Camera { * * @param degrees the angle that the picture will be rotated clockwise. * Valid values are 0, 90, 180, and 270. + * @throws RuntimeException if setting orientation fails; usually this would + * be because of a hardware or other low-level error, or because + * release() has been called on this Camera instance. * @see #setPreviewDisplay(SurfaceHolder) */ public native final void setDisplayOrientation(int degrees); @@ -1559,6 +1603,9 @@ public class Camera { * changed. {@code false} if the shutter sound state could not be * changed. {@code true} is also returned if shutter sound playback * is already set to the requested state. + * @throws RuntimeException if the call fails; usually this would be because + * of a hardware or other low-level error, or because release() has been + * called on this Camera instance. * @see #takePicture * @see CameraInfo#canDisableShutterSound * @see ShutterCallback @@ -1903,6 +1950,9 @@ public class Camera { * If modifications are made to the returned Parameters, they must be passed * to {@link #setParameters(Camera.Parameters)} to take effect. * + * @throws RuntimeException if reading parameters fails; usually this would + * be because of a hardware or other low-level error, or because + * release() has been called on this Camera instance. * @see #setParameters(Camera.Parameters) */ public Parameters getParameters() { diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java index e64eb0d62992..1a9afccdabe2 100644 --- a/core/java/android/service/autofill/AutofillServiceInfo.java +++ b/core/java/android/service/autofill/AutofillServiceInfo.java @@ -29,11 +29,12 @@ import android.os.RemoteException; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; import com.android.internal.R; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; /** @@ -147,4 +148,9 @@ public final class AutofillServiceInfo { public String getSettingsActivity() { return mSettingsActivity; } + + @Override + public String toString() { + return mServiceInfo == null ? "null" : mServiceInfo.toString(); + } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index f888ba298339..e906a1fa7807 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1342,11 +1342,17 @@ public final class AutofillManager { } } - private void setSessionFinished() { - if (sVerbose) Log.v(TAG, "setSessionFinished()"); + /** + * Marks the state of the session as finished. + * + * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null} + * FillResponse) or {@link #STATE_UNKNOWN} (because the session was removed). + */ + private void setSessionFinished(int newState) { synchronized (mLock) { + if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState); resetSessionLocked(); - mState = STATE_FINISHED; + mState = newState; } } @@ -1413,7 +1419,7 @@ public final class AutofillManager { if (sessionFinished) { // Callback call was "hijacked" to also update the session state. - setSessionFinished(); + setSessionFinished(STATE_FINISHED); } } @@ -1898,10 +1904,10 @@ public final class AutofillManager { } @Override - public void setSessionFinished() { + public void setSessionFinished(int newState) { final AutofillManager afm = mAfm.get(); if (afm != null) { - afm.post(() -> afm.setSessionFinished()); + afm.post(() -> afm.setSessionFinished(newState)); } } } diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index db6855a4dbf4..3dabcec8636a 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -82,8 +82,9 @@ oneway interface IAutoFillManagerClient { void setSaveUiState(int sessionId, boolean shown); /** - * Marks the state of the session as finished (because the AutofillService returned a null - * FillResponse). + * Marks the state of the session as finished. + * @param newState STATE_FINISHED (because the autofill service returned a null + * FillResponse) or STATE_UNKNOWN (because the session was removed). */ - void setSessionFinished(); + void setSessionFinished(int newState); } diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java index b6034d1a5c12..aa172f9893f1 100644 --- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java +++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java @@ -582,6 +582,7 @@ public final class SmartSelectionEventTracker { case ActionType.SMART_SHARE: // fall through case ActionType.DRAG: // fall through case ActionType.ABANDON: // fall through + case ActionType.OTHER: // fall through return true; default: return false; diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 4ebb3cfea266..fceefaf8a28c 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -49,6 +49,8 @@ import java.util.regex.Pattern; @UiThread final class SelectionActionModeHelper { + private static final String LOG_TAG = "SelectActionModeHelper"; + /** * Maximum time (in milliseconds) to wait for a result before timing out. */ @@ -216,6 +218,7 @@ final class SelectionActionModeHelper { private int mSelectionStart; private int mSelectionEnd; private boolean mAllowReset; + private final LogAbandonRunnable mDelayedLogAbandon = new LogAbandonRunnable(); SelectionTracker(TextView textView) { mTextView = Preconditions.checkNotNull(textView); @@ -227,6 +230,10 @@ final class SelectionActionModeHelper { */ public void onOriginalSelection( CharSequence text, int selectionStart, int selectionEnd, boolean editableText) { + // If we abandoned a selection and created a new one very shortly after, we may still + // have a pending request to log ABANDON, which we flush here. + mDelayedLogAbandon.flush(); + mOriginalStart = mSelectionStart = selectionStart; mOriginalEnd = mSelectionEnd = selectionEnd; mAllowReset = false; @@ -267,12 +274,7 @@ final class SelectionActionModeHelper { public void onSelectionDestroyed() { mAllowReset = false; // Wait a few ms to see if the selection was destroyed because of a text change event. - mTextView.postDelayed(() -> { - mLogger.logSelectionAction( - mSelectionStart, mSelectionEnd, - SelectionEvent.ActionType.ABANDON, null /* classification */); - mSelectionStart = mSelectionEnd = -1; - }, 100 /* ms */); + mDelayedLogAbandon.schedule(100 /* ms */); } /** @@ -329,6 +331,38 @@ final class SelectionActionModeHelper { private boolean isSelectionStarted() { return mSelectionStart >= 0 && mSelectionEnd >= 0 && mSelectionStart != mSelectionEnd; } + + /** A helper for keeping track of pending abandon logging requests. */ + private final class LogAbandonRunnable implements Runnable { + private boolean mIsPending; + + /** Schedules an abandon to be logged with the given delay. Flush if necessary. */ + void schedule(int delayMillis) { + if (mIsPending) { + Log.e(LOG_TAG, "Force flushing abandon due to new scheduling request"); + flush(); + } + mIsPending = true; + mTextView.postDelayed(this, delayMillis); + } + + /** If there is a pending log request, execute it now. */ + void flush() { + mTextView.removeCallbacks(this); + run(); + } + + @Override + public void run() { + if (mIsPending) { + mLogger.logSelectionAction( + mSelectionStart, mSelectionEnd, + SelectionEvent.ActionType.ABANDON, null /* classification */); + mSelectionStart = mSelectionEnd = -1; + mIsPending = false; + } + } + } } // TODO: Write tests diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 167587e6e780..9eed12f679c4 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1357,7 +1357,7 @@ <!-- The package of the time zone rules updater application. Expected to be the same for all Android devices that support APK-based time zone rule updates. - A package-targeted android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent + A package-targeted com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent will be sent to the updater app if the system server detects an update to the updater or data app packages. The package referenced here must have the android.permission.UPDATE_TIME_ZONE_RULES @@ -1368,7 +1368,7 @@ <!-- The package of the time zone rules data application. Expected to be configured by OEMs to reference their own priv-app APK package. - A package-targeted android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent + A package-targeted com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent will be sent to the updater app if the system server detects an update to the updater or data app packages. [This is only used if config_enableUpdateableTimeZoneRules and diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index c3ef45088f8e..ceac3253e178 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -31,6 +31,7 @@ import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.Shader; +import android.os.Trace; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -605,38 +606,44 @@ public class VectorDrawable extends Drawable { public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme) throws XmlPullParserException, IOException { - if (mVectorState.mRootGroup != null || mVectorState.mNativeTree != null) { - // This VD has been used to display other VD resource content, clean up. - if (mVectorState.mRootGroup != null) { - // Subtract the native allocation for all the nodes. - VMRuntime.getRuntime().registerNativeFree(mVectorState.mRootGroup.getNativeSize()); - // Remove child nodes' reference to tree - mVectorState.mRootGroup.setTree(null); - } - mVectorState.mRootGroup = new VGroup(); - if (mVectorState.mNativeTree != null) { - // Subtract the native allocation for the tree wrapper, which contains root node - // as well as rendering related data. - VMRuntime.getRuntime().registerNativeFree(mVectorState.NATIVE_ALLOCATION_SIZE); - mVectorState.mNativeTree.release(); + try { + Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "VectorDrawable#inflate"); + if (mVectorState.mRootGroup != null || mVectorState.mNativeTree != null) { + // This VD has been used to display other VD resource content, clean up. + if (mVectorState.mRootGroup != null) { + // Subtract the native allocation for all the nodes. + VMRuntime.getRuntime().registerNativeFree( + mVectorState.mRootGroup.getNativeSize()); + // Remove child nodes' reference to tree + mVectorState.mRootGroup.setTree(null); + } + mVectorState.mRootGroup = new VGroup(); + if (mVectorState.mNativeTree != null) { + // Subtract the native allocation for the tree wrapper, which contains root node + // as well as rendering related data. + VMRuntime.getRuntime().registerNativeFree(mVectorState.NATIVE_ALLOCATION_SIZE); + mVectorState.mNativeTree.release(); + } + mVectorState.createNativeTree(mVectorState.mRootGroup); } - mVectorState.createNativeTree(mVectorState.mRootGroup); - } - final VectorDrawableState state = mVectorState; - state.setDensity(Drawable.resolveDensity(r, 0)); + final VectorDrawableState state = mVectorState; + state.setDensity(Drawable.resolveDensity(r, 0)); - final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable); - updateStateFromTypedArray(a); - a.recycle(); + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable); + updateStateFromTypedArray(a); + a.recycle(); - mDpiScaledDirty = true; + mDpiScaledDirty = true; - state.mCacheDirty = true; - inflateChildElements(r, parser, attrs, theme); + state.mCacheDirty = true; + inflateChildElements(r, parser, attrs, theme); - state.onTreeConstructionFinished(); - // Update local properties. - updateLocalState(r); + state.onTreeConstructionFinished(); + // Update local properties. + updateLocalState(r); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); + } } private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException { diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp index f9a1cc5296d5..2687410897ac 100644 --- a/libs/hwui/OpenGLReadback.cpp +++ b/libs/hwui/OpenGLReadback.cpp @@ -280,6 +280,11 @@ CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer, SkBitmap* bitmap) { + if (!layer.isRenderable()) { + // layer has never been updated by DeferredLayerUpdater, abort copy + return false; + } + return CopyResult::Success == copyTextureInto(Caches::getInstance(), renderThread.renderState(), layer.getTexture(), layer.getTexTransform(), Rect(), bitmap); diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 8a1de02e6a0c..ca179c9a25d2 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -22,6 +22,7 @@ #include "SkShader.h" #include <utils/Log.h> #include "utils/Macros.h" +#include "utils/TraceUtils.h" #include "utils/VectorDrawableUtils.h" #include <math.h> @@ -593,14 +594,17 @@ void Tree::draw(SkCanvas* canvas) { void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { SkBitmap outCache; bitmap.getSkBitmap(&outCache); + int cacheWidth = outCache.width(); + int cacheHeight = outCache.height(); + ATRACE_FORMAT("VectorDrawable repaint %dx%d", cacheWidth, cacheHeight); outCache.eraseColor(SK_ColorTRANSPARENT); SkCanvas outCanvas(outCache); float viewportWidth = useStagingData ? mStagingProperties.getViewportWidth() : mProperties.getViewportWidth(); float viewportHeight = useStagingData ? mStagingProperties.getViewportHeight() : mProperties.getViewportHeight(); - float scaleX = outCache.width() / viewportWidth; - float scaleY = outCache.height() / viewportHeight; + float scaleX = cacheWidth / viewportWidth; + float scaleY = cacheHeight / viewportHeight; outCanvas.scale(scaleX, scaleY); mRootNode->draw(&outCanvas, useStagingData); } diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp new file mode 100644 index 000000000000..04fc2d46f946 --- /dev/null +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestSceneBase.h" +#include "tests/common/BitmapAllocationTestUtils.h" +#include "SkBlendMode.h" + +class TvApp; +class TvAppNoRoundedCorner; +class TvAppColorFilter; +class TvAppNoRoundedCornerColorFilter; + +static bool _TvApp( + BitmapAllocationTestUtils::registerBitmapAllocationScene<TvApp>( + "tvapp", "A dense grid of cards:" + "with rounded corner, using overlay RenderNode for dimming.")); + +static bool _TvAppNoRoundedCorner( + BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCorner>( + "tvapp_norc", "A dense grid of cards:" + "no rounded corner, using overlay RenderNode for dimming")); + +static bool _TvAppColorFilter( + BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppColorFilter>( + "tvapp_cf", "A dense grid of cards:" + "with rounded corner, using ColorFilter for dimming")); + +static bool _TvAppNoRoundedCornerColorFilter( + BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCornerColorFilter>( + "tvapp_norc_cf", "A dense grid of cards:" + "no rounded corner, using ColorFilter for dimming")); + +class TvApp : public TestScene { +public: + TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TestScene() + , mAllocator(allocator) { } + + sp<RenderNode> mBg; + std::vector<sp<RenderNode>> mCards; + std::vector<sp<RenderNode>> mInfoAreas; + std::vector<sp<RenderNode>> mImages; + std::vector<sp<RenderNode>> mOverlays; + std::vector<sk_sp<Bitmap>> mCachedBitmaps; + BitmapAllocationTestUtils::BitmapAllocator mAllocator; + sk_sp<Bitmap> mSingleBitmap; + int mSeed = 0; + int mSeed2 = 0; + + void createContent(int width, int height, Canvas& canvas) override { + mBg = createBitmapNode(canvas, 0xFF9C27B0, 0, 0, width, height); + canvas.drawRenderNode(mBg.get()); + + canvas.insertReorderBarrier(true); + mSingleBitmap = mAllocator(dp(160), dp(120), kRGBA_8888_SkColorType, + [](SkBitmap& skBitmap) { + skBitmap.eraseColor(0xFF0000FF); + }); + + for (int y = dp(18) - dp(178); y < height - dp(18); y += dp(178)) { + bool isFirstCard = true; + for (int x = dp(18); x < width - dp(18); x += dp(178)) { + sp<RenderNode> card = createCard(x, y, dp(160), dp(160), isFirstCard); + isFirstCard = false; + canvas.drawRenderNode(card.get()); + mCards.push_back(card); + } + } + canvas.insertReorderBarrier(false); + } + + void doFrame(int frameNr) override { + size_t numCards = mCards.size(); + for (size_t ci = 0; ci < numCards; ci++) { + updateCard(ci, frameNr); + } + } + +private: + sp<RenderNode> createBitmapNode(Canvas& canvas, SkColor color, int left, int top, + int width, int height) { + return TestUtils::createNode(left, top, left + width , top + height, + [this, width, height, color](RenderProperties& props, Canvas& canvas) { + sk_sp<Bitmap> bitmap = mAllocator(width, height, kRGBA_8888_SkColorType, + [color](SkBitmap& skBitmap) { + skBitmap.eraseColor(color); + }); + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + }); + } + + sp<RenderNode> createSharedBitmapNode(Canvas& canvas, int left, int top, + int width, int height, sk_sp<Bitmap>bitmap) { + return TestUtils::createNode(left, top, left + width , top + height, + [bitmap](RenderProperties& props, Canvas& canvas) { + canvas.drawBitmap(*bitmap, 0, 0, nullptr); + }); + } + + sp<RenderNode> createInfoNode(Canvas& canvas, int left, int top, + int width, int height, const char* text, const char* text2) { + return TestUtils::createNode(left, top, left + width , top + height, + [text, text2](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver); + + SkPaint paint; + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setAntiAlias(true); + paint.setTextSize(24); + + paint.setColor(Color::Black); + TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 10, 30); + paint.setTextSize(20); + TestUtils::drawUtf8ToCanvas(&canvas, text2, paint, 10, 54); + + }); + } + + sp<RenderNode> createColorNode(Canvas& canvas, int left, int top, + int width, int height, SkColor color) { + return TestUtils::createNode(left, top, left + width , top + height, + [color](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(color, SkBlendMode::kSrcOver); + }); + } + + virtual bool useSingleBitmap() { + return false; + } + + virtual float roundedCornerRadius() { + return dp(2); + } + + // when true, use overlay RenderNode for dimming, otherwise apply a ColorFilter to dim image + virtual bool useOverlay() { + return true; + } + + sp<RenderNode> createCard(int x, int y, int width, int height, bool selected) { + return TestUtils::createNode(x, y, x + width, y + height, + [width, height, selected, this](RenderProperties& props, Canvas& canvas) { + if (selected) { + props.setElevation(dp(16)); + props.setScaleX(1.2); + props.setScaleY(1.2); + } + props.mutableOutline().setRoundRect(0, 0, width, height, roundedCornerRadius(), 1); + props.mutableOutline().setShouldClip(true); + + sk_sp<Bitmap> bitmap = useSingleBitmap() ? mSingleBitmap + : mAllocator(width, dp(120), kRGBA_8888_SkColorType, [this](SkBitmap& skBitmap) { + skBitmap.eraseColor(0xFF000000 | ((mSeed << 3) & 0xFF)); + }); + sp<RenderNode> cardImage = createSharedBitmapNode(canvas, 0, 0, width, dp(120), + bitmap); + canvas.drawRenderNode(cardImage.get()); + mCachedBitmaps.push_back(bitmap); + mImages.push_back(cardImage); + + char buffer[128]; + sprintf(buffer, "Video %d-%d", mSeed, mSeed + 1); + mSeed++; + char buffer2[128]; + sprintf(buffer2, "Studio %d", mSeed2++); + sp<RenderNode> infoArea = createInfoNode(canvas, 0, dp(120), width, height, buffer, buffer2); + canvas.drawRenderNode(infoArea.get()); + mInfoAreas.push_back(infoArea); + + if (useOverlay()) { + sp<RenderNode> overlayColor = createColorNode(canvas, 0, 0, width, height, 0x00000000); + canvas.drawRenderNode(overlayColor.get()); + mOverlays.push_back(overlayColor); + } + }); + } + + void updateCard(int ci, int curFrame) { + // updating card's translation Y + sp<RenderNode> card = mCards[ci]; + card->setPropertyFieldsDirty(RenderNode::Y); + card->mutateStagingProperties().setTranslationY(curFrame % 150); + + // re-recording card's canvas, not necessary but to add some burden to CPU + std::unique_ptr<Canvas> cardcanvas(Canvas::create_recording_canvas( + card->stagingProperties().getWidth(), + card->stagingProperties().getHeight())); + sp<RenderNode> image = mImages[ci]; + sp<RenderNode> infoArea = mInfoAreas[ci]; + cardcanvas->drawRenderNode(infoArea.get()); + + if (useOverlay()) { + cardcanvas->drawRenderNode(image.get()); + // re-recording card overlay's canvas, animating overlay color alpha + sp<RenderNode> overlay = mOverlays[ci]; + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( + overlay->stagingProperties().getWidth(), + overlay->stagingProperties().getHeight())); + canvas->drawColor((curFrame % 150) << 24, SkBlendMode::kSrcOver); + overlay->setStagingDisplayList(canvas->finishRecording()); + cardcanvas->drawRenderNode(overlay.get()); + } else { + // re-recording image node's canvas, animating ColorFilter + std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( + image->stagingProperties().getWidth(), + image->stagingProperties().getHeight())); + SkPaint paint; + sk_sp<SkColorFilter> filter(SkColorFilter::MakeModeFilter((curFrame % 150) << 24, + SkBlendMode::kSrcATop)); + paint.setColorFilter(filter); + sk_sp<Bitmap> bitmap = mCachedBitmaps[ci]; + canvas->drawBitmap(*bitmap, 0, 0, &paint); + image->setStagingDisplayList(canvas->finishRecording()); + cardcanvas->drawRenderNode(image.get()); + } + + card->setStagingDisplayList(cardcanvas->finishRecording()); + } +}; + +class TvAppNoRoundedCorner : public TvApp { +public: + TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TvApp(allocator) { } + +private: + + virtual float roundedCornerRadius() override { + return dp(0); + } +}; + +class TvAppColorFilter : public TvApp { +public: + TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TvApp(allocator) { } + +private: + + virtual bool useOverlay() override { + return false; + } +}; + +class TvAppNoRoundedCornerColorFilter : public TvApp { +public: + TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) + : TvApp(allocator) { } + +private: + + virtual float roundedCornerRadius() override { + return dp(0); + } + + virtual bool useOverlay() override { + return false; + } +}; + + diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index dc7fa8c00f82..3308fc929b03 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -28,7 +28,6 @@ interface IMediaRouterService { MediaRouterClientState getState(IMediaRouterClient client); boolean isPlaybackActive(IMediaRouterClient client); - boolean isGlobalBluetoothA2doOn(); void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan); void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit); diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index b072f659f7ce..013e85e3cbf2 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -184,25 +184,33 @@ public class MediaRouter { void updateAudioRoutes(AudioRoutesInfo newRoutes) { boolean audioRoutesChanged = false; + boolean forceUseDefaultRoute = false; + if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) { mCurAudioRoutesInfo.mainType = newRoutes.mainType; int name; - if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0 - || (newRoutes.mainType&AudioRoutesInfo.MAIN_HEADSET) != 0) { + if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0 + || (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { name = com.android.internal.R.string.default_audio_route_name_headphones; - } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { name = com.android.internal.R.string.default_audio_route_name_dock_speakers; - } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) { + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { name = com.android.internal.R.string.default_media_route_name_hdmi; } else { name = com.android.internal.R.string.default_audio_route_name; } mDefaultAudioVideo.mNameResId = name; dispatchRouteChanged(mDefaultAudioVideo); + + if ((newRoutes.mainType & (AudioRoutesInfo.MAIN_HEADSET + | AudioRoutesInfo.MAIN_HEADPHONES | AudioRoutesInfo.MAIN_USB)) != 0) { + forceUseDefaultRoute = true; + } audioRoutesChanged = true; } if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) { + forceUseDefaultRoute = false; mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName; if (mCurAudioRoutesInfo.bluetoothName != null) { if (mBluetoothA2dpRoute == null) { @@ -231,30 +239,18 @@ public class MediaRouter { Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn()); if (mSelectedRoute == null || mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == mBluetoothA2dpRoute) { - selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, getDefaultSystemAudioRoute(), false); + if (forceUseDefaultRoute || mBluetoothA2dpRoute == null) { + selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false); + } else { + selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false); + } } } } - RouteInfo getDefaultSystemAudioRoute() { - boolean globalBluetoothA2doOn = false; - try { - globalBluetoothA2doOn = mMediaRouterService.isGlobalBluetoothA2doOn(); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to call isSystemBluetoothA2doOn.", ex); - } - return (globalBluetoothA2doOn && mBluetoothA2dpRoute != null) - ? mBluetoothA2dpRoute : mDefaultAudioVideo; - } - - RouteInfo getCurrentSystemAudioRoute() { - return (isBluetoothA2dpOn() && mBluetoothA2dpRoute != null) - ? mBluetoothA2dpRoute : mDefaultAudioVideo; - } - boolean isBluetoothA2dpOn() { try { - return mAudioService.isBluetoothA2dpOn(); + return mBluetoothA2dpRoute != null && mAudioService.isBluetoothA2dpOn(); } catch (RemoteException e) { Log.e(TAG, "Error querying Bluetooth A2DP state", e); return false; @@ -608,6 +604,7 @@ public class MediaRouter { || mSelectedRoute == null) { return; } + Log.v(TAG, "onRestoreRoute() : a2dp=" + isBluetoothA2dpOn()); mSelectedRoute.select(); } } @@ -940,10 +937,12 @@ public class MediaRouter { Log.v(TAG, "Selecting route: " + route); assert(route != null); final RouteInfo oldRoute = sStatic.mSelectedRoute; + final RouteInfo currentSystemRoute = sStatic.isBluetoothA2dpOn() + ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo; boolean wasDefaultOrBluetoothRoute = (oldRoute == sStatic.mDefaultAudioVideo || oldRoute == sStatic.mBluetoothA2dpRoute); if (oldRoute == route - && (!wasDefaultOrBluetoothRoute || route == sStatic.getCurrentSystemAudioRoute())) { + && (!wasDefaultOrBluetoothRoute || route == currentSystemRoute)) { return; } if (!route.matchesTypes(types)) { @@ -1014,8 +1013,7 @@ public class MediaRouter { static void selectDefaultRouteStatic() { // TODO: Be smarter about the route types here; this selects for all valid. - if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute - && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) { + if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute && sStatic.isBluetoothA2dpOn()) { selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false); } else { selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false); diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 6e93e86992a9..bdd8927c51a0 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -331,7 +331,7 @@ <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ਤਬਦੀਲ ਕਰੋ ..."</string> <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ਫ਼ਾਈਲ ਪਹਿਲਾਂ ਤੋਂ ਇਨਕ੍ਰਿਪਟਡ ਹੈ"</string> <string name="title_convert_fbe" msgid="1263622876196444453">"ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> - <string name="convert_to_fbe_warning" msgid="6139067817148865527">" ਡਾਟਾ ਪਾਰਟੀਸ਼ਨ ਦਾ ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਰੁਪਾਂਤਰਣ ਕਰੋ\n !! ਚਿਤਾਵਨੀ !! ਇਹ ਤੁਹਾਡੇ ਸਾਰੇ ਡੈਟੇ ਨੂੰ ਮਿਟਾ ਦੇਵੇਗਾ\n ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਿਕ ਹੈ, ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।\n ਜਾਰੀ ਰੱਖਣ ਲਈ \'ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ...\' ਨੂੰ ਦਬਾਓ।"</string> + <string name="convert_to_fbe_warning" msgid="6139067817148865527">" ਡਾਟਾ ਪਾਰਟੀਸ਼ਨ ਦਾ ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਰੁਪਾਂਤਰਣ ਕਰੋ\n !! ਚਿਤਾਵਨੀ !! ਇਹ ਤੁਹਾਡੇ ਸਾਰੇ ਡਾਟੇ ਨੂੰ ਮਿਟਾ ਦੇਵੇਗਾ\n ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਿਕ ਹੈ, ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।\n ਜਾਰੀ ਰੱਖਣ ਲਈ \'ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ...\' ਨੂੰ ਦਬਾਓ।"</string> <string name="button_convert_fbe" msgid="5152671181309826405">"ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ..."</string> <string name="picture_color_mode" msgid="4560755008730283695">"ਤਸਵੀਰ ਰੰਗ ਮੋਡ"</string> <string name="picture_color_mode_desc" msgid="1141891467675548590">"sRGB ਵਰਤੋਂ ਕਰੋ"</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index d95402cdb5dc..cd23c97c71b6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1069,6 +1069,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { cb.onDreamingStateChanged(mIsDreaming); } } + updateFingerprintListeningState(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index b3f992db1b6b..bae9ef8abfba 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -30,6 +30,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.util.Log; +import android.util.Pair; import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import android.view.IWindowManager; @@ -70,11 +71,11 @@ public class PipManager implements BasePipManager { */ TaskStackListener mTaskStackListener = new TaskStackListener() { @Override - public void onActivityPinned(String packageName, int taskId) { + public void onActivityPinned(String packageName, int userId, int taskId) { mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mMenuController.onActivityPinned(); - mNotificationController.onActivityPinned(packageName, + mNotificationController.onActivityPinned(packageName, userId, true /* deferUntilAnimationEnds */); SystemServicesProxy.getInstance(mContext).setPipVisibility(true); @@ -82,13 +83,15 @@ public class PipManager implements BasePipManager { @Override public void onActivityUnpinned() { - ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext, - mActivityManager); - mMenuController.onActivityUnpinned(topPipActivity); - mTouchHandler.onActivityUnpinned(topPipActivity); - mNotificationController.onActivityUnpinned(topPipActivity); - - SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null); + final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity( + mContext, mActivityManager); + final ComponentName topActivity = topPipActivityInfo.first; + final int userId = topActivity != null ? topPipActivityInfo.second : 0; + mMenuController.onActivityUnpinned(); + mTouchHandler.onActivityUnpinned(topActivity); + mNotificationController.onActivityUnpinned(topActivity, userId); + + SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java index b3a0794f742f..174a7ef19cbd 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java @@ -230,7 +230,7 @@ public class PipMediaController { private void resolveActiveMediaController(List<MediaController> controllers) { if (controllers != null) { final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext, - mActivityManager); + mActivityManager).first; if (topActivity != null) { for (int i = 0; i < controllers.size(); i++) { final MediaController controller = controllers.get(i); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 34666fb30689..68743b34884d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -223,7 +223,7 @@ public class PipMenuActivityController { } } - public void onActivityUnpinned(ComponentName topPipActivity) { + public void onActivityUnpinned() { hideMenu(); setStartActivityRequested(false); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java index 696fdbc811e7..6d083e9d601d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java @@ -35,10 +35,15 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.graphics.drawable.Icon; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.UserHandle; +import android.util.IconDrawableFactory; import android.util.Log; +import android.util.Pair; import com.android.systemui.R; import com.android.systemui.SystemUI; @@ -57,22 +62,29 @@ public class PipNotificationController { private IActivityManager mActivityManager; private AppOpsManager mAppOpsManager; private NotificationManager mNotificationManager; + private IconDrawableFactory mIconDrawableFactory; private PipMotionHelper mMotionHelper; // Used when building a deferred notification private String mDeferredNotificationPackageName; + private int mDeferredNotificationUserId; private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() { @Override public void onOpChanged(String op, String packageName) { try { // Dismiss the PiP once the user disables the app ops setting for that package - final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo( - packageName, 0); - if (mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) - != MODE_ALLOWED) { - mMotionHelper.dismissPip(); + final Pair<ComponentName, Integer> topPipActivityInfo = + PipUtils.getTopPinnedActivity(mContext, mActivityManager); + if (topPipActivityInfo.first != null) { + final ApplicationInfo appInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second); + if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) && + mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, + packageName) != MODE_ALLOWED) { + mMotionHelper.dismissPip(); + } } } catch (NameNotFoundException e) { // Unregister the listener if the package can't be found @@ -88,16 +100,18 @@ public class PipNotificationController { mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mNotificationManager = NotificationManager.from(context); mMotionHelper = motionHelper; + mIconDrawableFactory = IconDrawableFactory.newInstance(context); } - public void onActivityPinned(String packageName, boolean deferUntilAnimationEnds) { + public void onActivityPinned(String packageName, int userId, boolean deferUntilAnimationEnds) { // Clear any existing notification mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); if (deferUntilAnimationEnds) { mDeferredNotificationPackageName = packageName; + mDeferredNotificationUserId = userId; } else { - showNotificationForApp(mDeferredNotificationPackageName); + showNotificationForApp(packageName, userId); } // Register for changes to the app ops setting for this package while it is in PiP @@ -106,22 +120,25 @@ public class PipNotificationController { public void onPinnedStackAnimationEnded() { if (mDeferredNotificationPackageName != null) { - showNotificationForApp(mDeferredNotificationPackageName); + showNotificationForApp(mDeferredNotificationPackageName, mDeferredNotificationUserId); mDeferredNotificationPackageName = null; + mDeferredNotificationUserId = 0; } } - public void onActivityUnpinned(ComponentName topPipActivity) { + public void onActivityUnpinned(ComponentName topPipActivity, int userId) { // Unregister for changes to the previously PiP'ed package unregisterAppOpsListener(); // Reset the deferred notification package mDeferredNotificationPackageName = null; + mDeferredNotificationUserId = 0; if (topPipActivity != null) { // onActivityUnpinned() is only called after the transition is complete, so we don't // need to defer until the animation ends to update the notification - onActivityPinned(topPipActivity.getPackageName(), false /* deferUntilAnimationEnds */); + onActivityPinned(topPipActivity.getPackageName(), userId, + false /* deferUntilAnimationEnds */); } else { mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); } @@ -130,20 +147,27 @@ public class PipNotificationController { /** * Builds and shows the notification for the given app. */ - private void showNotificationForApp(String packageName) { + private void showNotificationForApp(String packageName, int userId) { // Build a new notification - final Notification.Builder builder = - new Notification.Builder(mContext, NotificationChannels.GENERAL) - .setLocalOnly(true) - .setOngoing(true) - .setSmallIcon(R.drawable.pip_notification_icon) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)); - if (updateNotificationForApp(builder, packageName)) { - SystemUI.overrideNotificationAppName(mContext, builder); - - // Show the new notification - mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build()); + try { + final UserHandle user = UserHandle.of(userId); + final Context userContext = mContext.createPackageContextAsUser( + mContext.getPackageName(), 0, user); + final Notification.Builder builder = + new Notification.Builder(userContext, NotificationChannels.GENERAL) + .setLocalOnly(true) + .setOngoing(true) + .setSmallIcon(R.drawable.pip_notification_icon) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)); + if (updateNotificationForApp(builder, packageName, user)) { + SystemUI.overrideNotificationAppName(mContext, builder); + + // Show the new notification + mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build()); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not show notification for application", e); } } @@ -151,33 +175,33 @@ public class PipNotificationController { * Updates the notification builder with app-specific information, returning whether it was * successful. */ - private boolean updateNotificationForApp(Notification.Builder builder, String packageName) { + private boolean updateNotificationForApp(Notification.Builder builder, String packageName, + UserHandle user) throws NameNotFoundException { final PackageManager pm = mContext.getPackageManager(); final ApplicationInfo appInfo; try { - appInfo = pm.getApplicationInfo(packageName, 0); + appInfo = pm.getApplicationInfoAsUser(packageName, 0, user.getIdentifier()); } catch (NameNotFoundException e) { Log.e(TAG, "Could not update notification for application", e); return false; } if (appInfo != null) { - final String appName = pm.getApplicationLabel(appInfo).toString(); + final String appName = pm.getUserBadgedLabel(pm.getApplicationLabel(appInfo), user) + .toString(); final String message = mContext.getString(R.string.pip_notification_message, appName); final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, Uri.fromParts("package", packageName, null)); + settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user); settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - final Icon appIcon = appInfo.icon != 0 - ? Icon.createWithResource(packageName, appInfo.icon) - : Icon.createWithResource(Resources.getSystem(), - com.android.internal.R.drawable.sym_def_app_icon); + final Drawable iconDrawable = mIconDrawableFactory.getBadgedIcon(appInfo); builder.setContentTitle(mContext.getString(R.string.pip_notification_title, appName)) .setContentText(message) - .setContentIntent(PendingIntent.getActivity(mContext, packageName.hashCode(), - settingsIntent, FLAG_CANCEL_CURRENT)) + .setContentIntent(PendingIntent.getActivityAsUser(mContext, packageName.hashCode(), + settingsIntent, FLAG_CANCEL_CURRENT, null, user)) .setStyle(new Notification.BigTextStyle().bigText(message)) - .setLargeIcon(appIcon); + .setLargeIcon(createBitmap(iconDrawable).createAshmemBitmap()); return true; } return false; @@ -191,4 +215,17 @@ public class PipNotificationController { private void unregisterAppOpsListener() { mAppOpsManager.stopWatchingMode(mAppOpsChangedListener); } + + /** + * Bakes a drawable into a bitmap. + */ + private Bitmap createBitmap(Drawable d) { + Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(), + Config.ARGB_8888); + Canvas c = new Canvas(bitmap); + d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); + d.draw(c); + c.setBitmap(null); + return bitmap; + } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java index a8cdd1bdb802..56275fd043cf 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java @@ -24,16 +24,17 @@ import android.content.ComponentName; import android.content.Context; import android.os.RemoteException; import android.util.Log; +import android.util.Pair; public class PipUtils { private static final String TAG = "PipUtils"; /** - * @return the ComponentName of the top non-SystemUI activity in the pinned stack, or null if - * none exists. + * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack. + * The component name may be null if no such activity exists. */ - public static ComponentName getTopPinnedActivity(Context context, + public static Pair<ComponentName, Integer> getTopPinnedActivity(Context context, IActivityManager activityManager) { try { final String sysUiPackageName = context.getPackageName(); @@ -44,13 +45,13 @@ public class PipUtils { ComponentName cn = ComponentName.unflattenFromString( pinnedStackInfo.taskNames[i]); if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) { - return cn; + return new Pair<>(cn, pinnedStackInfo.taskUserIds[i]); } } } } catch (RemoteException e) { Log.w(TAG, "Unable to get pinned stack."); } - return null; + return new Pair<>(null, 0); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index e8c129521010..186de5c4e121 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -654,7 +654,7 @@ public class PipManager implements BasePipManager { } @Override - public void onActivityPinned(String packageName, int taskId) { + public void onActivityPinned(String packageName, int userId, int taskId) { if (DEBUG) Log.d(TAG, "onActivityPinned()"); if (!checkCurrentUserId(mContext, DEBUG)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index aecf95fc677f..5d5c4a0c69c6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -173,7 +173,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } @Override - public void onActivityPinned(String packageName, int taskId) { + public void onActivityPinned(String packageName, int userId, int taskId) { // Check this is for the right user if (!checkCurrentUserId(mContext, false /* debug */)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index c66b2dd8eec5..c9ef43e3f372 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -176,7 +176,7 @@ public class SystemServicesProxy { public void onTaskStackChangedBackground() { } public void onTaskStackChanged() { } public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { } - public void onActivityPinned(String packageName, int taskId) { } + public void onActivityPinned(String packageName, int userId, int taskId) { } public void onActivityUnpinned() { } public void onPinnedActivityRestartAttempt(boolean clearedTask) { } public void onPinnedStackAnimationStarted() { } @@ -231,9 +231,10 @@ public class SystemServicesProxy { } @Override - public void onActivityPinned(String packageName, int taskId) throws RemoteException { + public void onActivityPinned(String packageName, int userId, int taskId) + throws RemoteException { mHandler.removeMessages(H.ON_ACTIVITY_PINNED); - mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, taskId, 0, packageName).sendToTarget(); + mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, userId, taskId, packageName).sendToTarget(); } @Override @@ -1387,7 +1388,8 @@ public class SystemServicesProxy { } case ON_ACTIVITY_PINNED: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1); + mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1, + msg.arg2); } break; } diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index f15749f61047..54c9ec51a6d8 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4527,6 +4527,17 @@ message MetricsEvent { // Type TYPE_FAILURE: An invalid opperation was reported by the app's AutofillManager AUTOFILL_PENDING_SAVE_UI_OPERATION = 1134; + // Autofill service called API that disables itself + // Package: Package of the autofill service + // OS: O MR + AUTOFILL_SERVICE_DISABLED_SELF = 1135; + + // Counter showing how long it took (in ms) to show the autofill UI after a field was focused + // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request + // Package: Package of the autofill service + // OS: O MR + AUTOFILL_UI_LATENCY = 1136; + // ---- End O-MR1 Constants, all O-MR1 constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index a1c75bfc16c0..1f4161ac54d4 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -115,11 +115,24 @@ public final class AutofillManagerService extends SystemService { private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray(); private final LocalLog mRequestsHistory = new LocalLog(20); + private final LocalLog mUiLatencyHistory = new LocalLog(20); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { + if (sDebug) Slog.d(TAG, "Close system dialogs"); + + // TODO(b/64940307): we need to destroy all sessions that are finished but showing + // Save UI because there is no way to show the Save UI back when the activity + // beneath it is brought back to top. Ideally, we should just hide the UI and + // bring it back when the activity resumes. + synchronized (mLock) { + for (int i = 0; i < mServicesCache.size(); i++) { + mServicesCache.valueAt(i).destroyFinishedSessionsLocked(); + } + } + mUi.hideAll(null); } } @@ -294,7 +307,7 @@ public final class AutofillManagerService extends SystemService { AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId); if (service == null) { service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory, - resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId)); + mUiLatencyHistory, resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId)); mServicesCache.put(userId, service); } return service; @@ -724,6 +737,8 @@ public final class AutofillManagerService extends SystemService { if (showHistory) { pw.println("Requests history:"); mRequestsHistory.reverseDump(fd, pw, args); + pw.println("UI latency history:"); + mUiLatencyHistory.reverseDump(fd, pw, args); } } finally { setDebugLocked(oldDebug); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index e28e1c9e6a90..3a3b5707fc6f 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -35,6 +35,7 @@ import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.metrics.LogMaker; import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; @@ -63,6 +64,8 @@ import android.view.autofill.IAutoFillManagerClient; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.HandlerCaller; import com.android.server.autofill.ui.AutoFillUI; @@ -89,6 +92,7 @@ final class AutofillManagerServiceImpl { private final Context mContext; private final Object mLock; private final AutoFillUI mUi; + private final MetricsLogger mMetricsLogger = new MetricsLogger(); private RemoteCallbackList<IAutoFillManagerClient> mClients; private AutofillServiceInfo mInfo; @@ -96,6 +100,8 @@ final class AutofillManagerServiceImpl { private static final Random sRandom = new Random(); private final LocalLog mRequestsHistory; + private final LocalLog mUiLatencyHistory; + /** * Whether service was disabled for user due to {@link UserManager} restrictions. */ @@ -137,10 +143,11 @@ final class AutofillManagerServiceImpl { private long mLastPrune = 0; AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, - int userId, AutoFillUI ui, boolean disabled) { + LocalLog uiLatencyHistory, int userId, AutoFillUI ui, boolean disabled) { mContext = context; mLock = lock; mRequestsHistory = requestsHistory; + mUiLatencyHistory = uiLatencyHistory; mUserId = userId; mUi = ui; updateLocked(disabled); @@ -218,8 +225,10 @@ final class AutofillManagerServiceImpl { if (serviceInfo != null) { mInfo = new AutofillServiceInfo(mContext.getPackageManager(), serviceComponent, mUserId); + if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo); } else { mInfo = null; + if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId); } final boolean isEnabled = isEnabled(); if (wasEnabled != isEnabled) { @@ -345,17 +354,31 @@ final class AutofillManagerServiceImpl { } void disableOwnedAutofillServicesLocked(int uid) { - if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid != uid) { + Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo); + if (mInfo == null) return; + + final ServiceInfo serviceInfo = mInfo.getServiceInfo(); + if (serviceInfo.applicationInfo.uid != uid) { + Slog.w(TAG, "disableOwnedServices(): ignored when called by UID " + uid + + " instead of " + serviceInfo.applicationInfo.uid + + " for service " + mInfo); return; } + + final long identity = Binder.clearCallingIdentity(); try { final String autoFillService = getComponentNameFromSettings(); - if (mInfo.getServiceInfo().getComponentName().equals( - ComponentName.unflattenFromString(autoFillService))) { + final ComponentName componentName = serviceInfo.getComponentName(); + if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) { + mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF, + componentName.getPackageName()); Settings.Secure.putStringForUser(mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, null, mUserId); destroySessionsLocked(); + } else { + Slog.w(TAG, "disableOwnedServices(): ignored because current service (" + + serviceInfo + ") does not match Settings (" + autoFillService + ")"); } } finally { Binder.restoreCallingIdentity(identity); @@ -379,7 +402,7 @@ final class AutofillManagerServiceImpl { final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock, sessionId, uid, activityToken, appCallbackToken, hasCallback, - mInfo.getServiceInfo().getComponentName(), packageName); + mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), packageName); mSessions.put(newSession.id, newSession); return newSession; @@ -452,7 +475,7 @@ final class AutofillManagerServiceImpl { final int sessionCount = mSessions.size(); for (int i = sessionCount - 1; i >= 0; i--) { final Session session = mSessions.valueAt(i); - if (session.isSaveUiPendingForToken(token)) { + if (session.isSaveUiPendingForTokenLocked(token)) { session.onPendingSaveUi(operation, token); return; } @@ -641,6 +664,18 @@ final class AutofillManagerServiceImpl { } } + // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities + void destroyFinishedSessionsLocked() { + final int sessionCount = mSessions.size(); + for (int i = sessionCount - 1; i >= 0; i--) { + final Session session = mSessions.valueAt(i); + if (session.isSavingLocked()) { + if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id); + session.forceRemoveSelfLocked(); + } + } + } + void listSessionsLocked(ArrayList<String> output) { final int numSessions = mSessions.size(); for (int i = 0; i < numSessions; i++) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index ff6e94b2490d..905db6734a73 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -50,6 +50,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.RemoteException; +import android.os.SystemClock; import android.service.autofill.AutofillService; import android.service.autofill.Dataset; import android.service.autofill.FillContext; @@ -61,8 +62,10 @@ import android.service.autofill.SaveRequest; import android.service.autofill.ValueFinder; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.LocalLog; import android.util.Slog; import android.util.SparseArray; +import android.util.TimeUtils; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; @@ -177,6 +180,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private PendingUi mPendingSaveUi; /** + * When the session started (using elapsed time since boot). + */ + private final long mStartTime; + + /** + * When the UI was shown for the first time (using elapsed time since boot). + */ + @GuardedBy("mLock") + private long mUiShownTime; + + @GuardedBy("mLock") + private final LocalLog mUiLatencyHistory; + + /** * Receiver of assist data from the app's {@link Activity}. */ private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { @@ -397,10 +414,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui, @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId, @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken, - @NonNull IBinder client, boolean hasCallback, + @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, @NonNull ComponentName componentName, @NonNull String packageName) { id = sessionId; this.uid = uid; + mStartTime = SystemClock.elapsedRealtime(); mService = service; mLock = lock; mUi = ui; @@ -408,6 +426,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mRemoteFillService = new RemoteFillService(context, componentName, userId, this); mActivityToken = activityToken; mHasCallback = hasCallback; + mUiLatencyHistory = uiLatencyHistory; mPackageName = packageName; mClient = IAutoFillManagerClient.Stub.asInterface(client); @@ -1347,6 +1366,31 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getUiForShowing().showFillUi(filledId, response, filterText, mService.getServicePackageName(), mPackageName, this); + + synchronized (mLock) { + if (mUiShownTime == 0) { + // Log first time UI is shown. + mUiShownTime = SystemClock.elapsedRealtime(); + final long duration = mUiShownTime - mStartTime; + if (sDebug) { + final StringBuilder msg = new StringBuilder("1st UI for ") + .append(mActivityToken) + .append(" shown in "); + TimeUtils.formatDuration(duration, msg); + Slog.d(TAG, msg.toString()); + } + final StringBuilder historyLog = new StringBuilder("id=").append(id) + .append(" app=").append(mActivityToken) + .append(" svc=").append(mService.getServicePackageName()) + .append(" latency="); + TimeUtils.formatDuration(duration, historyLog); + mUiLatencyHistory.log(historyLog.toString()); + + final LogMaker metricsLog = newLogMaker(MetricsEvent.AUTOFILL_UI_LATENCY) + .setCounterValue((int) duration); + mMetricsLogger.write(metricsLog); + } + } } boolean isDestroyed() { @@ -1368,7 +1412,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mHasCallback) { mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinished); } else if (sessionFinished) { - mClient.setSessionFinished(); + mClient.setSessionFinished(AutofillManager.STATE_FINISHED); } } catch (RemoteException e) { Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e); @@ -1655,6 +1699,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(prefix); pw.print("uid: "); pw.println(uid); pw.print(prefix); pw.print("mPackagename: "); pw.println(mPackageName); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); + pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); + pw.print(prefix); pw.print("Time to show UI: "); + if (mUiShownTime == 0) { + pw.println("N/A"); + } else { + TimeUtils.formatDuration(mUiShownTime - mStartTime, pw); + pw.println(); + } pw.print(prefix); pw.print("mResponses: "); if (mResponses == null) { pw.println("null"); @@ -1780,18 +1832,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void forceRemoveSelfLocked() { if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi); + final boolean isPendingSaveUi = isSaveUiPendingLocked(); mPendingSaveUi = null; removeSelfLocked(); - - mHandlerCaller.getHandler().post(() -> { + mUi.destroyAll(mPendingSaveUi, this, false); + if (!isPendingSaveUi) { try { - mClient.setState(mService.isEnabled(), true, false); + mClient.setSessionFinished(AutofillManager.STATE_UNKNOWN); } catch (RemoteException e) { - Slog.w(TAG, "error updating client state: " + e); + Slog.e(TAG, "Error notifying client to finish session", e); } - }); - - mUi.destroyAll(mPendingSaveUi, this, false); + } } /** @@ -1814,7 +1865,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } - if (isSaveUiPending()) { + if (isSaveUiPendingLocked()) { Slog.i(TAG, "removeSelfLocked() ignored, waiting for pending save ui"); return; } @@ -1835,14 +1886,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * a specific {@code token} created by * {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}. */ - boolean isSaveUiPendingForToken(@NonNull IBinder token) { - return isSaveUiPending() && token.equals(mPendingSaveUi.getToken()); + boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) { + return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken()); } /** * Checks whether this session is hiding the Save UI to handle a custom description link. */ - private boolean isSaveUiPending() { + private boolean isSaveUiPendingLocked() { return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING; } diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 32f4d69fc3e3..cd9bdb7205b1 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -263,9 +263,7 @@ final class SaveUi { } else { noButton.setText(R.string.autofill_save_no); } - final View.OnClickListener cancelListener = - (v) -> mListener.onCancel(info.getNegativeActionListener()); - noButton.setOnClickListener(cancelListener); + noButton.setOnClickListener((v) -> mListener.onCancel(info.getNegativeActionListener())); final View yesButton = view.findViewById(R.id.autofill_save_yes); yesButton.setOnClickListener((v) -> mListener.onSave()); @@ -273,9 +271,6 @@ final class SaveUi { mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel); mDialog.setContentView(view); - // Dialog can be dismissed when touched outside. - mDialog.setOnDismissListener((d) -> mListener.onCancel(info.getNegativeActionListener())); - final Window window = mDialog.getWindow(); window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE @@ -305,7 +300,7 @@ final class SaveUi { if (actualWidth <= maxWidth && actualHeight <= maxHeight) { if (sDebug) { - Slog.d(TAG, "Addingservice icon " + Slog.d(TAG, "Adding service icon " + "(" + actualWidth + "x" + actualHeight + ") as it's less than maximum " + "(" + maxWidth + "x" + maxHeight + ")."); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e70a294d4684..2b4f0f35fefb 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2205,7 +2205,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // A network factory has connected. Send it all current NetworkRequests. for (NetworkRequestInfo nri : mNetworkRequests.values()) { if (nri.request.isListen()) continue; - NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); + NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId); ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, (nai != null ? nai.getCurrentScore() : 0), 0, nri.request); } @@ -2282,9 +2282,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // Remove all previously satisfied requests. for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest request = nai.requestAt(i); - NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId); + NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId); if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) { - mNetworkForRequestId.remove(request.requestId); + clearNetworkForRequest(request.requestId); sendUpdatedScoreToFactories(request, 0); } } @@ -2360,7 +2360,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } rematchAllNetworksAndRequests(null, 0); - if (nri.request.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) { + if (nri.request.isRequest() && getNetworkForRequest(nri.request.requestId) == null) { sendUpdatedScoreToFactories(nri.request, 0); } } @@ -2415,7 +2415,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 2. Unvalidated WiFi will not be reaped when validated cellular // is currently satisfying the request. This is desirable when // WiFi ends up validating and out scoring cellular. - mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < + getNetworkForRequest(nri.request.requestId).getCurrentScore() < nai.getCurrentScoreAsValidated())) { return false; } @@ -2442,7 +2442,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (mNetworkRequests.get(nri.request) == null) { return; } - if (mNetworkForRequestId.get(nri.request.requestId) != null) { + if (getNetworkForRequest(nri.request.requestId) != null) { return; } if (VDBG || (DBG && nri.request.isRequest())) { @@ -2482,7 +2482,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("RELEASE " + nri); if (nri.request.isRequest()) { boolean wasKept = false; - NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); + NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId); if (nai != null) { boolean wasBackgroundNetwork = nai.isBackgroundNetwork(); nai.removeRequest(nri.request.requestId); @@ -2499,7 +2499,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { wasKept = true; } - mNetworkForRequestId.remove(nri.request.requestId); + clearNetworkForRequest(nri.request.requestId); if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) { // Went from foreground to background. updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); @@ -4296,7 +4296,8 @@ public class ConnectivityService extends IConnectivityManager.Stub * and the are the highest scored network available. * the are keyed off the Requests requestId. */ - // TODO: Yikes, this is accessed on multiple threads: add synchronization. + // NOTE: Accessed on multiple threads, must be synchronized on itself. + @GuardedBy("mNetworkForRequestId") private final SparseArray<NetworkAgentInfo> mNetworkForRequestId = new SparseArray<NetworkAgentInfo>(); @@ -4326,8 +4327,26 @@ public class ConnectivityService extends IConnectivityManager.Stub // priority networks like Wi-Fi are active. private final NetworkRequest mDefaultMobileDataRequest; + private NetworkAgentInfo getNetworkForRequest(int requestId) { + synchronized (mNetworkForRequestId) { + return mNetworkForRequestId.get(requestId); + } + } + + private void clearNetworkForRequest(int requestId) { + synchronized (mNetworkForRequestId) { + mNetworkForRequestId.remove(requestId); + } + } + + private void setNetworkForRequest(int requestId, NetworkAgentInfo nai) { + synchronized (mNetworkForRequestId) { + mNetworkForRequestId.put(requestId, nai); + } + } + private NetworkAgentInfo getDefaultNetwork() { - return mNetworkForRequestId.get(mDefaultRequest.requestId); + return getNetworkForRequest(mDefaultRequest.requestId); } private boolean isDefaultNetwork(NetworkAgentInfo nai) { @@ -4886,7 +4905,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // requests or not, and doesn't affect the network's score. if (nri.request.isListen()) continue; - final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); + final NetworkAgentInfo currentNetwork = getNetworkForRequest(nri.request.requestId); final boolean satisfies = newNetwork.satisfies(nri.request); if (newNetwork == currentNetwork && satisfies) { if (VDBG) { @@ -4918,7 +4937,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (VDBG) log(" accepting network in place of null"); } newNetwork.unlingerRequest(nri.request); - mNetworkForRequestId.put(nri.request.requestId, newNetwork); + setNetworkForRequest(nri.request.requestId, newNetwork); if (!newNetwork.addRequest(nri.request)) { Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request); } @@ -4952,7 +4971,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } newNetwork.removeRequest(nri.request.requestId); if (currentNetwork == newNetwork) { - mNetworkForRequestId.remove(nri.request.requestId); + clearNetworkForRequest(nri.request.requestId); sendUpdatedScoreToFactories(nri.request, 0); } else { Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " + diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index ab6e6025e602..b2dc3e6a8b69 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3042,7 +3042,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); resumeFocusedStackTopActivityLocked(); - mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName, + mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName, r.userId, r.getTask().taskId); } diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java index 82971696d670..6a986bb8a684 100644 --- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java @@ -95,7 +95,7 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> { - l.onActivityPinned((String) m.obj, m.arg1); + l.onActivityPinned((String) m.obj, m.arg1, m.arg2); }; private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> { @@ -278,10 +278,10 @@ class TaskChangeNotificationController { } /** Notifies all listeners when an Activity is pinned. */ - void notifyActivityPinned(String packageName, int taskId) { + void notifyActivityPinned(String packageName, int userId, int taskId) { mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG); final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, - taskId, 0, packageName); + userId, taskId, packageName); forAllLocalListeners(mNotifyActivityPinned, msg); msg.sendToTarget(); } diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 3795b7f3091c..1cfd5f02e810 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -271,14 +271,6 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public boolean isGlobalBluetoothA2doOn() { - synchronized (mLock) { - return mGlobalBluetoothA2dpOn; - } - } - - // Binder call - @Override public void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan) { if (client == null) { @@ -383,7 +375,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub synchronized (mLock) { a2dpOn = mGlobalBluetoothA2dpOn; } - Slog.v(TAG, "restoreBluetoothA2dp( " + a2dpOn + ")"); + Slog.v(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")"); mAudioService.setBluetoothA2dpOn(a2dpOn); } catch (RemoteException e) { Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn."); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 92b360bdf8f7..08b96d104266 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -16,8 +16,10 @@ package com.android.server.notification; +import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -1143,6 +1145,12 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + NotificationRecord getNotificationRecord(String key) { + return mNotificationsByKey.get(key); + } + + + @VisibleForTesting void setSystemReady(boolean systemReady) { mSystemReady = systemReady; } @@ -1217,7 +1225,7 @@ public class NotificationManagerService extends SystemService { mUsageStats = usageStats; mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper()); mRankingHelper = new RankingHelper(getContext(), - getContext().getPackageManager(), + mPackageManagerClient, mRankingHandler, mUsageStats, extractorNames); @@ -1477,7 +1485,7 @@ public class NotificationManagerService extends SystemService { } } } - mRankingHelper.updateNotificationChannel(pkg, uid, channel); + mRankingHelper.updateNotificationChannel(pkg, uid, channel, true); if (!fromListener) { final NotificationChannel modifiedChannel = @@ -3485,6 +3493,21 @@ public class NotificationManagerService extends SystemService { user, null, System.currentTimeMillis()); final NotificationRecord r = new NotificationRecord(getContext(), n, channel); + if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0 + && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 + && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) { + // Increase the importance of foreground service notifications unless the user had an + // opinion otherwise + if (TextUtils.isEmpty(channelId) + || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { + r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service"); + } else { + channel.setImportance(IMPORTANCE_LOW); + mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false); + r.updateNotificationChannel(channel); + } + } + if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r, r.sbn.getOverrideGroupKey() != null)) { return; diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index 36da04dfc3c6..332ab6d7a2d7 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -38,7 +38,7 @@ public interface RankingConfig { int uid, boolean includeDeleted); void createNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromTargetApp); - void updateNotificationChannel(String pkg, int uid, NotificationChannel channel); + void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser); NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted); void deleteNotificationChannel(String pkg, int uid, String channelId); void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId); diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index f193a1953a3a..9db458452b9c 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -589,7 +589,8 @@ public class RankingHelper implements RankingConfig { } @Override - public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel) { + public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel, + boolean fromUser) { Preconditions.checkNotNull(updatedChannel); Preconditions.checkNotNull(updatedChannel.getId()); Record r = getOrCreateRecord(pkg, uid); @@ -603,7 +604,11 @@ public class RankingHelper implements RankingConfig { if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE); } - lockFieldsForUpdate(channel, updatedChannel); + updatedChannel.unlockFields(updatedChannel.getUserLockedFields()); + updatedChannel.lockFields(channel.getUserLockedFields()); + if (fromUser) { + lockFieldsForUpdate(channel, updatedChannel); + } r.channels.put(updatedChannel.getId(), updatedChannel); if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) { @@ -828,8 +833,6 @@ public class RankingHelper implements RankingConfig { @VisibleForTesting void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) { - update.unlockFields(update.getUserLockedFields()); - update.lockFields(original.getUserLockedFields()); if (original.canBypassDnd() != update.canBypassDnd()) { update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ea1c029eebcb..aae9170dcbe8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3312,6 +3312,24 @@ public class PackageManagerService extends IPackageManager.Stub removeCodePathLI(dstCodePath); return null; } + + // If we have a profile for a compressed APK, copy it to the reference location. + // Since the package is the stub one, remove the stub suffix to get the normal package and + // APK name. + File profileFile = new File(getPrebuildProfilePath(pkg).replace(STUB_SUFFIX, "")); + if (profileFile.exists()) { + try { + // We could also do this lazily before calling dexopt in + // PackageDexOptimizer to prevent this happening on first boot. The issue + // is that we don't have a good way to say "do this only once". + if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), + pkg.applicationInfo.uid, pkg.packageName)) { + Log.e(TAG, "decompressPackage failed to copy system profile!"); + } + } catch (Exception e) { + Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); + } + } return dstCodePath; } @@ -9723,7 +9741,7 @@ public class PackageManagerService extends IPackageManager.Stub * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog, - String compilerFilter, boolean bootComplete) { + final String compilerFilter, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; @@ -9734,6 +9752,8 @@ public class PackageManagerService extends IPackageManager.Stub for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; + boolean useProfileForDexopt = false; + if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. @@ -9747,11 +9767,28 @@ public class PackageManagerService extends IPackageManager.Stub if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName)) { Log.e(TAG, "Installer failed to copy system profile!"); + } else { + useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } + } else { + PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); + // Handle compressed APKs in this path. Only do this for stubs with profiles to + // minimize the number off apps being speed-profile compiled during first boot. + // The other paths will not change the filter. + if (disabledPs != null && disabledPs.pkg.isStub) { + // The package is the stub one, remove the stub suffix to get the normal + // package and APK names. + String systemProfilePath = + getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); + File systemProfile = new File(systemProfilePath); + // Use the profile for compilation if there exists one for the same package + // in the system partition. + useProfileForDexopt = systemProfile.exists(); + } } } @@ -9780,6 +9817,14 @@ public class PackageManagerService extends IPackageManager.Stub } } + String pkgCompilerFilter = compilerFilter; + if (useProfileForDexopt) { + // Use background dexopt mode to try and use the profile. Note that this does not + // guarantee usage of the profile. + pkgCompilerFilter = + PackageManagerServiceCompilerMapping.getCompilerFilterForReason( + PackageManagerService.REASON_BACKGROUND_DEXOPT); + } // If the OTA updates a system app which was previously preopted to a non-preopted state // the app might end up being verified at runtime. That's because by default the apps // are verify-profile but for preopted apps there's no profile. @@ -9788,9 +9833,9 @@ public class PackageManagerService extends IPackageManager.Stub // filter (by default 'quicken'). // Note that at this stage unused apps are already filtered. if (isSystemApp(pkg) && - DexFile.isProfileGuidedCompilerFilter(compilerFilter) && + DexFile.isProfileGuidedCompilerFilter(pkgCompilerFilter) && !Environment.getReferenceProfile(pkg.packageName).exists()) { - compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter); + pkgCompilerFilter = getNonProfileGuidedCompilerFilter(pkgCompilerFilter); } // checkProfiles is false to avoid merging profiles during boot which @@ -9801,7 +9846,7 @@ public class PackageManagerService extends IPackageManager.Stub int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, - compilerFilter, + pkgCompilerFilter, dexoptFlags)); switch (primaryDexOptStaus) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index bc531c3dee1b..f79f0e86b765 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2623,18 +2623,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; } - if (ActivityManager.isHighEndGfx()) { - if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { - attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - } - final boolean forceWindowDrawsStatusBarBackground = - (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) - != 0; - if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 - || forceWindowDrawsStatusBarBackground - && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) { - attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - } + if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { + attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + } + final boolean forceWindowDrawsStatusBarBackground = + (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; + if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 + || forceWindowDrawsStatusBarBackground + && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) { + attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index be44607658d0..f84b20c05705 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -120,7 +120,7 @@ public final class PowerManagerService extends SystemService implements Watchdog.Monitor { private static final String TAG = "PowerManagerService"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private static final boolean DEBUG_SPEW = DEBUG && true; // Message: Sent when a user activity timeout occurs to update the power state. diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java index 0f251fd83822..1ea70585aa5f 100644 --- a/services/core/java/com/android/server/utils/ManagedApplicationService.java +++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java @@ -44,6 +44,7 @@ public class ManagedApplicationService { private final int mClientLabel; private final String mSettingsAction; private final BinderChecker mChecker; + private final boolean mIsImportant; private final DeathRecipient mDeathRecipient = new DeathRecipient() { @Override @@ -64,13 +65,14 @@ public class ManagedApplicationService { private ManagedApplicationService(final Context context, final ComponentName component, final int userId, int clientLabel, String settingsAction, - BinderChecker binderChecker) { + BinderChecker binderChecker, boolean isImportant) { mContext = context; mComponent = component; mUserId = userId; mClientLabel = clientLabel; mSettingsAction = settingsAction; mChecker = binderChecker; + mIsImportant = isImportant; } /** @@ -99,13 +101,15 @@ public class ManagedApplicationService { * @param settingsAction an action that can be used to open the Settings UI to enable/disable * binding to these services. * @param binderChecker an interface used to validate the returned binder object. + * @param isImportant bind the user service with BIND_IMPORTANT. * @return a ManagedApplicationService instance. */ public static ManagedApplicationService build(@NonNull final Context context, @NonNull final ComponentName component, final int userId, @NonNull int clientLabel, - @NonNull String settingsAction, @NonNull BinderChecker binderChecker) { + @NonNull String settingsAction, @NonNull BinderChecker binderChecker, + boolean isImportant) { return new ManagedApplicationService(context, component, userId, clientLabel, - settingsAction, binderChecker); + settingsAction, binderChecker, isImportant); } /** @@ -248,9 +252,12 @@ public class ManagedApplicationService { mPendingConnection = serviceConnection; + int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; + if (mIsImportant) { + flags |= Context.BIND_IMPORTANT; + } try { - if (!mContext.bindServiceAsUser(intent, serviceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + if (!mContext.bindServiceAsUser(intent, serviceConnection, flags, new UserHandle(mUserId))) { Slog.w(TAG, "Unable to bind service: " + intent); } diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 1f0b2f000be1..830ebda0b9c1 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -1026,7 +1026,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC @NonNull ComponentName component, int userId) { return ManagedApplicationService.build(context, component, userId, R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS, - sBinderChecker); + sBinderChecker, /*isImportant*/true); } /** diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index 04b42f1ee312..7ea42daa617b 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -16,12 +16,15 @@ package com.android.server.notification; +import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_NONE; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; @@ -56,6 +59,7 @@ import android.content.pm.ParceledListSlice; import android.graphics.Color; import android.media.AudioManager; import android.os.Binder; +import android.os.Build; import android.os.Process; import android.os.UserHandle; import android.provider.Settings.Secure; @@ -241,6 +245,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { nb.build(), new UserHandle(mUid), null, 0); return new NotificationRecord(mContext, sbn, channel); } + private NotificationRecord generateNotificationRecord(NotificationChannel channel) { return generateNotificationRecord(channel, null); } @@ -342,7 +347,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { // Recreating the channel doesn't throw, but ignores importance. final NotificationChannel dupeChannel = - new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH); + new NotificationChannel("id", "name", IMPORTANCE_HIGH); mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(dupeChannel))); final NotificationChannel createdChannel = @@ -378,7 +383,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { // The user modifies importance directly, can no longer be changed by the app. final NotificationChannel updatedChannel = - new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH); + new NotificationChannel("id", "name", IMPORTANCE_HIGH); mBinderService.updateNotificationChannelForPackage(PKG, mUid, updatedChannel); // Recreating with a lower importance leaves channel unchanged. @@ -388,7 +393,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { new ParceledListSlice(Arrays.asList(dupeChannel))); final NotificationChannel createdChannel = mBinderService.getNotificationChannel(PKG, "id"); - assertEquals(NotificationManager.IMPORTANCE_HIGH, createdChannel.getImportance()); + assertEquals(IMPORTANCE_HIGH, createdChannel.getImportance()); } @Test @@ -397,7 +402,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { final NotificationChannel channel1 = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT); final NotificationChannel channel2 = - new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH); + new NotificationChannel("id", "name", IMPORTANCE_HIGH); mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(channel1, channel2))); final NotificationChannel createdChannel = @@ -410,7 +415,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(true); NotificationChannel channel = new NotificationChannel("id", "name", - NotificationManager.IMPORTANCE_HIGH); + IMPORTANCE_HIGH); NotificationRecord r = generateNotificationRecord(channel); assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats)); verify(mUsageStats, times(1)).registerSuspendedByAdmin(eq(r)); @@ -421,11 +426,68 @@ public class NotificationManagerServiceTest extends NotificationTestCase { when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); NotificationChannel channel = new NotificationChannel("id", "name", - NotificationManager.IMPORTANCE_HIGH); - channel.setImportance(IMPORTANCE_NONE); + NotificationManager.IMPORTANCE_NONE); NotificationRecord r = generateNotificationRecord(channel); assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats)); verify(mUsageStats, times(1)).registerBlocked(eq(r)); + + mBinderService.createNotificationChannels( + PKG, new ParceledListSlice(Arrays.asList(channel))); + final StatusBarNotification sbn = generateNotificationRecord(channel).sbn; + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + sbn.getId(), sbn.getNotification(), sbn.getUserId()); + waitForIdle(); + assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length); + } + + @Test + public void testEnqueuedBlockedNotifications_appBlockedChannelForegroundService() + throws Exception { + when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); + + NotificationChannel channel = new NotificationChannel("blocked", "name", + NotificationManager.IMPORTANCE_NONE); + mBinderService.createNotificationChannels( + PKG, new ParceledListSlice(Arrays.asList(channel))); + + final StatusBarNotification sbn = generateNotificationRecord(channel).sbn; + sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE; + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + sbn.getId(), sbn.getNotification(), sbn.getUserId()); + waitForIdle(); + assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length); + assertEquals(IMPORTANCE_LOW, + mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance()); + assertEquals(IMPORTANCE_LOW, + mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); + } + + @Test + public void testEnqueuedBlockedNotifications_userBlockedChannelForegroundService() + throws Exception { + when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); + + NotificationChannel channel = + new NotificationChannel("blockedbyuser", "name", IMPORTANCE_HIGH); + mBinderService.createNotificationChannels( + PKG, new ParceledListSlice(Arrays.asList(channel))); + + NotificationChannel update = + new NotificationChannel("blockedbyuser", "name", IMPORTANCE_NONE); + mBinderService.updateNotificationChannelForPackage(PKG, mUid, update); + waitForIdle(); + assertEquals(IMPORTANCE_NONE, + mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); + + final StatusBarNotification sbn = generateNotificationRecord(channel).sbn; + sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE; + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + sbn.getId(), sbn.getNotification(), sbn.getUserId()); + waitForIdle(); + assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length); + assertNull(mNotificationManagerService.getNotificationRecord(sbn.getKey())); + assertEquals(IMPORTANCE_NONE, + mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance()); } @Test @@ -442,6 +504,21 @@ public class NotificationManagerServiceTest extends NotificationTestCase { } @Test + public void testEnqueuedBlockedNotifications_blockedAppForegroundService() throws Exception { + when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); + + mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); + + final StatusBarNotification sbn = generateNotificationRecord(null).sbn; + sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE; + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + sbn.getId(), sbn.getNotification(), sbn.getUserId()); + waitForIdle(); + assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length); + assertNull(mNotificationManagerService.getNotificationRecord(sbn.getKey())); + } + + @Test public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception { mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0, generateNotificationRecord(null).getNotification(), 0); @@ -798,7 +875,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mNotificationManagerService.setRankingHelper(mRankingHelper); when(mRankingHelper.getNotificationChannel( anyString(), anyInt(), eq("foo"), anyBoolean())).thenReturn( - new NotificationChannel("foo", "foo", NotificationManager.IMPORTANCE_HIGH)); + new NotificationChannel("foo", "foo", IMPORTANCE_HIGH)); Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo"); mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0, @@ -927,7 +1004,8 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mBinderService.updateNotificationChannelFromPrivilegedListener( null, PKG, Process.myUserHandle(), mTestNotificationChannel); - verify(mRankingHelper, times(1)).updateNotificationChannel(anyString(), anyInt(), any()); + verify(mRankingHelper, times(1)).updateNotificationChannel( + anyString(), anyInt(), any(), anyBoolean()); verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG), eq(Process.myUserHandle()), eq(mTestNotificationChannel), @@ -948,7 +1026,8 @@ public class NotificationManagerServiceTest extends NotificationTestCase { // pass } - verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any()); + verify(mRankingHelper, never()).updateNotificationChannel( + anyString(), anyInt(), any(), anyBoolean()); verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG), eq(Process.myUserHandle()), eq(mTestNotificationChannel), @@ -974,7 +1053,8 @@ public class NotificationManagerServiceTest extends NotificationTestCase { // pass } - verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any()); + verify(mRankingHelper, never()).updateNotificationChannel( + anyString(), anyInt(), any(), anyBoolean()); verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG), eq(Process.myUserHandle()), eq(mTestNotificationChannel), @@ -1345,7 +1425,8 @@ public class NotificationManagerServiceTest extends NotificationTestCase { @Test public void testOnlyAutogroupIfGroupChanged_groupChanged_autogroups() throws Exception { - NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "group", false); + NotificationRecord r = + generateNotificationRecord(mTestNotificationChannel, 0, "group", false); mNotificationManagerService.addNotification(r); r = generateNotificationRecord(mTestNotificationChannel, 0, null, false); @@ -1425,12 +1506,16 @@ public class NotificationManagerServiceTest extends NotificationTestCase { // Same notifications are enqueued as posted, everything counts b/c id and tag don't match int userId = new UserHandle(mUid).getIdentifier(); - assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null)); - assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2")); - assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana")); + assertEquals(40, + mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null)); + assertEquals(40, + mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2")); + assertEquals(2, + mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana")); // exclude a known notification - it's excluded from only the posted list, not enqueued - assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag")); + assertEquals(39, + mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag")); } @Test @@ -1560,4 +1645,51 @@ public class NotificationManagerServiceTest extends NotificationTestCase { verify(mZenModeHelper, times(1)).updateDefaultZenRules(); } + + @Test + public void testBumpFGImportance_noChannelChangePreOApp() throws Exception { + String preOPkg = "preO"; + int preOUid = 145; + final ApplicationInfo legacy = new ApplicationInfo(); + legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; + when(mPackageManagerClient.getApplicationInfoAsUser(eq(preOPkg), anyInt(), anyInt())) + .thenReturn(legacy); + when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt())).thenReturn(preOUid); + getContext().setMockPackageManager(mPackageManagerClient); + + Notification.Builder nb = new Notification.Builder(mContext, + NotificationChannel.DEFAULT_CHANNEL_ID) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) + .setPriority(Notification.PRIORITY_MIN); + + StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid, + 0, nb.build(), new UserHandle(preOUid), null, 0); + + mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag", + sbn.getId(), sbn.getNotification(), sbn.getUserId()); + waitForIdle(); + assertEquals(IMPORTANCE_LOW, + mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance()); + + nb = new Notification.Builder(mContext) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) + .setPriority(Notification.PRIORITY_MIN); + + sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid, + 0, nb.build(), new UserHandle(preOUid), null, 0); + + mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag", + sbn.getId(), sbn.getNotification(), sbn.getUserId()); + waitForIdle(); + assertEquals(IMPORTANCE_LOW, + mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance()); + + NotificationChannel defaultChannel = mBinderService.getNotificationChannel( + preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID); + assertEquals(IMPORTANCE_UNSPECIFIED, defaultChannel.getImportance()); + } } diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java index 65bf33084bb4..61d999a80b33 100644 --- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java @@ -484,7 +484,7 @@ public class RankingHelperTest extends NotificationTestCase { final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false); defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mHelper.updateNotificationChannel(PKG, UID, defaultChannel); + mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false, NotificationChannel.DEFAULT_CHANNEL_ID); @@ -633,7 +633,7 @@ public class RankingHelperTest extends NotificationTestCase { channel2.setBypassDnd(false); channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); - mHelper.updateNotificationChannel(PKG, UID, channel2); + mHelper.updateNotificationChannel(PKG, UID, channel2, true); // all fields should be changed assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false)); @@ -657,7 +657,7 @@ public class RankingHelperTest extends NotificationTestCase { defaultChannel.setBypassDnd(true); defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); - mHelper.updateNotificationChannel(PKG, UID, defaultChannel); + mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true); // ensure app level fields are changed assertFalse(mHelper.canShowBadge(PKG, UID)); @@ -681,7 +681,7 @@ public class RankingHelperTest extends NotificationTestCase { channel.setBypassDnd(true); channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); - mHelper.updateNotificationChannel(PKG, UID, channel); + mHelper.updateNotificationChannel(PKG, UID, channel, true); // ensure app level fields are not changed assertTrue(mHelper.canShowBadge(PKG, UID)); @@ -772,14 +772,14 @@ public class RankingHelperTest extends NotificationTestCase { update1.setSound(new Uri.Builder().scheme("test").build(), new AudioAttributes.Builder().build()); update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); // should be ignored - mHelper.updateNotificationChannel(PKG, UID, update1); + mHelper.updateNotificationChannel(PKG, UID, update1, true); assertEquals(NotificationChannel.USER_LOCKED_SOUND, mHelper.getNotificationChannel(PKG, UID, update1.getId(), false) .getUserLockedFields()); NotificationChannel update2 = getChannel(); update2.enableVibration(true); - mHelper.updateNotificationChannel(PKG, UID, update2); + mHelper.updateNotificationChannel(PKG, UID, update2, true); assertEquals(NotificationChannel.USER_LOCKED_SOUND | NotificationChannel.USER_LOCKED_VIBRATION, mHelper.getNotificationChannel(PKG, UID, update2.getId(), false) @@ -792,14 +792,14 @@ public class RankingHelperTest extends NotificationTestCase { final NotificationChannel update1 = getChannel(); update1.setVibrationPattern(new long[]{7945, 46 ,246}); - mHelper.updateNotificationChannel(PKG, UID, update1); + mHelper.updateNotificationChannel(PKG, UID, update1, true); assertEquals(NotificationChannel.USER_LOCKED_VIBRATION, mHelper.getNotificationChannel(PKG, UID, update1.getId(), false) .getUserLockedFields()); final NotificationChannel update2 = getChannel(); update2.enableLights(true); - mHelper.updateNotificationChannel(PKG, UID, update2); + mHelper.updateNotificationChannel(PKG, UID, update2, true); assertEquals(NotificationChannel.USER_LOCKED_VIBRATION | NotificationChannel.USER_LOCKED_LIGHTS, mHelper.getNotificationChannel(PKG, UID, update2.getId(), false) @@ -812,14 +812,14 @@ public class RankingHelperTest extends NotificationTestCase { final NotificationChannel update1 = getChannel(); update1.setLightColor(Color.GREEN); - mHelper.updateNotificationChannel(PKG, UID, update1); + mHelper.updateNotificationChannel(PKG, UID, update1, true); assertEquals(NotificationChannel.USER_LOCKED_LIGHTS, mHelper.getNotificationChannel(PKG, UID, update1.getId(), false) .getUserLockedFields()); final NotificationChannel update2 = getChannel(); update2.setImportance(IMPORTANCE_DEFAULT); - mHelper.updateNotificationChannel(PKG, UID, update2); + mHelper.updateNotificationChannel(PKG, UID, update2, true); assertEquals(NotificationChannel.USER_LOCKED_LIGHTS | NotificationChannel.USER_LOCKED_IMPORTANCE, mHelper.getNotificationChannel(PKG, UID, update2.getId(), false) @@ -835,14 +835,14 @@ public class RankingHelperTest extends NotificationTestCase { final NotificationChannel update1 = getChannel(); update1.setBypassDnd(true); - mHelper.updateNotificationChannel(PKG, UID, update1); + mHelper.updateNotificationChannel(PKG, UID, update1, true); assertEquals(NotificationChannel.USER_LOCKED_PRIORITY, mHelper.getNotificationChannel(PKG, UID, update1.getId(), false) .getUserLockedFields()); final NotificationChannel update2 = getChannel(); update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET); - mHelper.updateNotificationChannel(PKG, UID, update2); + mHelper.updateNotificationChannel(PKG, UID, update2, true); assertEquals(NotificationChannel.USER_LOCKED_PRIORITY | NotificationChannel.USER_LOCKED_VISIBILITY, mHelper.getNotificationChannel(PKG, UID, update2.getId(), false) @@ -850,7 +850,7 @@ public class RankingHelperTest extends NotificationTestCase { final NotificationChannel update3 = getChannel(); update3.setShowBadge(false); - mHelper.updateNotificationChannel(PKG, UID, update3); + mHelper.updateNotificationChannel(PKG, UID, update3, true); assertEquals(NotificationChannel.USER_LOCKED_PRIORITY | NotificationChannel.USER_LOCKED_VISIBILITY | NotificationChannel.USER_LOCKED_SHOW_BADGE, @@ -1263,7 +1263,7 @@ public class RankingHelperTest extends NotificationTestCase { mHelper.getNotificationChannelGroups(PKG, UID, true).getList(); channel1.setImportance(IMPORTANCE_LOW); - mHelper.updateNotificationChannel(PKG, UID, channel1); + mHelper.updateNotificationChannel(PKG, UID, channel1, true); List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(PKG, UID, true).getList(); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 2c02869ed763..011d76b0a545 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -106,8 +106,6 @@ public class TelephonyManager { public static final String MODEM_ACTIVITY_RESULT_KEY = BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY; - private static ITelephonyRegistry sRegistry; - /** * The allowed states of Wi-Fi calling. * @@ -178,11 +176,6 @@ public class TelephonyManager { mContext = context; } mSubscriptionManager = SubscriptionManager.from(mContext); - - if (sRegistry == null) { - sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( - "telephony.registry")); - } } /** @hide */ @@ -3506,6 +3499,10 @@ public class TelephonyManager { return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE)); } + private ITelephonyRegistry getTelephonyRegistry() { + return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry")); + } + // // // PhoneStateListener @@ -3545,12 +3542,16 @@ public class TelephonyManager { if (listener.mSubId == null) { listener.mSubId = mSubId; } - sRegistry.listenForSubscriber(listener.mSubId, getOpPackageName(), - listener.callback, events, notifyNow); + + ITelephonyRegistry registry = getTelephonyRegistry(); + if (registry != null) { + registry.listenForSubscriber(listener.mSubId, getOpPackageName(), + listener.callback, events, notifyNow); + } else { + Rlog.w(TAG, "telephony registry not ready."); + } } catch (RemoteException ex) { // system process dead - } catch (NullPointerException ex) { - // system process dead } } |