diff options
author | Scott Lobdell <slobdell@google.com> | 2021-04-10 00:27:34 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-04-10 00:27:34 +0000 |
commit | 01da99e3e562d93c902419316b2f6aa464777e16 (patch) | |
tree | 2fe6d242f8218ab669e2a98f757a85005ff88088 /core/java | |
parent | 80bf29781b44a4c22692a3a1a1e60a371471c25a (diff) | |
parent | 6560a5b465795d6922ccd0c83693fa04be2910a6 (diff) |
Merge changes from topic "SP1A.210407.002" into s-keystone-qcom-dev
* changes:
Adapt to new ranking logic from upstream.
Revert "Initial data pipeline for Smartspace media recommendations data in media carousel."
Merge SP1A.210407.002
Diffstat (limited to 'core/java')
139 files changed, 3012 insertions, 1354 deletions
diff --git a/core/java/Android.bp b/core/java/Android.bp index 2fdf9c146ff6..919f1e2e85fb 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -10,6 +10,15 @@ package { } filegroup { + name: "framework-core-sources", + srcs: [ + "**/*.java", + "**/*.aidl", + ], + visibility: ["//frameworks/base"], +} + +filegroup { name: "IKeyAttestationApplicationIdProvider.aidl", srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"], } @@ -23,3 +32,98 @@ filegroup { name: "ITracingServiceProxy.aidl", srcs: ["android/tracing/ITracingServiceProxy.aidl"], } + +// These are subset of framework-core-sources that are needed by the +// android.test.mock library. The implementation of android.test.mock references +// private members of various components to allow mocking of classes that cannot +// be mocked without access to those internal implementation details. +filegroup { + name: "framework-core-sources-for-test-mock", + srcs: [ + "android/accounts/AccountManagerCallback.java", + "android/accounts/AccountManagerFuture.java", + "android/accounts/AccountManager.java", + "android/accounts/AccountsException.java", + "android/accounts/AuthenticatorException.java", + "android/accounts/OperationCanceledException.java", + "android/annotation/AnimatorRes.java", + "android/annotation/AnimRes.java", + "android/annotation/AnyRes.java", + "android/annotation/ArrayRes.java", + "android/annotation/AttrRes.java", + "android/annotation/BoolRes.java", + "android/annotation/BroadcastBehavior.java", + "android/annotation/CallbackExecutor.java", + "android/annotation/CallSuper.java", + "android/annotation/CheckResult.java", + "android/annotation/ColorInt.java", + "android/annotation/ColorRes.java", + "android/annotation/DimenRes.java", + "android/annotation/DrawableRes.java", + "android/annotation/FontRes.java", + "android/annotation/FractionRes.java", + "android/annotation/IntDef.java", + "android/annotation/IntegerRes.java", + "android/annotation/IntRange.java", + "android/annotation/LayoutRes.java", + "android/annotation/NonNull.java", + "android/annotation/Nullable.java", + "android/annotation/PluralsRes.java", + "android/annotation/RawRes.java", + "android/annotation/RequiresPermission.java", + "android/annotation/SdkConstant.java", + "android/annotation/Size.java", + "android/annotation/StringDef.java", + "android/annotation/StringRes.java", + "android/annotation/StyleableRes.java", + "android/annotation/StyleRes.java", + "android/annotation/SuppressLint.java", + "android/annotation/SystemApi.java", + "android/annotation/SystemService.java", + "android/annotation/TestApi.java", + "android/annotation/UserIdInt.java", + "android/annotation/XmlRes.java", + "android/app/Application.java", + "android/app/IApplicationThread.aidl", + "android/app/IServiceConnection.aidl", + "android/app/PackageDeleteObserver.java", + "android/content/ComponentCallbacks2.java", + "android/content/ComponentCallbacks.java", + "android/content/ContentInterface.java", + "android/content/ContentProvider.java", + "android/content/ContentProviderNative.java", + "android/content/ContentResolver.java", + "android/content/Context.java", + "android/content/ContextWrapper.java", + "android/content/DialogInterface.java", + "android/content/IContentProvider.java", + "android/content/Intent.java", + "android/content/IntentSender.java", + "android/content/OperationApplicationException.java", + "android/content/pm/ActivityInfo.java", + "android/content/pm/ApplicationInfo.java", + "android/content/pm/InstantAppInfo.java", + "android/content/pm/IPackageDataObserver.aidl", + "android/content/pm/KeySet.java", + "android/content/pm/PackageManager.java", + "android/content/pm/VerifierDeviceIdentity.java", + "android/content/res/Resources.java", + "android/database/CrossProcessCursor.java", + "android/database/CrossProcessCursorWrapper.java", + "android/database/Cursor.java", + "android/database/CursorWrapper.java", + "android/os/Binder.java", + "android/os/Bundle.java", + "android/os/IBinder.java", + "android/os/IInterface.java", + "android/os/Parcelable.java", + "android/os/ParcelFileDescriptor.java", + "android/os/RemoteException.java", + "android/os/storage/VolumeInfo.java", + "android/util/AndroidException.java", + "android/view/DisplayAdjustments.java", + "android/view/ViewDebug.java", + "com/android/internal/annotations/VisibleForTesting.java", + ], + visibility: ["//frameworks/base/test-mock"], +} diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index d5368213be08..483defab43c9 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -886,7 +886,7 @@ public abstract class AccessibilityService extends Service { * them, otherwise an empty list. */ public List<AccessibilityWindowInfo> getWindows() { - return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId); + return AccessibilityInteractionClient.getInstance(this).getWindows(mConnectionId); } /** @@ -914,7 +914,8 @@ public abstract class AccessibilityService extends Service { */ @NonNull public final SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() { - return AccessibilityInteractionClient.getInstance().getWindowsOnAllDisplays(mConnectionId); + return AccessibilityInteractionClient.getInstance(this).getWindowsOnAllDisplays( + mConnectionId); } /** @@ -940,7 +941,8 @@ public abstract class AccessibilityService extends Service { * @return The root node if this service can retrieve window content. */ public AccessibilityNodeInfo getRootInActiveWindow() { - return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId); + return AccessibilityInteractionClient.getInstance(this).getRootInActiveWindow( + mConnectionId); } /** @@ -949,7 +951,7 @@ public abstract class AccessibilityService extends Service { */ public final void disableSelf() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { connection.disableSelf(); @@ -970,7 +972,7 @@ public abstract class AccessibilityService extends Service { private void setDefaultTokenInternal(Context context, int displayId) { final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE); final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); IBinder token = null; if (connection != null) { synchronized (mLock) { @@ -1042,7 +1044,7 @@ public abstract class AccessibilityService extends Service { public final @NonNull FingerprintGestureController getFingerprintGestureController() { if (mFingerprintGestureController == null) { mFingerprintGestureController = new FingerprintGestureController( - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId)); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId)); } return mFingerprintGestureController; } @@ -1074,8 +1076,7 @@ public abstract class AccessibilityService extends Service { @Nullable GestureResultCallback callback, @Nullable Handler handler) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( - mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection == null) { return false; } @@ -1287,7 +1288,7 @@ public abstract class AccessibilityService extends Service { private void setMagnificationCallbackEnabled(boolean enabled) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1348,7 +1349,7 @@ public abstract class AccessibilityService extends Service { */ public float getScale() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1377,7 +1378,7 @@ public abstract class AccessibilityService extends Service { */ public float getCenterX() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1406,7 +1407,7 @@ public abstract class AccessibilityService extends Service { */ public float getCenterY() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1440,7 +1441,7 @@ public abstract class AccessibilityService extends Service { @NonNull public Region getMagnificationRegion() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1469,7 +1470,7 @@ public abstract class AccessibilityService extends Service { */ public boolean reset(boolean animate) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1497,7 +1498,7 @@ public abstract class AccessibilityService extends Service { */ public boolean setScale(float scale, boolean animate) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1529,7 +1530,7 @@ public abstract class AccessibilityService extends Service { */ public boolean setCenter(float centerX, float centerY, boolean animate) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1701,7 +1702,7 @@ public abstract class AccessibilityService extends Service { private void setSoftKeyboardCallbackEnabled(boolean enabled) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1760,7 +1761,7 @@ public abstract class AccessibilityService extends Service { @SoftKeyboardShowMode public int getShowMode() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1789,7 +1790,7 @@ public abstract class AccessibilityService extends Service { */ public boolean setShowMode(@SoftKeyboardShowMode int showMode) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1835,7 +1836,7 @@ public abstract class AccessibilityService extends Service { */ public boolean switchToInputMethod(@NonNull String imeId) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1888,7 +1889,7 @@ public abstract class AccessibilityService extends Service { displayId); if (controller == null) { controller = new AccessibilityButtonController( - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId)); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId)); mAccessibilityButtonControllers.put(displayId, controller); } return controller; @@ -1922,7 +1923,7 @@ public abstract class AccessibilityService extends Service { */ public final @NonNull List<AccessibilityAction> getSystemActions() { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { return connection.getSystemActions(); @@ -1955,7 +1956,7 @@ public abstract class AccessibilityService extends Service { */ public final boolean performGlobalAction(int action) { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { return connection.performGlobalAction(action); @@ -1996,7 +1997,7 @@ public abstract class AccessibilityService extends Service { * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY */ public AccessibilityNodeInfo findFocus(int focus) { - return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, + return AccessibilityInteractionClient.getInstance(this).findFocus(mConnectionId, AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); } @@ -2012,7 +2013,7 @@ public abstract class AccessibilityService extends Service { */ public final AccessibilityServiceInfo getServiceInfo() { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { return connection.getServiceInfo(); @@ -2044,12 +2045,12 @@ public abstract class AccessibilityService extends Service { */ private void sendServiceInfo() { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (mInfo != null && connection != null) { try { connection.setServiceInfo(mInfo); mInfo = null; - AccessibilityInteractionClient.getInstance().clearCache(); + AccessibilityInteractionClient.getInstance(this).clearCache(); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); re.rethrowFromSystemServer(); @@ -2096,8 +2097,7 @@ public abstract class AccessibilityService extends Service { Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( - mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection == null) { sendScreenshotFailure(ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, executor, callback); return; @@ -2136,7 +2136,7 @@ public abstract class AccessibilityService extends Service { */ public void setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color) { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { connection.setFocusAppearance(strokeWidth, color); @@ -2261,12 +2261,14 @@ public abstract class AccessibilityService extends Service { private final HandlerCaller mCaller; private final Callbacks mCallback; + private final Context mContext; private int mConnectionId = AccessibilityInteractionClient.NO_ID; public IAccessibilityServiceClientWrapper(Context context, Looper looper, Callbacks callback) { mCallback = callback; + mContext = context; mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); } @@ -2366,7 +2368,8 @@ public abstract class AccessibilityService extends Service { boolean serviceWantsEvent = message.arg1 != 0; if (event != null) { // Send the event to AccessibilityCache via AccessibilityInteractionClient - AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); + AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent( + event); if (serviceWantsEvent && (mConnectionId != AccessibilityInteractionClient.NO_ID)) { // Send the event to AccessibilityService @@ -2395,15 +2398,15 @@ public abstract class AccessibilityService extends Service { IBinder windowToken = (IBinder) args.arg2; args.recycle(); if (connection != null) { - AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, - connection); + AccessibilityInteractionClient.getInstance(mContext).addConnection( + mConnectionId, connection); mCallback.init(mConnectionId, windowToken); mCallback.onServiceConnected(); } else { - AccessibilityInteractionClient.getInstance().removeConnection( + AccessibilityInteractionClient.getInstance(mContext).removeConnection( mConnectionId); mConnectionId = AccessibilityInteractionClient.NO_ID; - AccessibilityInteractionClient.getInstance().clearCache(); + AccessibilityInteractionClient.getInstance(mContext).clearCache(); mCallback.init(AccessibilityInteractionClient.NO_ID, null); } return; @@ -2415,14 +2418,14 @@ public abstract class AccessibilityService extends Service { return; } case DO_CLEAR_ACCESSIBILITY_CACHE: { - AccessibilityInteractionClient.getInstance().clearCache(); + AccessibilityInteractionClient.getInstance(mContext).clearCache(); return; } case DO_ON_KEY_EVENT: { KeyEvent event = (KeyEvent) message.obj; try { IAccessibilityServiceConnection connection = AccessibilityInteractionClient - .getInstance().getConnection(mConnectionId); + .getInstance(mContext).getConnection(mConnectionId); if (connection != null) { final boolean result = mCallback.onKeyEvent(event); final int sequence = message.arg1; @@ -2637,7 +2640,7 @@ public abstract class AccessibilityService extends Service { public void setGestureDetectionPassthroughRegion(int displayId, @NonNull Region region) { Preconditions.checkNotNull(region, "region cannot be null"); final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { connection.setGestureDetectionPassthroughRegion(displayId, region); @@ -2663,7 +2666,7 @@ public abstract class AccessibilityService extends Service { public void setTouchExplorationPassthroughRegion(int displayId, @NonNull Region region) { Preconditions.checkNotNull(region, "region cannot be null"); final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { connection.setTouchExplorationPassthroughRegion(displayId, region); diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index ab21dc9f14ad..923b6f41414a 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -117,4 +117,7 @@ interface IAccessibilityServiceConnection { void setTouchExplorationPassthroughRegion(int displayId, in Region region); void setFocusAppearance(int strokeWidth, int color); + + oneway void logTrace(long timestamp, String where, String callingParams, int processId, + long threadId, int callingUid, in Bundle serializedCallingStackInBundle); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 0f38b5fdb5c3..f0d5a893bb96 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5254,32 +5254,17 @@ public class Activity extends ContextThemeWrapper return; } - List<String> filteredPermissions = null; - if (!getAttributionSource().getRenouncedPermissions().isEmpty()) { final int permissionCount = permissions.length; for (int i = 0; i < permissionCount; i++) { if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) { - if (filteredPermissions == null) { - filteredPermissions = new ArrayList<>(i); - for (int j = 0; j < i; j++) { - filteredPermissions.add(permissions[i]); - } - } - } else if (filteredPermissions != null) { - filteredPermissions.add(permissions[i]); + throw new IllegalArgumentException("Cannot request renounced permission: " + + permissions[i]); } } } - final Intent intent; - if (filteredPermissions == null) { - intent = getPackageManager().buildRequestPermissionsIntent(permissions); - } else { - intent = getPackageManager().buildRequestPermissionsIntent( - filteredPermissions.toArray(new String[0])); - } - + final Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions); startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null); mHasCurrentPermissionsRequest = true; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index df0fdfa00d06..2aa3e1ef2590 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4804,6 +4804,10 @@ public final class ActivityThread extends ClientTransactionHandler } if (r.isTopResumedActivity == onTop) { + if (!Build.IS_DEBUGGABLE) { + Slog.w(TAG, "Activity top position already set to onTop=" + onTop); + return; + } throw new IllegalStateException("Activity top position already set to onTop=" + onTop); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6ce0b2283603..436007cae502 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -33,6 +33,7 @@ import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -50,7 +51,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; -import android.content.AttributionSource; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; @@ -1223,8 +1223,11 @@ public class AppOpsManager { public static final int OP_MANAGE_MEDIA = AppProtoEnums.APP_OP_MANAGE_MEDIA; /** @hide */ + public static final int OP_UWB_RANGING = AppProtoEnums.APP_OP_UWB_RANGING; + + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 112; + public static final int _NUM_OP = 113; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1637,6 +1640,8 @@ public class AppOpsManager { * @hide */ public static final String OPSTR_MANAGE_MEDIA = "android:manage_media"; + /** @hide */ + public static final String OPSTR_UWB_RANGING = "android:uwb_ranging"; /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; @@ -1706,6 +1711,7 @@ public class AppOpsManager { // Nearby devices OP_BLUETOOTH_SCAN, OP_BLUETOOTH_CONNECT, + OP_UWB_RANGING, // APPOP PERMISSIONS OP_ACCESS_NOTIFICATIONS, @@ -1846,6 +1852,7 @@ public class AppOpsManager { OP_COARSE_LOCATION, // OP_COARSE_LOCATION_SOURCE OP_MANAGE_MEDIA, // MANAGE_MEDIA OP_BLUETOOTH_CONNECT, // OP_BLUETOOTH_CONNECT + OP_UWB_RANGING, // OP_UWB_RANGING }; /** @@ -1964,6 +1971,7 @@ public class AppOpsManager { OPSTR_COARSE_LOCATION_SOURCE, OPSTR_MANAGE_MEDIA, OPSTR_BLUETOOTH_CONNECT, + OPSTR_UWB_RANGING, }; /** @@ -2083,6 +2091,7 @@ public class AppOpsManager { "COARSE_LOCATION_SOURCE", "MANAGE_MEDIA", "BLUETOOTH_CONNECT", + "UWB_RANGING" }; /** @@ -2203,6 +2212,7 @@ public class AppOpsManager { null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE, Manifest.permission.MANAGE_MEDIA, Manifest.permission.BLUETOOTH_CONNECT, + Manifest.permission.UWB_RANGING, }; /** @@ -2323,6 +2333,7 @@ public class AppOpsManager { null, // ACCESS_COARSE_LOCATION_SOURCE null, // MANAGE_MEDIA null, // BLUETOOTH_CONNECT + null, // UWB_RANGING }; /** @@ -2442,6 +2453,7 @@ public class AppOpsManager { null, // ACCESS_COARSE_LOCATION_SOURCE null, // MANAGE_MEDIA null, // BLUETOOTH_CONNECT + null, // UWB_RANGING }; /** @@ -2560,6 +2572,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE AppOpsManager.MODE_DEFAULT, // MANAGE_MEDIA AppOpsManager.MODE_ALLOWED, // BLUETOOTH_CONNECT + AppOpsManager.MODE_ALLOWED, // UWB_RANGING }; /** @@ -2682,6 +2695,7 @@ public class AppOpsManager { false, // ACCESS_COARSE_LOCATION_SOURCE false, // MANAGE_MEDIA false, // BLUETOOTH_CONNECT + false, // UWB_RANGING }; /** @@ -8015,18 +8029,18 @@ public class AppOpsManager { } } - int mode = mService.noteOperation(op, uid, packageName, attributionTag, + SyncNotedAppOp syncOp = mService.noteOperation(op, uid, packageName, attributionTag, collectionMode == COLLECT_ASYNC, message, shouldCollectMessage); - if (mode == MODE_ALLOWED) { + if (syncOp.getOpMode()== MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { - collectNotedOpForSelf(op, attributionTag); + collectNotedOpForSelf(syncOp); } else if (collectionMode == COLLECT_SYNC) { - collectNotedOpSync(op, attributionTag); + collectNotedOpSync(syncOp); } } - return mode; + return syncOp.getOpMode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -8183,23 +8197,23 @@ public class AppOpsManager { } } - int mode = mService.noteProxyOperation(op, attributionSource, + SyncNotedAppOp syncOp = mService.noteProxyOperation(op, attributionSource, collectionMode == COLLECT_ASYNC, message, shouldCollectMessage, skipProxyOperation); - if (mode == MODE_ALLOWED) { + if (syncOp.getOpMode() == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { - collectNotedOpForSelf(op, attributionSource.getNextAttributionTag()); + collectNotedOpForSelf(syncOp); } else if (collectionMode == COLLECT_SYNC // Only collect app-ops when the proxy is trusted && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, myUid) == PackageManager.PERMISSION_GRANTED || Binder.getCallingUid() == attributionSource.getNextUid())) { - collectNotedOpSync(op, attributionSource.getNextAttributionTag()); + collectNotedOpSync(syncOp); } } - return mode; + return syncOp.getOpMode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -8496,19 +8510,19 @@ public class AppOpsManager { } } - int mode = mService.startOperation(getClientId(), op, uid, packageName, + SyncNotedAppOp syncOp = mService.startOperation(getClientId(), op, uid, packageName, attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message, shouldCollectMessage); - if (mode == MODE_ALLOWED) { + if (syncOp.getOpMode() == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { - collectNotedOpForSelf(op, attributionTag); + collectNotedOpForSelf(syncOp); } else if (collectionMode == COLLECT_SYNC) { - collectNotedOpSync(op, attributionTag); + collectNotedOpSync(syncOp); } } - return mode; + return syncOp.getOpMode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -8611,24 +8625,23 @@ public class AppOpsManager { } } - int mode = mService.startProxyOperation(getClientId(), op, + SyncNotedAppOp syncOp = mService.startProxyOperation(getClientId(), op, attributionSource, false, collectionMode == COLLECT_ASYNC, message, shouldCollectMessage, skipProxyOperation); - if (mode == MODE_ALLOWED) { + if (syncOp.getOpMode() == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { - collectNotedOpForSelf(op, - attributionSource.getNextAttributionTag()); + collectNotedOpForSelf(syncOp); } else if (collectionMode == COLLECT_SYNC // Only collect app-ops when the proxy is trusted && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, Process.myUid()) == PackageManager.PERMISSION_GRANTED || Binder.getCallingUid() == attributionSource.getNextUid())) { - collectNotedOpSync(op, attributionSource.getNextAttributionTag()); + collectNotedOpSync(syncOp); } } - return mode; + return syncOp.getOpMode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -8869,13 +8882,13 @@ public class AppOpsManager { * @param op The noted op * @param attributionTag The attribution tag the op is noted for */ - private void collectNotedOpForSelf(int op, @Nullable String attributionTag) { + private void collectNotedOpForSelf(SyncNotedAppOp syncOp) { synchronized (sLock) { if (sOnOpNotedCallback != null) { - sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, attributionTag)); + sOnOpNotedCallback.onSelfNoted(syncOp); } } - sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, attributionTag)); + sMessageCollector.onSelfNoted(syncOp); } /** @@ -8883,23 +8896,23 @@ public class AppOpsManager { * * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded} * - * @param op The noted op - * @param attributionTag The attribution tag the op is noted for + * @param syncOp the op and attribution tag to note for */ - private void collectNotedOpSync(int op, @Nullable String attributionTag) { + private void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) { // If this is inside of a two-way binder call: // We are inside of a two-way binder call. Delivered to caller via // {@link #prefixParcelWithAppOpsIfNeeded} + int op = sOpStrToOp.get(syncOp.getOp()); ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get(); if (appOpsNoted == null) { appOpsNoted = new ArrayMap<>(1); sAppOpsNotedInThisBinderTransaction.set(appOpsNoted); } - long[] appOpsNotedForAttribution = appOpsNoted.get(attributionTag); + long[] appOpsNotedForAttribution = appOpsNoted.get(syncOp.getAttributionTag()); if (appOpsNotedForAttribution == null) { appOpsNotedForAttribution = new long[2]; - appOpsNoted.put(attributionTag, appOpsNotedForAttribution); + appOpsNoted.put(syncOp.getAttributionTag(), appOpsNotedForAttribution); } if (op < 64) { diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index a3d0cf2e972f..6af4d9364ad2 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -76,11 +76,11 @@ public abstract class AppOpsManagerInternal { * @param superImpl The super implementation. * @return The app op note result. */ - int noteOperation(int code, int uid, @Nullable String packageName, + SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName, @Nullable String featureId, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean, - Integer> superImpl); + SyncNotedAppOp> superImpl); /** * Allows overriding note proxy operation behavior. @@ -94,11 +94,11 @@ public abstract class AppOpsManagerInternal { * @param superImpl The super implementation. * @return The app op note result. */ - int noteProxyOperation(int code, @NonNull AttributionSource attributionSource, + SyncNotedAppOp noteProxyOperation(int code, @NonNull AttributionSource attributionSource, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean, - Boolean, Integer> superImpl); + Boolean, SyncNotedAppOp> superImpl); /** * Allows overriding start proxy operation behavior. @@ -113,12 +113,12 @@ public abstract class AppOpsManagerInternal { * @param superImpl The super implementation. * @return The app op note result. */ - int startProxyOperation(IBinder token, int code, + SyncNotedAppOp startProxyOperation(IBinder token, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer, AttributionSource, Boolean, Boolean, String, Boolean, Boolean, - Integer> superImpl); + SyncNotedAppOp> superImpl); /** * Allows overriding finish proxy op. diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index eb31b5294a26..89312f4b3837 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -115,6 +115,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; class ReceiverRestrictedContext extends ContextWrapper { @@ -3074,14 +3075,16 @@ class ContextImpl extends Context { mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName; mParams = Objects.requireNonNull(params); - mAttributionSource = createAttributionSource(attributionTag, nextAttributionSource); + mAttributionSource = createAttributionSource(attributionTag, nextAttributionSource, + params.getRenouncedPermissions()); mContentResolver = new ApplicationContentResolver(this, mainThread); } private @NonNull AttributionSource createAttributionSource(@Nullable String attributionTag, - @Nullable AttributionSource nextAttributionSource) { - AttributionSource attributionSource = new AttributionSource(Process.myUid(), mOpPackageName, - attributionTag, nextAttributionSource); + @Nullable AttributionSource nextAttributionSource, + @Nullable Set<String> renouncedPermissions) { + AttributionSource attributionSource = new AttributionSource(Process.myUid(), + mOpPackageName, attributionTag, renouncedPermissions, nextAttributionSource); // If we want to access protected data on behalf of another app we need to // tell the OS that we opt in to participate in the attribution chain. if (nextAttributionSource != null) { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 81e5e1d96294..1b8eb8add791 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -571,6 +571,12 @@ interface IActivityManager { */ void stopDelegateShellPermissionIdentity(); + /** + * Method for the shell UID to get currently adopted permissions for an active instrumentation. + * An active instrumentation is one running and started from the shell. + */ + List<String> getDelegatedShellPermissions(); + /** Returns a file descriptor that'll be closed when the system server process dies. */ ParcelFileDescriptor getLifeMonitor(); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index a5f8f103e437..dab5aff5c9a8 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -85,6 +85,7 @@ interface INotificationManager void setBubblesAllowed(String pkg, int uid, int bubblePreference); boolean areBubblesAllowed(String pkg); + boolean areBubblesEnabled(in UserHandle user); int getBubblePreferenceForPackage(String pkg, int uid); void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList); @@ -227,6 +228,7 @@ interface INotificationManager NotificationListenerFilter getListenerFilter(in ComponentName cn, int userId); void setListenerFilter(in ComponentName cn, int userId, in NotificationListenerFilter nlf); + void migrateNotificationFilter(in INotificationListener token, int defaultTypes, in List<String> disallowedPkgs); void setToastRateLimitingEnabled(boolean enable); } diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index ec7d7832dc82..c30bc248344c 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -24,6 +24,8 @@ import android.view.WindowContentFrameStats; import android.view.WindowAnimationFrameStats; import android.os.ParcelFileDescriptor; +import java.util.List; + /** * This interface contains privileged operations a shell program can perform * on behalf of an instrumentation that it runs. These operations require @@ -54,4 +56,5 @@ interface IUiAutomationConnection { oneway void shutdown(); void executeShellCommandWithStderr(String command, in ParcelFileDescriptor sink, in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink); + List<String> getAdoptedShellPermissions(); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 2fbea28f34d5..3de78f6e3cab 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3744,10 +3744,8 @@ public class Notification implements Parcelable private int mTextColorsAreForBackground = COLOR_INVALID; private int mPrimaryTextColor = COLOR_INVALID; private int mSecondaryTextColor = COLOR_INVALID; - private boolean mRebuildStyledRemoteViews; private boolean mTintActionButtons; - private boolean mTintWithThemeAccent; private boolean mInNightMode; /** @@ -3783,7 +3781,6 @@ public class Notification implements Parcelable mContext = context; Resources res = mContext.getResources(); mTintActionButtons = res.getBoolean(R.bool.config_tintNotificationActionButtons); - mTintWithThemeAccent = res.getBoolean(R.bool.config_tintNotificationsWithTheme); if (res.getBoolean(R.bool.config_enableNightMode)) { Configuration currentConfig = res.getConfiguration(); @@ -5208,15 +5205,21 @@ public class Notification implements Parcelable || mSecondaryTextColor == COLOR_INVALID || mTextColorsAreForBackground != backgroundColor) { mTextColorsAreForBackground = backgroundColor; - mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext, + int defaultPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext, backgroundColor, mInNightMode); - mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext, + int defaultSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext, backgroundColor, mInNightMode); - if (backgroundColor != COLOR_DEFAULT && isColorized(p)) { + boolean colorized = backgroundColor != COLOR_DEFAULT; + if (colorized) { mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( - mPrimaryTextColor, backgroundColor, 4.5); + defaultPrimaryTextColor, backgroundColor, 4.5); mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast( - mSecondaryTextColor, backgroundColor, 4.5); + defaultSecondaryTextColor, backgroundColor, 4.5); + } else { + mPrimaryTextColor = obtainThemeColor(R.attr.textColorPrimary, + defaultPrimaryTextColor); + mSecondaryTextColor = obtainThemeColor(R.attr.textColorSecondary, + defaultSecondaryTextColor); } } } @@ -5243,11 +5246,9 @@ public class Notification implements Parcelable contentView.setProgressBar(R.id.progress, max, progress, ind); contentView.setProgressBackgroundTintList(R.id.progress, mContext.getColorStateList(R.color.notification_progress_background_color)); - if (mTintWithThemeAccent || getRawColor(p) != COLOR_DEFAULT) { - ColorStateList progressTint = ColorStateList.valueOf(getAccentColor(p)); - contentView.setProgressTintList(R.id.progress, progressTint); - contentView.setProgressIndeterminateTintList(R.id.progress, progressTint); - } + ColorStateList progressTint = ColorStateList.valueOf(getAccentColor(p)); + contentView.setProgressTintList(R.id.progress, progressTint); + contentView.setProgressIndeterminateTintList(R.id.progress, progressTint); return true; } else { contentView.setViewVisibility(R.id.progress, View.GONE); @@ -5357,8 +5358,8 @@ public class Notification implements Parcelable int pillColor = getProtectionColor(p); contentView.setInt(R.id.expand_button, "setDefaultTextColor", textColor); contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor); - // Use different highlighted colors except when low-priority mode prevents that - if (!p.mReduceHighlights) { + // Use different highlighted colors for conversations' unread count + if (p.mHighlightExpander) { pillColor = getAccentTertiaryColor(p); // TODO(b/183710694): The accent tertiary is currently too bright in dark mode, so // we need to pick a contrasting color. @@ -5810,8 +5811,7 @@ public class Notification implements Parcelable } private boolean useExistingRemoteView() { - return mStyle == null || (!mStyle.displayCustomViewInline() - && !mRebuildStyledRemoteViews); + return mStyle == null || !mStyle.displayCustomViewInline(); } /** @@ -5993,7 +5993,7 @@ public class Notification implements Parcelable .viewType(StandardTemplateParams.VIEW_TYPE_PUBLIC) .fillTextsFrom(this); if (isLowPriority) { - params.reduceHighlights(); + params.highlightExpander(false); } view = makeNotificationHeader(params); view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true); @@ -6016,7 +6016,7 @@ public class Notification implements Parcelable public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) { StandardTemplateParams p = mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED) - .reduceHighlights() + .highlightExpander(false) .fillTextsFrom(this); if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) { p.summaryText(createSummaryText()); @@ -6083,8 +6083,7 @@ public class Notification implements Parcelable background = outResultColor[0].getDefaultColor(); textColor = ContrastColorUtil.resolvePrimaryColor(mContext, background, mInNightMode); - } else if (mTintActionButtons && !mInNightMode - && getRawColor(p) != COLOR_DEFAULT && !isColorized(p)) { + } else if (mTintActionButtons && !mInNightMode && !isColorized(p)) { textColor = getAccentColor(p); } else { textColor = getPrimaryTextColor(p); @@ -6262,7 +6261,7 @@ public class Notification implements Parcelable * is the primary text color, otherwise it's the contrast-adjusted app-provided color. */ private @ColorInt int getSmallIconColor(StandardTemplateParams p) { - return isColorized(p) ? getPrimaryTextColor(p) : getContrastColor(p); + return getContrastColor(p); } /** @@ -6274,11 +6273,9 @@ public class Notification implements Parcelable if (isColorized(p)) { return getPrimaryTextColor(p); } - if (mTintWithThemeAccent) { - int color = obtainThemeColor(R.attr.colorAccent, COLOR_INVALID); - if (color != COLOR_INVALID) { - return color; - } + int color = obtainThemeColor(R.attr.colorAccent, COLOR_INVALID); + if (color != COLOR_INVALID) { + return color; } return getContrastColor(p); } @@ -6288,7 +6285,7 @@ public class Notification implements Parcelable * color when colorized, or when not using theme color tints. */ private @ColorInt int getProtectionColor(StandardTemplateParams p) { - if (mTintWithThemeAccent && !isColorized(p)) { + if (!isColorized(p)) { int color = obtainThemeColor(R.attr.colorBackgroundFloating, COLOR_INVALID); if (color != COLOR_INVALID) { return color; @@ -6307,12 +6304,10 @@ public class Notification implements Parcelable if (isColorized(p)) { return getPrimaryTextColor(p); } - if (mTintWithThemeAccent) { - int color = obtainThemeColor(com.android.internal.R.attr.colorAccentTertiary, - COLOR_INVALID); - if (color != COLOR_INVALID) { - return color; - } + int color = obtainThemeColor(com.android.internal.R.attr.colorAccentTertiary, + COLOR_INVALID); + if (color != COLOR_INVALID) { + return color; } return getContrastColor(p); } @@ -6342,6 +6337,9 @@ public class Notification implements Parcelable * Gets the contrast-adjusted version of the color provided by the app. */ private @ColorInt int getContrastColor(StandardTemplateParams p) { + if (isColorized(p)) { + return getPrimaryTextColor(p); + } int rawColor = getRawColor(p); if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) { return mCachedContrastColor; @@ -6352,9 +6350,10 @@ public class Notification implements Parcelable int background = getDefaultBackgroundColor(); if (rawColor == COLOR_DEFAULT) { ensureColors(p); - color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode); - if (mTintWithThemeAccent) { - color = obtainThemeColor(R.attr.colorAccent, color); + color = obtainThemeColor(R.attr.colorAccent, COLOR_INVALID); + if (color == COLOR_INVALID) { + color = ContrastColorUtil.resolveDefaultColor(mContext, background, + mInNightMode); } } else { color = ContrastColorUtil.resolveContrastColor(mContext, rawColor, @@ -6375,11 +6374,6 @@ public class Notification implements Parcelable * @param p the template params to inflate this with */ private @ColorInt int getRawColor(StandardTemplateParams p) { - // When notifications are theme-tinted, the raw color is only used for the icon, so go - // ahead and keep that color instead of changing the color for minimized notifs. - if (p.mReduceHighlights && !mTintWithThemeAccent) { - return COLOR_DEFAULT; - } return mN.color; } @@ -6486,6 +6480,7 @@ public class Notification implements Parcelable + " notification: " + mN.mShortcutId + " vs bubble: " + mN.mBubbleMetadata.getShortcutId()); } + validateColorizedHasColor(); // first, add any extras from the calling code if (mUserExtras != null) { @@ -6539,6 +6534,21 @@ public class Notification implements Parcelable return mN; } + // This code is executed on behalf of other apps' notifications, sometimes even by 3p apps, + // a use case that is not supported by the Compat Framework library. + @SuppressWarnings("AndroidFrameworkCompatChange") + private void validateColorizedHasColor() { + if (mN.color == COLOR_DEFAULT && mN.extras.getBoolean(EXTRA_COLORIZED)) { + if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) { + throw new IllegalArgumentException( + "Colorized notifications must set a color (other than COLOR_DEFAULT)."); + } else { + Log.w(TAG, "Colorized notifications must set a color (other than " + + "COLOR_DEFAULT). This is required for apps targeting S."); + } + } + } + /** * Returns the color for the given Theme.DeviceDefault.DayNight attribute, or * defValue if that could not be completed @@ -6551,13 +6561,9 @@ public class Notification implements Parcelable } theme = new ContextThemeWrapper(mContext, R.style.Theme_DeviceDefault_DayNight) .getTheme(); - TypedArray ta = theme.obtainStyledAttributes(new int[]{attrRes}); - if (ta == null) { - return defaultColor; + try (TypedArray ta = theme.obtainStyledAttributes(new int[]{attrRes})) { + return ta == null ? defaultColor : ta.getColor(0, defaultColor); } - int background = ta.getColor(0, defaultColor); - ta.recycle(); - return background; } /** @@ -6671,11 +6677,7 @@ public class Notification implements Parcelable * which must be resolved by the caller before being used. */ private @ColorInt int getUnresolvedBackgroundColor(StandardTemplateParams p) { - if (isColorized(p)) { - return getRawColor(p); - } else { - return COLOR_DEFAULT; - } + return isColorized(p) ? getRawColor(p) : COLOR_DEFAULT; } /** @@ -6700,18 +6702,6 @@ public class Notification implements Parcelable } /** - * Forces all styled remoteViews to be built from scratch and not use any cached - * RemoteViews. - * This is needed for legacy apps that are baking in their remoteviews into the - * notification. - * - * @hide - */ - public void setRebuildStyledRemoteViews(boolean rebuild) { - mRebuildStyledRemoteViews = rebuild; - } - - /** * Get the text that should be displayed in the statusBar when heads upped. This is * usually just the app name, but may be different depending on the style. * @@ -6878,7 +6868,7 @@ public class Notification implements Parcelable * @hide */ public boolean isColorized() { - return extras.getBoolean(EXTRA_COLORIZED) + return color != COLOR_DEFAULT && extras.getBoolean(EXTRA_COLORIZED) && (hasColorizedPermission() || isForegroundService()); } @@ -8303,6 +8293,7 @@ public class Notification implements Parcelable StandardTemplateParams p = mBuilder.mParams.reset() .viewType(isCollapsed ? StandardTemplateParams.VIEW_TYPE_NORMAL : StandardTemplateParams.VIEW_TYPE_BIG) + .highlightExpander(isConversationLayout) .hideProgress(true) .title(conversationTitle) .text(null) @@ -8389,27 +8380,6 @@ public class Notification implements Parcelable return true; } - private CharSequence createConversationTitleFromMessages() { - ArraySet<CharSequence> names = new ArraySet<>(); - for (int i = 0; i < mMessages.size(); i++) { - Message m = mMessages.get(i); - Person sender = m.getSenderPerson(); - if (sender != null) { - names.add(sender.getName()); - } - } - SpannableStringBuilder title = new SpannableStringBuilder(); - int size = names.size(); - for (int i = 0; i < size; i++) { - CharSequence name = names.valueAt(i); - if (!TextUtils.isEmpty(title)) { - title.append(", "); - } - title.append(BidiFormatter.getInstance().unicodeWrap(name)); - } - return title; - } - /** * @hide */ @@ -8423,11 +8393,6 @@ public class Notification implements Parcelable return remoteViews; } - private static TextAppearanceSpan makeFontColorSpan(int color) { - return new TextAppearanceSpan(null, 0, 0, - ColorStateList.valueOf(color), null); - } - public static final class Message { /** @hide */ public static final String KEY_TEXT = "text"; @@ -9096,10 +9061,8 @@ public class Notification implements Parcelable container.setDrawableTint(buttonId, false, tintColor, PorterDuff.Mode.SRC_ATOP); - final TypedArray typedArray = mBuilder.mContext.obtainStyledAttributes( - new int[]{ android.R.attr.colorControlHighlight }); - int rippleAlpha = Color.alpha(typedArray.getColor(0, 0)); - typedArray.recycle(); + int rippleAlpha = Color.alpha(mBuilder.obtainThemeColor( + android.R.attr.colorControlHighlight, COLOR_DEFAULT)); int rippleColor = Color.argb(rippleAlpha, Color.red(tintColor), Color.green(tintColor), Color.blue(tintColor)); container.setRippleDrawableColor(buttonId, ColorStateList.valueOf(rippleColor)); @@ -9186,9 +9149,12 @@ public class Notification implements Parcelable * </pre> */ public static class CallStyle extends Style { - private static final int CALL_TYPE_INCOMING = 1; - private static final int CALL_TYPE_ONGOING = 2; - private static final int CALL_TYPE_SCREENING = 3; + /** @hide */ + public static final int CALL_TYPE_INCOMING = 1; + /** @hide */ + public static final int CALL_TYPE_ONGOING = 2; + /** @hide */ + public static final int CALL_TYPE_SCREENING = 3; /** * This is a key used privately on the action.extras to give spacing priority @@ -9889,23 +9855,6 @@ public class Notification implements Parcelable // Comparison done for all custom RemoteViews, independent of style return false; } - - private RemoteViews buildIntoRemoteView(RemoteViews template, RemoteViews customContent, - boolean headerless) { - if (customContent != null) { - // Need to clone customContent before adding, because otherwise it can no longer be - // parceled independently of remoteViews. - customContent = customContent.clone(); - customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams)); - if (headerless) { - template.removeFromParent(R.id.notification_top_line); - } - template.removeAllViews(R.id.notification_main_column); - template.addView(R.id.notification_main_column, customContent); - template.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED); - } - return template; - } } /** @@ -12230,7 +12179,7 @@ public class Notification implements Parcelable int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; boolean hideLargeIcon; boolean allowColorization = true; - boolean mReduceHighlights = false; + boolean mHighlightExpander = false; final StandardTemplateParams reset() { mViewType = VIEW_TYPE_UNSPECIFIED; @@ -12254,7 +12203,7 @@ public class Notification implements Parcelable maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; hideLargeIcon = false; allowColorization = true; - mReduceHighlights = false; + mHighlightExpander = false; return this; } @@ -12362,8 +12311,8 @@ public class Notification implements Parcelable return this; } - final StandardTemplateParams reduceHighlights() { - this.mReduceHighlights = true; + final StandardTemplateParams highlightExpander(boolean highlight) { + this.mHighlightExpander = highlight; return this; } diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java index 0c8188b9a51e..eb9ec869af12 100644 --- a/core/java/android/app/NotificationHistory.java +++ b/core/java/android/app/NotificationHistory.java @@ -404,6 +404,26 @@ public final class NotificationHistory implements Parcelable { } /** + * Removes all notifications from a channel and regenerates the string pool + */ + public boolean removeChannelFromWrite(String packageName, String channelId) { + boolean removed = false; + for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) { + HistoricalNotification hn = mNotificationsToWrite.get(i); + if (packageName.equals(hn.getPackage()) + && Objects.equals(channelId, hn.getChannelId())) { + removed = true; + mNotificationsToWrite.remove(i); + } + } + if (removed) { + poolStringsFromNotifications(); + } + + return removed; + } + + /** * Gets pooled strings in order to write them to disk */ public @NonNull String[] getPooledStringsToWrite() { diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 6cce270bc5a3..f0d580f066d8 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1326,7 +1326,6 @@ public class NotificationManager { } } - /** * Gets whether all notifications posted by this app can appear outside of the * notification shade, floating over other apps' content. @@ -1348,6 +1347,21 @@ public class NotificationManager { } /** + * Returns whether bubbles are enabled at the feature level for the current user. When enabled, + * notifications able to bubble will display an affordance allowing the user to bubble them. + * + * @see Notification.Builder#setBubbleMetadata(Notification.BubbleMetadata) + */ + public boolean areBubblesEnabled() { + INotificationManager service = getService(); + try { + return service.areBubblesEnabled(mContext.getUser()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Gets the bubble preference for the app. This preference only applies to notifications that * have been properly configured to bubble. * diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index f4b95420154b..4cf3a8059b3e 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -379,8 +379,7 @@ public final class PendingIntent implements Parcelable { if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED) && !flagImmutableSet && !flagMutableSet) { - //TODO(b/178065720) Remove check for chrome and enforce this requirement - if (packageName.equals("com.android.chrome") || mInstrumentation.isInstrumenting()) { + if (mInstrumentation.isInstrumenting()) { Log.e(TAG, msg); } else { throw new IllegalArgumentException(msg); diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java index 0a880dc0cbb7..bc4e5436996d 100644 --- a/core/java/android/app/SyncNotedAppOp.java +++ b/core/java/android/app/SyncNotedAppOp.java @@ -34,19 +34,37 @@ import com.android.internal.util.DataClass; * itself}. */ @Immutable -/*@DataClass( +@DataClass( genEqualsHashCode = true, + genAidl = true, genConstructor = false ) -@DataClass.Suppress("getOpCode")*/ +@DataClass.Suppress({"getOpCode", "getOpMode"}) public final class SyncNotedAppOp implements Parcelable { + /** mode returned by the system on a call to note/startOp, if applicable */ + private final int mOpMode; /** op code of synchronous appop noted */ private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode; /** attributionTag of synchronous appop noted */ private final @Nullable String mAttributionTag; /** + * Native code relies on parcel ordering, do not change + * @hide + */ + public SyncNotedAppOp(int opMode, @IntRange(from = 0L) int opCode, + @Nullable String attributionTag) { + this.mOpCode = opCode; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mOpCode, + "from", 0, + "to", AppOpsManager._NUM_OP - 1); + this.mAttributionTag = attributionTag; + this.mOpMode = opMode; + } + + /** * Creates a new SyncNotedAppOp. * * @param opCode @@ -55,12 +73,7 @@ public final class SyncNotedAppOp implements Parcelable { * attributionTag of synchronous appop noted */ public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag) { - this.mOpCode = opCode; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mOpCode, - "from", 0, - "to", AppOpsManager._NUM_OP - 1); - this.mAttributionTag = attributionTag; + this(AppOpsManager.MODE_IGNORED, opCode, attributionTag); } /** @@ -70,7 +83,16 @@ public final class SyncNotedAppOp implements Parcelable { return AppOpsManager.opToPublicName(mOpCode); } - // Code below generated by codegen v1.0.14. + /** + * @hide + */ + public int getOpMode() { + return mOpMode; + } + + + + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -104,6 +126,7 @@ public final class SyncNotedAppOp implements Parcelable { SyncNotedAppOp that = (SyncNotedAppOp) o; //noinspection PointlessBooleanExpression return true + && mOpMode == that.mOpMode && mOpCode == that.mOpCode && java.util.Objects.equals(mAttributionTag, that.mAttributionTag); } @@ -115,6 +138,7 @@ public final class SyncNotedAppOp implements Parcelable { // int fieldNameHashCode() { ... } int _hash = 1; + _hash = 31 * _hash + mOpMode; _hash = 31 * _hash + mOpCode; _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag); return _hash; @@ -127,8 +151,9 @@ public final class SyncNotedAppOp implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mAttributionTag != null) flg |= 0x2; + if (mAttributionTag != null) flg |= 0x4; dest.writeByte(flg); + dest.writeInt(mOpMode); dest.writeInt(mOpCode); if (mAttributionTag != null) dest.writeString(mAttributionTag); } @@ -145,13 +170,15 @@ public final class SyncNotedAppOp implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } byte flg = in.readByte(); + int opMode = in.readInt(); int opCode = in.readInt(); - String attributionTag = (flg & 0x2) == 0 ? null : in.readString(); + String attributionTag = (flg & 0x4) == 0 ? null : in.readString(); + this.mOpMode = opMode; this.mOpCode = opCode; com.android.internal.util.AnnotationValidations.validate( IntRange.class, null, mOpCode, - "from", 0, + "from", 0L, "to", AppOpsManager._NUM_OP - 1); this.mAttributionTag = attributionTag; @@ -172,11 +199,11 @@ public final class SyncNotedAppOp implements Parcelable { } }; - /*@DataClass.Generated( - time = 1579188889960L, - codegenVersion = "1.0.14", + @DataClass.Generated( + time = 1617317997768L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java", - inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/ + inputSignatures = "private final int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\npublic @android.annotation.NonNull java.lang.String getOp()\npublic int getOpMode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 31b0d413c395..47a9fbb5fe4a 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -722,7 +722,7 @@ public final class SystemServiceRegistry { new CachedServiceFetcher<UwbManager>() { @Override public UwbManager createService(ContextImpl ctx) { - return UwbManager.getInstance(); + return UwbManager.getInstance(ctx); } }); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 05872bad30f0..7d3db5e2cc49 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -43,6 +43,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -66,7 +67,9 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeoutException; /** @@ -160,6 +163,16 @@ public final class UiAutomation { */ public static final int FLAG_DONT_USE_ACCESSIBILITY = 0x00000002; + /** + * Returned by {@link #getAdoptedShellPermissions} to indicate that all permissions have been + * adopted using {@link #adoptShellPermissionIdentity}. + * + * @hide + */ + @TestApi + @NonNull + public static final Set<String> ALL_PERMISSIONS = Set.of("_ALL_PERMISSIONS_"); + private final Object mLock = new Object(); private final ArrayList<AccessibilityEvent> mEventQueue = new ArrayList<AccessibilityEvent>(); @@ -499,6 +512,25 @@ public final class UiAutomation { } /** + * Returns a list of adopted shell permissions using {@link #adoptShellPermissionIdentity}, + * returns and empty set if no permissions are adopted and {@link #ALL_PERMISSIONS} if all + * permissions are adopted. + * + * @hide + */ + @TestApi + @NonNull + public Set<String> getAdoptedShellPermissions() { + try { + final List<String> permissions = mUiAutomationConnection.getAdoptedShellPermissions(); + return permissions == null ? ALL_PERMISSIONS : new ArraySet<>(permissions); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error getting adopted shell permissions", re); + return Collections.emptySet(); + } + } + + /** * Performs a global action. Such an action can be performed at any moment * regardless of the current application or user location in that application. * For example going back, going home, opening recents, etc. diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 7036b6e7dbc9..90210a9a2756 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -49,6 +49,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; /** * This is a remote object that is passed from the shell to an instrumentation @@ -340,6 +341,22 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } } + @Override + @Nullable + public List<String> getAdoptedShellPermissions() throws RemoteException { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } + final long identity = Binder.clearCallingIdentity(); + try { + return mActivityManager.getDelegatedShellPermissions(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + public class Repeater implements Runnable { // Continuously read readFrom and write back to writeTo until EOF is encountered private final InputStream readFrom; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 89d08ccefb79..843aa2ecb492 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -306,10 +306,12 @@ public class DevicePolicyManager { * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies * that the user backed-out of provisioning, or some precondition for provisioning wasn't met. * - * @deprecated admin apps must now implement activities with intent filters for the {@link - * #ACTION_GET_PROVISIONING_MODE} and {@link #ACTION_ADMIN_POLICY_COMPLIANCE} intent actions; - * using {@link #ACTION_PROVISION_MANAGED_DEVICE} to start provisioning will cause the - * provisioning to fail. + * @deprecated to support {@link android.os.Build.VERSION_CODES#S} and later, admin apps must + * implement activities with intent filters for the {@link #ACTION_GET_PROVISIONING_MODE} and + * {@link #ACTION_ADMIN_POLICY_COMPLIANCE} intent actions; using {@link + * #ACTION_PROVISION_MANAGED_DEVICE} to start provisioning will cause the provisioning to fail; + * to additionally support pre-{@link android.os.Build.VERSION_CODES#S}, admin apps must also + * continue to use this constant. */ @Deprecated @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -1275,6 +1277,13 @@ public class DevicePolicyManager { /** * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning * trigger is persistent device owner enrollment. + * <p>This constant is meant to represent a specific type of managed account provisioning which + * provisions a device to a device owner by invoking the standard provisioning flow (where + * the ManagedProvisioning component downloads and installs the admin app), as opposed to + * relying on the provisioning trigger to handle download and install of the admin app. + * <p>As of {@link android.os.Build.VERSION_CODES#S}, this constant is no longer used in favor + * of the more general {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} which handles all managed + * account provisioning types. * @deprecated Use the broader {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} instead * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT * @see #PROVISIONING_TRIGGER_QR_CODE @@ -1288,6 +1297,7 @@ public class DevicePolicyManager { /** * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning * trigger is managed account enrollment. + * <p> * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT * @see #PROVISIONING_TRIGGER_QR_CODE * @see #PROVISIONING_TRIGGER_UNSPECIFIED @@ -1764,19 +1774,6 @@ public class DevicePolicyManager { = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED"; /** - * Broadcast action to notify ManagedProvisioning that - * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed. - * @hide - * @deprecated No longer needed as ManagedProvisioning no longer handles - * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing. - */ - // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it. - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @Deprecated - public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED = - "android.app.action.DATA_SHARING_RESTRICTION_CHANGED"; - - /** * Broadcast action from ManagedProvisioning to notify that the latest change to * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has been successfully * applied (cross profile intent filters updated). Only usesd for CTS tests. @@ -2668,8 +2665,8 @@ public class DevicePolicyManager { * A boolean extra which determines whether to skip the ownership disclaimer screen during the * provisioning flow. The default value is {@code false}. * - * If the value is {@code true}, it is the responsibility of the provisioning initiator to - * show the relevant disclaimer. + * If the value is {@code true}, the provisioning initiator must display a device ownership + * disclaimer screen similar to that provided in AOSP. * * <p>This extra is only respected when provided alongside the {@link * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action. @@ -2704,7 +2701,13 @@ public class DevicePolicyManager { public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; /** - * The provisioning mode for a work profile on a personal device. + * The provisioning mode for a managed profile on a personal device. + * <p>This mode is only available when the provisioning initiator has explicitly instructed the + * provisioning flow to support managed profile on a personal device provisioning. In that case, + * {@link #PROVISIONING_MODE_MANAGED_PROFILE} corresponds to an organization-owned managed + * profile, whereas this constant corresponds to a personally-owned managed profile. + * + * @see #EXTRA_PROVISIONING_MODE */ public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3; diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 2b52875f4b0b..fc4a2b49d0c2 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -717,7 +717,7 @@ public class AssistStructure implements Parcelable { String mWebDomain; Bundle mExtras; LocaleList mLocaleList; - String[] mOnReceiveContentMimeTypes; + String[] mReceiveContentMimeTypes; ViewNode[] mChildren; @@ -884,7 +884,7 @@ public class AssistStructure implements Parcelable { mLocaleList = in.readParcelable(null); } if ((flags & FLAGS_HAS_MIME_TYPES) != 0) { - mOnReceiveContentMimeTypes = in.readStringArray(); + mReceiveContentMimeTypes = in.readStringArray(); } if ((flags&FLAGS_HAS_EXTRAS) != 0) { mExtras = in.readBundle(); @@ -945,7 +945,7 @@ public class AssistStructure implements Parcelable { if (mLocaleList != null) { flags |= FLAGS_HAS_LOCALE_LIST; } - if (mOnReceiveContentMimeTypes != null) { + if (mReceiveContentMimeTypes != null) { flags |= FLAGS_HAS_MIME_TYPES; } if (mExtras != null) { @@ -1119,7 +1119,7 @@ public class AssistStructure implements Parcelable { out.writeParcelable(mLocaleList, 0); } if ((flags & FLAGS_HAS_MIME_TYPES) != 0) { - out.writeStringArray(mOnReceiveContentMimeTypes); + out.writeStringArray(mReceiveContentMimeTypes); } if ((flags&FLAGS_HAS_EXTRAS) != 0) { out.writeBundle(mExtras); @@ -1540,12 +1540,12 @@ public class AssistStructure implements Parcelable { /** * Returns the MIME types accepted by {@link View#performReceiveContent} for this view. See - * {@link View#getOnReceiveContentMimeTypes()} for details. + * {@link View#getReceiveContentMimeTypes()} for details. */ @Nullable @SuppressLint("NullableCollection") - public String[] getOnReceiveContentMimeTypes() { - return mOnReceiveContentMimeTypes; + public String[] getReceiveContentMimeTypes() { + return mReceiveContentMimeTypes; } /** @@ -2168,8 +2168,8 @@ public class AssistStructure implements Parcelable { } @Override - public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) { - mNode.mOnReceiveContentMimeTypes = mimeTypes; + public void setReceiveContentMimeTypes(@Nullable String[] mimeTypes) { + mNode.mReceiveContentMimeTypes = mimeTypes; } @Override @@ -2449,7 +2449,7 @@ public class AssistStructure implements Parcelable { if (localeList != null) { Log.i(TAG, prefix + " LocaleList: " + localeList); } - String[] mimeTypes = node.getOnReceiveContentMimeTypes(); + String[] mimeTypes = node.getReceiveContentMimeTypes(); if (mimeTypes != null) { Log.i(TAG, prefix + " MIME types: " + Arrays.toString(mimeTypes)); } diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 30ea5c476191..2a50e0de2145 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -67,4 +67,5 @@ interface IUsageStatsManager { void reportUsageStop(in IBinder activity, String token, String callingPackage); int getUsageSource(); void forceUsageSourceSettingRead(); + long getLastTimeAnyComponentUsed(String packageName); } diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 1db7e9da0bcb..542473a3d2a9 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -1250,4 +1250,32 @@ public final class UsageStatsManager { } catch (RemoteException re) { } } + + /** + * Get the last time a package is used by any users including explicit user interaction and + * component usage, measured in milliseconds since the epoch and truncated to the boundary of + * last day before the exact time. For packages that are never used, the time will be the epoch. + * <p> Note that this usage stats is user-agnostic. </p> + * <p> + * Also note that component usage is only reported for component bindings (e.g. broadcast + * receiver, service, content provider) and only when such a binding would cause an app to leave + * the stopped state. + * See {@link UsageEvents.Event.USER_INTERACTION}, {@link UsageEvents.Event.APP_COMPONENT_USED}. + * </p> + * + * @param packageName The name of the package to be queried. + * @return last time the queried package is used since the epoch. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.PACKAGE_USAGE_STATS}) + public long getLastTimeAnyComponentUsed(@NonNull String packageName) { + try { + return mService.getLastTimeAnyComponentUsed(packageName); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 9fa4438cd153..cb0dad3b16c4 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -38,12 +38,14 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -857,8 +859,8 @@ public final class BluetoothAdapter { } synchronized (mLock) { if (sBluetoothLeScanner == null) { - sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), - getAttributionTag()); + sBluetoothLeScanner = + new BluetoothLeScanner(mManagerService, getAttributionSource()); } } return sBluetoothLeScanner; @@ -1674,13 +1676,11 @@ public final class BluetoothAdapter { return ActivityThread.currentOpPackageName(); } - private String getAttributionTag() { - // Workaround for legacy API for getting a BluetoothAdapter not - // passing a context + private AttributionSource getAttributionSource() { if (mContext != null) { - return mContext.getAttributionTag(); + return mContext.getAttributionSource(); } - return null; + return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(), null); } /** @@ -1721,7 +1721,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getOpPackageName(), getAttributionTag()); + return mService.startDiscovery(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java index fd04ccc1b530..280c845c63b0 100644 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java @@ -26,6 +26,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -83,9 +84,7 @@ public final class BluetoothLeScanner { private final Handler mHandler; private BluetoothAdapter mBluetoothAdapter; private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients; - - private final String mOpPackageName; - private final String mFeatureId; + private final AttributionSource mAttributionSource; /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. @@ -96,13 +95,12 @@ public final class BluetoothLeScanner { * @hide */ public BluetoothLeScanner(IBluetoothManager bluetoothManager, - @NonNull String opPackageName, @Nullable String featureId) { + @NonNull AttributionSource attributionSource) { mBluetoothManager = bluetoothManager; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mHandler = new Handler(Looper.getMainLooper()); mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>(); - mOpPackageName = opPackageName; - mFeatureId = featureId; + mAttributionSource = attributionSource; } /** @@ -268,8 +266,7 @@ public final class BluetoothLeScanner { wrapper.startRegistration(); } else { try { - gatt.startScanForIntent(callbackIntent, settings, filters, mOpPackageName, - mFeatureId); + gatt.startScanForIntent(callbackIntent, settings, filters, mAttributionSource); } catch (RemoteException e) { return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; } @@ -310,7 +307,7 @@ public final class BluetoothLeScanner { IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopScanForIntent(callbackIntent, mOpPackageName); + gatt.stopScanForIntent(callbackIntent); } catch (RemoteException e) { } } @@ -470,7 +467,7 @@ public final class BluetoothLeScanner { } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, - mResultStorages, mOpPackageName, mFeatureId); + mResultStorages, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 053bfc1a3253..c851519e9b50 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -179,6 +179,15 @@ public final class AttributionSource implements Parcelable { } /** @hide */ + @TestApi + public AttributionSource(int uid, @Nullable String packageName, + @Nullable String attributionTag, @Nullable Set<String> renouncedPermissions, + @Nullable AttributionSource next) { + this(uid, packageName, attributionTag, /*token*/ null, + renouncedPermissions, next); + } + + /** @hide */ public AttributionSource(@NonNull AttributionSource current, @Nullable AttributionSource next) { this(current.getUid(), current.getPackageName(), current.getAttributionTag(), @@ -526,7 +535,7 @@ public final class AttributionSource implements Parcelable { /** * The package that is accessing the permission protected data. */ - public @NonNull Builder setPackageName(@NonNull String value) { + public @NonNull Builder setPackageName(@Nullable String value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; mPackageName = value; @@ -536,7 +545,7 @@ public final class AttributionSource implements Parcelable { /** * The attribution tag of the app accessing the permission protected data. */ - public @NonNull Builder setAttributionTag(@NonNull String value) { + public @NonNull Builder setAttributionTag(@Nullable String value) { checkNotUsed(); mBuilderFieldsSet |= 0x4; mAttributionTag = value; @@ -550,7 +559,7 @@ public final class AttributionSource implements Parcelable { */ @SystemApi @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) - public @NonNull Builder setRenouncedPermissions(@NonNull Set<String> value) { + public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) { checkNotUsed(); mBuilderFieldsSet |= 0x10; mRenouncedPermissions = value; @@ -560,7 +569,7 @@ public final class AttributionSource implements Parcelable { /** * The next app to receive the permission protected data. */ - public @NonNull Builder setNext(@NonNull AttributionSource value) { + public @NonNull Builder setNext(@Nullable AttributionSource value) { checkNotUsed(); mBuilderFieldsSet |= 0x20; mNext = value; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 232daa8f8b47..36769825c439 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4165,6 +4165,7 @@ public abstract class Context { * @see android.net.PacProxyManager * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final String PAC_PROXY_SERVICE = "pac_proxy"; /** diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java index bd3eaea24126..ace2ba721c10 100644 --- a/core/java/android/content/ContextParams.java +++ b/core/java/android/content/ContextParams.java @@ -16,10 +16,13 @@ package android.content; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.ActivityThread; +import android.content.pm.PackageManager; import java.util.Collections; import java.util.Objects; @@ -179,6 +182,13 @@ public final class ContextParams { @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public @NonNull Builder setRenouncedPermissions( @Nullable Set<String> renouncedPermissions) { + // This is not a security check but a fail fast - the OS enforces the permission too + if (renouncedPermissions != null && !renouncedPermissions.isEmpty() + && ActivityThread.currentApplication().checkSelfPermission(Manifest.permission + .RENOUNCE_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Renouncing permissions requires: " + + Manifest.permission.RENOUNCE_PERMISSIONS); + } mRenouncedPermissions = renouncedPermissions; return this; } diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index fc963fef3eb4..049bfe7f23fb 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -979,7 +979,9 @@ public final class PermissionChecker { int uid, @NonNull Set<String> renouncedPermissions) { final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED; - if (permissionGranted && renouncedPermissions.contains(permission)) { + if (permissionGranted && renouncedPermissions.contains(permission) + && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS, + /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) { return false; } return permissionGranted; diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 0952b3e1233c..fa9142ca9449 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -896,11 +896,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { }; /** - * This change id forces the packages it is applied to to be resizable. We only allow resizing - * in fullscreen windowing mode, but not forcing the app into resizable multi-windowing mode. + * This change id forces the packages it is applied to be resizable. It won't change whether + * the app can be put into multi-windowing mode, but allow the app to resize when the window + * container resizes, such as display size change. * @hide */ @ChangeId + @Overridable @Disabled @TestApi public static final long FORCE_RESIZE_APP = 174042936L; // buganizer id @@ -910,6 +912,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @hide */ @ChangeId + @Overridable @Disabled @TestApi public static final long FORCE_NON_RESIZE_APP = 181136395L; // buganizer id diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 93acb7df7e30..3724ab942428 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -2289,7 +2289,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0; } - /** @hide */ + /** + * True if the application is pre-installed on the OEM partition of the system image. + * @hide + */ @SystemApi public boolean isOem() { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0; @@ -2337,13 +2340,19 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } - /** @hide */ + /** + * True if the application is pre-installed on the vendor partition of the system image. + * @hide + */ @SystemApi public boolean isVendor() { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0; } - /** @hide */ + /** + * True if the application is pre-installed on the product partition of the system image. + * @hide + */ @SystemApi public boolean isProduct() { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 60aac76fa8a5..97e1b543520e 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -72,7 +72,6 @@ import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringValueMap; import com.android.internal.util.Parcelling.BuiltIn.ForStringSet; import java.security.PublicKey; -import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -227,6 +226,14 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @NonNull @DataClass.ParcelWith(ForInternedStringList.class) protected List<String> adoptPermissions = emptyList(); + /** + * @deprecated consider migrating to {@link #getUsesPermissions} which has + * more parsed details, such as flags + */ + @NonNull + @Deprecated + @DataClass.ParcelWith(ForInternedStringList.class) + protected List<String> requestedPermissions = emptyList(); @NonNull private List<ParsedUsesPermission> usesPermissions = emptyList(); @@ -701,6 +708,11 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Override public ParsingPackageImpl addUsesPermission(ParsedUsesPermission permission) { this.usesPermissions = CollectionUtils.add(this.usesPermissions, permission); + + // Continue populating legacy data structures to avoid performance + // issues until all that code can be migrated + this.requestedPermissions = CollectionUtils.add(this.requestedPermissions, permission.name); + return this; } @@ -1142,6 +1154,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeByteArray(this.restrictUpdateHash); dest.writeStringList(this.originalPackages); sForInternedStringList.parcel(this.adoptPermissions, dest, flags); + sForInternedStringList.parcel(this.requestedPermissions, dest, flags); dest.writeTypedList(this.usesPermissions); sForInternedStringList.parcel(this.implicitPermissions, dest, flags); sForStringSet.parcel(this.upgradeKeySets, dest, flags); @@ -1264,6 +1277,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.restrictUpdateHash = in.createByteArray(); this.originalPackages = in.createStringArrayList(); this.adoptPermissions = sForInternedStringList.unparcel(in); + this.requestedPermissions = sForInternedStringList.unparcel(in); this.usesPermissions = in.createTypedArrayList(ParsedUsesPermission.CREATOR); this.implicitPermissions = sForInternedStringList.unparcel(in); this.upgradeKeySets = sForStringSet.unparcel(in); @@ -1331,7 +1345,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.gwpAsanMode = in.readInt(); this.minExtensionVersions = in.readSparseIntArray(); this.mBooleans = in.readLong(); - this.mProperties = in.createTypedArrayMap(Property.CREATOR); + this.mProperties = in.readHashMap(boot); this.memtagMode = in.readInt(); this.nativeHeapZeroInitialized = in.readInt(); this.requestOptimizedExternalStorageAccess = sForBoolean.unparcel(in); @@ -1558,15 +1572,14 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return adoptPermissions; } + /** + * @deprecated consider migrating to {@link #getUsesPermissions} which has + * more parsed details, such as flags + */ @NonNull @Override + @Deprecated public List<String> getRequestedPermissions() { - final List<ParsedUsesPermission> usesPermissions = getUsesPermissions(); - final int size = usesPermissions.size(); - final List<String> requestedPermissions = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - requestedPermissions.add(usesPermissions.get(i).name); - } return requestedPermissions; } diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index cdd8265ad5cd..4c6255da0a39 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -376,10 +376,13 @@ public class ColorStateList extends ComplexColor implements Parcelable { int[] stateSpec = new int[numAttrs]; for (int i = 0; i < numAttrs; i++) { final int stateResId = attrs.getAttributeNameResource(i); + if (stateResId == R.attr.lStar) { + // Non-finalized resource ids cannot be used in switch statements. + continue; + } switch (stateResId) { case R.attr.color: case R.attr.alpha: - case R.attr.lStar: // Recognized attribute, ignore. break; default: diff --git a/core/java/android/content/res/loader/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java index 0a698d18682b..463dcaced723 100644 --- a/core/java/android/content/res/loader/ResourcesProvider.java +++ b/core/java/android/content/res/loader/ResourcesProvider.java @@ -284,11 +284,6 @@ public class ResourcesProvider implements AutoCloseable, Closeable { Log.w(TAG, "ResourcesProvider " + this + " finalized with non-zero refs: " + mOpenCount); } - - if (mOpen) { - mOpen = false; - mApkAssets.close(); - } } } } diff --git a/core/java/android/content/rollback/RollbackManagerFrameworkInitializer.java b/core/java/android/content/rollback/RollbackManagerFrameworkInitializer.java index c5e4f9947550..24617a0bcb9a 100644 --- a/core/java/android/content/rollback/RollbackManagerFrameworkInitializer.java +++ b/core/java/android/content/rollback/RollbackManagerFrameworkInitializer.java @@ -15,7 +15,6 @@ */ package android.content.rollback; -import android.annotation.SystemApi; import android.app.SystemServiceRegistry; import android.content.Context; @@ -24,7 +23,6 @@ import android.content.Context; * * @hide */ -@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public class RollbackManagerFrameworkInitializer { private RollbackManagerFrameworkInitializer() {} diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java index 8c7695ad5a5a..fbc951e2f6e6 100644 --- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java +++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java @@ -16,6 +16,7 @@ package android.graphics.fonts; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; @@ -213,6 +214,16 @@ public final class FontFamilyUpdateRequest { public List<FontVariationAxis> getAxes() { return mAxes; } + + /** + * Returns the index of collection + * + * TODO(183752879): Make font index configurable and make this SystemApi. + * @hide + */ + public @IntRange(from = 0) int getIndex() { + return 0; + } } /** diff --git a/core/java/android/graphics/fonts/FontUpdateRequest.java b/core/java/android/graphics/fonts/FontUpdateRequest.java index b79c8f62d492..4dd5a72d446e 100644 --- a/core/java/android/graphics/fonts/FontUpdateRequest.java +++ b/core/java/android/graphics/fonts/FontUpdateRequest.java @@ -17,19 +17,24 @@ package android.graphics.fonts; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.LocaleList; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.text.FontConfig; +import android.util.TypedXmlSerializer; -import java.io.File; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Represents a font update request. Currently only font install request is supported. @@ -47,6 +52,273 @@ public final class FontUpdateRequest implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface Type {} + /** + * Font object used for update. + * + * Here is an example of Family/Font XML. + * <family name="my-sans"> + * <font name="MySans" weight="400" slant="0" axis="'wght' 400 'ital' 0" index="0" /> + * <font name="MySans" weight="400" slant="0" axis="'wght' 400 'ital' 1" index="0" /> + * <font name="MySans" weight="400" slant="0" axis="'wght' 700 'ital' 0" index="0" /> + * <font name="MySans" weight="400" slant="0" axis="'wght' 700 'ital' 1" index="0" /> + * </family> + * + * @see Font#readFromXml(XmlPullParser) + * @see Font#writeToXml(TypedXmlSerializer, Font) + * @see Family#readFromXml(XmlPullParser) + * @see Family#writeFamilyToXml(TypedXmlSerializer, Family) + */ + public static final class Font implements Parcelable { + private static final String ATTR_INDEX = "index"; + private static final String ATTR_WEIGHT = "weight"; + private static final String ATTR_SLANT = "slant"; + private static final String ATTR_AXIS = "axis"; + private static final String ATTR_POSTSCRIPT_NAME = "name"; + + private final @NonNull String mPostScriptName; + private final @NonNull FontStyle mFontStyle; + private final @IntRange(from = 0) int mIndex; + private final @NonNull String mFontVariationSettings; + + public Font(@NonNull String postScriptName, @NonNull FontStyle fontStyle, + @IntRange(from = 0) int index, @NonNull String fontVariationSettings) { + mPostScriptName = postScriptName; + mFontStyle = fontStyle; + mIndex = index; + mFontVariationSettings = fontVariationSettings; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString8(mPostScriptName); + dest.writeInt(mFontStyle.getWeight()); + dest.writeInt(mFontStyle.getSlant()); + dest.writeInt(mIndex); + dest.writeString8(mFontVariationSettings); + } + + public static final @NonNull Creator<Font> CREATOR = new Creator<Font>() { + @Override + public Font createFromParcel(Parcel source) { + String fontName = source.readString8(); + int weight = source.readInt(); + int slant = source.readInt(); + int index = source.readInt(); + String varSettings = source.readString8(); + return new Font(fontName, new FontStyle(weight, slant), index, varSettings); + } + + @Override + public Font[] newArray(int size) { + return new Font[size]; + } + }; + + /** + * Write {@link Font} instance to XML file. + * + * For the XML format, see {@link Font} class comment. + * + * @param out output XML serializer + * @param font a Font instance to be written. + */ + public static void writeToXml(TypedXmlSerializer out, Font font) throws IOException { + out.attribute(null, ATTR_POSTSCRIPT_NAME, font.getPostScriptName()); + out.attributeInt(null, ATTR_INDEX, font.getIndex()); + out.attributeInt(null, ATTR_WEIGHT, font.getFontStyle().getWeight()); + out.attributeInt(null, ATTR_SLANT, font.getFontStyle().getSlant()); + out.attribute(null, ATTR_AXIS, font.getFontVariationSettings()); + } + + /** + * Read {@link Font} instance from <font> element in XML + * + * For the XML format, see {@link Font} class comment. + * + * @param parser a parser that point <font> element. + * @return a font instance + * @throws IOException if font element is invalid. + */ + public static Font readFromXml(XmlPullParser parser) throws IOException { + String psName = parser.getAttributeValue(null, ATTR_POSTSCRIPT_NAME); + if (psName == null) { + throw new IOException("name attribute is missing font tag."); + } + int index = getAttributeValueInt(parser, ATTR_INDEX, 0); + int weight = getAttributeValueInt(parser, ATTR_WEIGHT, FontStyle.FONT_WEIGHT_NORMAL); + int slant = getAttributeValueInt(parser, ATTR_SLANT, FontStyle.FONT_SLANT_UPRIGHT); + String varSettings = parser.getAttributeValue(null, ATTR_AXIS); + if (varSettings == null) { + varSettings = ""; + } + return new Font(psName, new FontStyle(weight, slant), index, varSettings); + } + + public @NonNull String getPostScriptName() { + return mPostScriptName; + } + + public @NonNull FontStyle getFontStyle() { + return mFontStyle; + } + + public @IntRange(from = 0) int getIndex() { + return mIndex; + } + + public @NonNull String getFontVariationSettings() { + return mFontVariationSettings; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Font font = (Font) o; + return mIndex == font.mIndex + && mPostScriptName.equals(font.mPostScriptName) + && mFontStyle.equals(font.mFontStyle) + && mFontVariationSettings.equals(font.mFontVariationSettings); + } + + @Override + public int hashCode() { + return Objects.hash(mPostScriptName, mFontStyle, mIndex, mFontVariationSettings); + } + + @Override + public String toString() { + return "Font{" + + "mPostScriptName='" + mPostScriptName + '\'' + + ", mFontStyle=" + mFontStyle + + ", mIndex=" + mIndex + + ", mFontVariationSettings='" + mFontVariationSettings + '\'' + + '}'; + } + } + + /** + * Font Family object used for update request. + */ + public static final class Family implements Parcelable { + private static final String TAG_FAMILY = "family"; + private static final String ATTR_NAME = "name"; + private static final String TAG_FONT = "font"; + + private final @Nullable String mName; + private final @NonNull List<Font> mFonts; + + public Family(String name, List<Font> fonts) { + mName = name; + mFonts = fonts; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString8(mName); + dest.writeParcelableList(mFonts, flags); + } + + public static final @NonNull Creator<Family> CREATOR = new Creator<Family>() { + + @Override + public Family createFromParcel(Parcel source) { + String familyName = source.readString8(); + List<Font> fonts = source.readParcelableList( + new ArrayList<>(), Font.class.getClassLoader()); + return new Family(familyName, fonts); + } + + @Override + public Family[] newArray(int size) { + return new Family[size]; + } + }; + + /** + * Write {@link Family} instance to XML. + * + * For the XML format, see {@link Font} class comment. + * + * @param out an output XML serializer + * @param family a {@link Family} instance to be written + */ + public static void writeFamilyToXml(@NonNull TypedXmlSerializer out, @NonNull Family family) + throws IOException { + out.attribute(null, ATTR_NAME, family.getName()); + List<Font> fonts = family.getFonts(); + for (int i = 0; i < fonts.size(); ++i) { + Font font = fonts.get(i); + out.startTag(null, TAG_FONT); + Font.writeToXml(out, font); + out.endTag(null, TAG_FONT); + } + } + + /** + * Read a {@link Family} instance from <family> element in XML + * + * For the XML format, see {@link Font} class comment. + * + * @param parser an XML parser that points <family> element. + * @return an {@link Family} instance + */ + public static @NonNull Family readFromXml(@NonNull XmlPullParser parser) + throws XmlPullParserException, IOException { + List<Font> fonts = new ArrayList<>(); + if (parser.getEventType() != XmlPullParser.START_TAG + || !parser.getName().equals(TAG_FAMILY)) { + throw new IOException("Unexpected parser state: must be START_TAG with family"); + } + String name = parser.getAttributeValue(null, ATTR_NAME); + int type = 0; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_FONT)) { + fonts.add(Font.readFromXml(parser)); + } else if (type == XmlPullParser.END_TAG && parser.getName().equals(TAG_FAMILY)) { + break; + } + } + return new Family(name, fonts); + } + + public @NonNull String getName() { + return mName; + } + + public @NonNull List<Font> getFonts() { + return mFonts; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Family family = (Family) o; + return mName.equals(family.mName) && mFonts.equals(family.mFonts); + } + + @Override + public int hashCode() { + return Objects.hash(mName, mFonts); + } + + @Override + public String toString() { + return "Family{mName='" + mName + '\'' + ", mFonts=" + mFonts + '}'; + } + } + public static final Creator<FontUpdateRequest> CREATOR = new Creator<FontUpdateRequest>() { @Override public FontUpdateRequest createFromParcel(Parcel in) { @@ -68,7 +340,7 @@ public final class FontUpdateRequest implements Parcelable { private final byte[] mSignature; // NonNull if mType == TYPE_UPDATE_FONT_FAMILY. @Nullable - private final FontConfig.FontFamily mFontFamily; + private final Family mFontFamily; public FontUpdateRequest(@NonNull ParcelFileDescriptor fd, @NonNull byte[] signature) { mType = TYPE_UPDATE_FONT_FILE; @@ -77,31 +349,29 @@ public final class FontUpdateRequest implements Parcelable { mFontFamily = null; } - public FontUpdateRequest(@NonNull FontConfig.FontFamily fontFamily) { + public FontUpdateRequest(@NonNull Family fontFamily) { mType = TYPE_UPDATE_FONT_FAMILY; mFd = null; mSignature = null; mFontFamily = fontFamily; } - public FontUpdateRequest(@NonNull String postScriptName, + public FontUpdateRequest(@NonNull String familyName, @NonNull List<FontFamilyUpdateRequest.Font> variations) { - // TODO: Serialize the request directly instead of reusing FontConfig.FontFamily. - this(createFontFamily(postScriptName, variations)); + this(createFontFamily(familyName, variations)); } - private static FontConfig.FontFamily createFontFamily(@NonNull String postScriptName, + private static Family createFontFamily(@NonNull String familyName, @NonNull List<FontFamilyUpdateRequest.Font> fonts) { - List<FontConfig.Font> configFonts = new ArrayList<>(fonts.size()); + List<Font> updateFonts = new ArrayList<>(fonts.size()); for (FontFamilyUpdateRequest.Font font : fonts) { - // TODO: Support .otf. - configFonts.add(new FontConfig.Font(new File(font.getPostScriptName() + ".ttf"), null, - font.getStyle(), 0 /* index */, - FontVariationAxis.toFontVariationSettings(font.getAxes()), - null /* fontFamilyName */)); - } - return new FontConfig.FontFamily(configFonts, postScriptName, - LocaleList.getEmptyLocaleList(), FontConfig.FontFamily.VARIANT_DEFAULT); + updateFonts.add(new Font( + font.getPostScriptName(), + font.getStyle(), + font.getIndex(), + FontVariationAxis.toFontVariationSettings(font.getAxes()))); + } + return new Family(familyName, updateFonts); } protected FontUpdateRequest(Parcel in) { @@ -126,7 +396,7 @@ public final class FontUpdateRequest implements Parcelable { } @Nullable - public FontConfig.FontFamily getFontFamily() { + public Family getFontFamily() { return mFontFamily; } @@ -142,4 +412,17 @@ public final class FontUpdateRequest implements Parcelable { dest.writeBlob(mSignature); dest.writeParcelable(mFontFamily, flags); } + + // Utility functions + private static int getAttributeValueInt(XmlPullParser parser, String name, int defaultValue) { + try { + String value = parser.getAttributeValue(null, name); + if (value == null) { + return defaultValue; + } + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } } diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java index ff1a17e07c11..41672b7cbaea 100644 --- a/core/java/android/hardware/biometrics/BiometricTestSession.java +++ b/core/java/android/hardware/biometrics/BiometricTestSession.java @@ -38,7 +38,7 @@ import java.util.concurrent.TimeUnit; */ @TestApi public class BiometricTestSession implements AutoCloseable { - private static final String TAG = "BiometricTestSession"; + private static final String BASE_TAG = "BiometricTestSession"; /** * @hide @@ -66,12 +66,12 @@ public class BiometricTestSession implements AutoCloseable { private final ITestSessionCallback mCallback = new ITestSessionCallback.Stub() { @Override public void onCleanupStarted(int userId) { - Log.d(TAG, "onCleanupStarted, sensor: " + mSensorId + ", userId: " + userId); + Log.d(getTag(), "onCleanupStarted, sensor: " + mSensorId + ", userId: " + userId); } @Override public void onCleanupFinished(int userId) { - Log.d(TAG, "onCleanupFinished, sensor: " + mSensorId + Log.d(getTag(), "onCleanupFinished, sensor: " + mSensorId + ", userId: " + userId + ", remaining users: " + mUsersCleaningUp.size()); mUsersCleaningUp.remove(userId); @@ -107,7 +107,7 @@ public class BiometricTestSession implements AutoCloseable { @RequiresPermission(TEST_BIOMETRIC) private void setTestHalEnabled(boolean enabled) { try { - Log.w(TAG, "setTestHalEnabled, sensor: " + mSensorId + " enabled: " + enabled); + Log.w(getTag(), "setTestHalEnabled, sensor: " + mSensorId + " enabled: " + enabled); mTestSession.setTestHalEnabled(enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -217,7 +217,7 @@ public class BiometricTestSession implements AutoCloseable { public void cleanupInternalState(int userId) { try { if (mUsersCleaningUp.contains(userId)) { - Log.w(TAG, "Cleanup already in progress for user: " + userId); + Log.w(getTag(), "Cleanup already in progress for user: " + userId); } mUsersCleaningUp.add(userId); @@ -230,6 +230,7 @@ public class BiometricTestSession implements AutoCloseable { @Override @RequiresPermission(TEST_BIOMETRIC) public void close() { + Log.d(getTag(), "Close, mTestedUsers size; " + mTestedUsers.size()); // Cleanup can be performed using the test HAL, since it always responds to enumerate with // zero enrollments. if (!mTestedUsers.isEmpty()) { @@ -239,15 +240,19 @@ public class BiometricTestSession implements AutoCloseable { } try { - Log.d(TAG, "Awaiting latch..."); - mCloseLatch.await(10, TimeUnit.SECONDS); - Log.d(TAG, "Finished awaiting"); + Log.d(getTag(), "Awaiting latch..."); + mCloseLatch.await(3, TimeUnit.SECONDS); + Log.d(getTag(), "Finished awaiting"); } catch (InterruptedException e) { - Log.e(TAG, "Latch interrupted", e); + Log.e(getTag(), "Latch interrupted", e); } } // Disable the test HAL after the sensor becomes idle. setTestHalEnabled(false); } + + private String getTag() { + return BASE_TAG + "_" + mSensorId; + } } diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 8b53fc748b7b..293c0af04b8b 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -1400,6 +1400,8 @@ public final class CameraManager { // devices going offline (in real world scenarios, these permissions aren't // changeable). Future calls to getCameraIdList() will reflect the changes in // the camera id list after getCameraIdListNoLazy() is called. + // We need to remove the torch ids which may have been associated with the + // devices removed as well. This is the same situation. cameraStatuses = mCameraService.addListener(testListener); mCameraService.removeListener(testListener); for (CameraStatus c : cameraStatuses) { @@ -1418,6 +1420,7 @@ public final class CameraManager { } for (String id : deviceIdsToRemove) { onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, id); + mTorchStatus.remove(id); } } catch (ServiceSpecificException e) { // Unexpected failure @@ -1518,7 +1521,11 @@ public final class CameraManager { */ public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) { if (!mDeviceStatus.containsKey(cameraId)) { - Log.e(TAG, "cameraIdHasConcurrentStreamsLocked called on non existing camera id"); + // physical camera ids aren't advertised in concurrent camera id combinations. + if (DEBUG) { + Log.v(TAG, " physical camera id " + cameraId + " is hidden." + + " Available logical camera ids : " + mDeviceStatus.toString()); + } return false; } for (Set<String> comb : mConcurrentCameraIdCombinations) { diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java index 8eb1dccdf65e..bf4593260a70 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java @@ -25,6 +25,7 @@ import android.media.Image; import android.media.ImageReader; import android.media.ImageWriter; import android.annotation.NonNull; +import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.util.Size; @@ -39,6 +40,7 @@ public class CameraExtensionForwardProcessor { private final IPreviewImageProcessorImpl mProcessor; private final long mOutputSurfaceUsage; private final int mOutputSurfaceFormat; + private final Handler mHandler; private ImageReader mIntermediateReader = null; private Surface mIntermediateSurface = null; @@ -48,10 +50,11 @@ public class CameraExtensionForwardProcessor { private boolean mOutputAbandoned = false; public CameraExtensionForwardProcessor(@NonNull IPreviewImageProcessorImpl processor, - int format, long surfaceUsage) { + int format, long surfaceUsage, @NonNull Handler handler) { mProcessor = processor; mOutputSurfaceUsage = surfaceUsage; mOutputSurfaceFormat = format; + mHandler = handler; } public void close() { @@ -98,7 +101,7 @@ public class CameraExtensionForwardProcessor { mResolution.getHeight(), CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, FORWARD_QUEUE_SIZE, mOutputSurfaceUsage); mIntermediateSurface = mIntermediateReader.getSurface(); - mIntermediateReader.setOnImageAvailableListener(new ForwardCallback(), null); + mIntermediateReader.setOnImageAvailableListener(new ForwardCallback(), mHandler); mProcessor.onOutputSurface(mIntermediateSurface, mOutputSurfaceFormat); // PreviewImageProcessorImpl always expect the extension processing format as input @@ -124,11 +127,15 @@ public class CameraExtensionForwardProcessor { @Override public void onImageAvailable(ImageReader reader) { Image processedImage = null; try { - processedImage = mIntermediateReader.acquireNextImage(); + processedImage = reader.acquireNextImage(); } catch (IllegalStateException e) { Log.e(TAG, "Failed to acquire processed image!"); return; } + if (processedImage == null) { + Log.e(TAG, "Invalid image"); + return; + } if (mOutputSurface != null && mOutputSurface.isValid() && !mOutputAbandoned) { if (mOutputWriter == null) { diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 1f5098f80735..3d771c01e8ac 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -352,7 +352,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { try { mPreviewImageProcessor = new CameraExtensionForwardProcessor( mPreviewExtender.getPreviewImageProcessor(), repeatingSurfaceInfo.mFormat, - repeatingSurfaceInfo.mUsage); + repeatingSurfaceInfo.mUsage, mHandler); } catch (ClassCastException e) { throw new UnsupportedOperationException("Failed casting preview processor!"); } diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 29bdee3edcee..bd8df8724f92 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -63,27 +63,32 @@ public final class MandatoryStreamCombination { private final ArrayList<Size> mAvailableSizes = new ArrayList<Size> (); private final boolean mIsInput; private final boolean mIsUltraHighResolution; + private final boolean mIsMaximumSize; /** * Create a new {@link MandatoryStreamInformation}. * - @param availableSizes List of possible stream sizes. + * @param availableSizes List of possible stream sizes. * @param format Image format. + * @param isMaximumSize Whether this is a maximum size stream. * * @throws IllegalArgumentException * if sizes is empty or if the format was not user-defined in * ImageFormat/PixelFormat. * @hide */ - public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format) { - this(availableSizes, format, /*isInput*/false, /*maximumResolution*/false); + public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format, + boolean isMaximumSize) { + this(availableSizes, format, isMaximumSize, /*isInput*/false, + /*isUltraHighResolution*/false); } /** * Create a new {@link MandatoryStreamInformation}. * - @param availableSizes List of possible stream sizes. + * @param availableSizes List of possible stream sizes. * @param format Image format. + * @param isMaximumSize Whether this is a maximum size stream. * @param isInput Flag indicating whether this stream is input. * * @throws IllegalArgumentException @@ -92,17 +97,20 @@ public final class MandatoryStreamCombination { * @hide */ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format, - boolean isInput) { - this(availableSizes, format, isInput, /*maximumResolution*/ false); + boolean isMaximumSize, boolean isInput) { + this(availableSizes, format, isMaximumSize, isInput, + /*isUltraHighResolution*/ false); } /** * Create a new {@link MandatoryStreamInformation}. * - @param availableSizes List of possible stream sizes. + * @param availableSizes List of possible stream sizes. * @param format Image format. + * @param isMaximumSize Whether this is a maximum size stream. * @param isInput Flag indicating whether this stream is input. - * @param isMaxResolution Flag indicating whether this is a maximum resolution stream. + * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution + * stream. * * @throws IllegalArgumentException * if sizes is empty or if the format was not user-defined in @@ -110,12 +118,13 @@ public final class MandatoryStreamCombination { * @hide */ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format, - boolean isInput, boolean isUltraHighResolution) { + boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution) { if (availableSizes.isEmpty()) { throw new IllegalArgumentException("No available sizes"); } mAvailableSizes.addAll(availableSizes); mFormat = checkArgumentFormat(format); + mIsMaximumSize = isMaximumSize; mIsInput = isInput; mIsUltraHighResolution = isUltraHighResolution; } @@ -130,10 +139,12 @@ public final class MandatoryStreamCombination { /** * Confirms whether or not this is an ultra high resolution stream. - * An 'ultra high resolution' stream is one which has a configuration which appears in + * + * <p>An 'ultra high resolution' stream is one which has a configuration which appears in * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION}, * Streams which are ultra high resolution must not be included with streams which are not - * ultra high resolution in the same {@link android.hardware.camera2.CaptureRequest}. + * ultra high resolution in the same {@link android.hardware.camera2.CaptureRequest}.</p> + * * @return true in case the stream is ultra high resolution, false otherwise. */ public boolean isUltraHighResolution() { @@ -141,6 +152,34 @@ public final class MandatoryStreamCombination { } /** + * Confirms whether or not this is a maximum size stream. + * + * <p>A stream with maximum size is one with the camera device's maximum resolution + * for the stream's format as appears in {@link + * android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}. This + * maximum size has the same meaning as the 'MAXIMUM' target size documented in the camera + * capture session {@link CameraDevice#createCaptureSession guideline}.</p> + * + * <p>The application can use a + * {@link android.hardware.camera2.MultiResolutionImageReader} for a maximum size + * output stream if the camera device supports multi-resolution outputs for the stream's + * format. See {@link + * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP} + * for details.</p> + * + * <p>This is different from the ultra high resolution flag, which applies only to + * ultra high resolution sensor camera devices and refers to a stream in + * {@link + * android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION} + * instead.</p> + * + * @return true if the stream is a maximum size stream. + */ + public boolean isMaximumSize() { + return mIsMaximumSize; + } + + /** * Return the list of available sizes for this mandatory stream. * * <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest @@ -827,7 +866,8 @@ public final class MandatoryStreamCombination { getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat))); sizes.add(sizeChosen); try { - streamInfo = new MandatoryStreamInformation(sizes, template.mFormat); + streamInfo = new MandatoryStreamInformation(sizes, template.mFormat, + /*isMaximumSize*/false); } catch (IllegalArgumentException e) { String cause = "No available sizes found for format: " + template.mFormat + " size threshold: " + template.mSizeThreshold + " combination: " @@ -890,9 +930,11 @@ public final class MandatoryStreamCombination { inputSize.add(maxRawInputSize); streamsInfo.add(new MandatoryStreamInformation(inputSize, - ImageFormat.RAW_SENSOR, /*isInput*/true, /*ultraHighResolution*/true)); + ImageFormat.RAW_SENSOR, /*isMaximumSize*/true, /*isInput*/true, + /*ultraHighResolution*/true)); streamsInfo.add(new MandatoryStreamInformation(inputSize, - ImageFormat.RAW_SENSOR, /*isInput*/ false, /*ultraHighResolution*/true)); + ImageFormat.RAW_SENSOR, /*isMaximumSize*/true, /*isInput*/ false, + /*ultraHighResolution*/true)); MandatoryStreamCombination streamCombination; streamCombination = new MandatoryStreamCombination(streamsInfo, "Remosaic reprocessing", /*isReprocess*/true); @@ -917,10 +959,11 @@ public final class MandatoryStreamCombination { Size sizeChosen = getMaxSize(streamConfigMap.getOutputSizes( template.mFormat)); + boolean isMaximumSize = (template.mSizeThreshold == SizeThreshold.MAXIMUM); sizes.add(sizeChosen); try { streamInfo = new MandatoryStreamInformation(sizes, template.mFormat, - /*isInput*/ false, /*ultraHighResolution*/ true); + isMaximumSize, /*isInput*/ false, /*ultraHighResolution*/ true); } catch (IllegalArgumentException e) { String cause = "No available sizes found for format: " + template.mFormat + " size threshold: " + template.mSizeThreshold + " combination: " @@ -1096,8 +1139,9 @@ public final class MandatoryStreamCombination { format = ImageFormat.YUV_420_888; } streamsInfo.add(new MandatoryStreamInformation(inputSize, format, - /*isInput*/true)); - streamsInfo.add(new MandatoryStreamInformation(inputSize, format)); + /*isMaximumSize*/true, /*isInput*/true)); + streamsInfo.add(new MandatoryStreamInformation(inputSize, format, + /*isMaximumSize*/true)); } for (StreamTemplate template : combTemplate.mStreamTemplates) { @@ -1112,8 +1156,11 @@ public final class MandatoryStreamCombination { } MandatoryStreamInformation streamInfo; + boolean isMaximumSize = + (template.mSizeThreshold == SizeThreshold.MAXIMUM); try { - streamInfo = new MandatoryStreamInformation(sizes, template.mFormat); + streamInfo = new MandatoryStreamInformation(sizes, template.mFormat, + isMaximumSize); } catch (IllegalArgumentException e) { Log.e(TAG, "No available sizes found for format: " + template.mFormat + " size threshold: " + template.mSizeThreshold + " combination: " + diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 5cfcd667632b..9198eb74d1f8 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -171,7 +171,7 @@ class IInputMethodWrapper extends IInputMethod.Stub SomeArgs args = (SomeArgs) msg.obj; try { inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1, - (IInputMethodPrivilegedOperations) args.arg2); + (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3); } finally { args.recycle(); } @@ -280,9 +280,10 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void initializeInternal(IBinder token, int displayId, - IInputMethodPrivilegedOperations privOps) { + IInputMethodPrivilegedOperations privOps, int configChanges) { mCaller.executeOrSendMessage( - mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps)); + mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps, + configChanges)); } @BinderThread diff --git a/core/java/android/inputmethodservice/ImsConfigurationTracker.java b/core/java/android/inputmethodservice/ImsConfigurationTracker.java new file mode 100644 index 000000000000..3c788884371b --- /dev/null +++ b/core/java/android/inputmethodservice/ImsConfigurationTracker.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.inputmethodservice; + +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.Configuration; +import android.content.res.Resources; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +/** + * Helper class that takes care of Configuration change behavior of {@link InputMethodService}. + * Note: this class is public for testing only. Never call any of it's methods for development + * of IMEs. + * @hide + */ +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public final class ImsConfigurationTracker { + + /** + * A constant value that represents {@link Configuration} has changed from the last time + * {@link InputMethodService#onConfigurationChanged(Configuration)} was called. + */ + private static final int CONFIG_CHANGED = -1; + + @Nullable + private Configuration mLastKnownConfig = null; + private int mHandledConfigChanges = 0; + private boolean mInitialized = false; + + /** + * Called from {@link InputMethodService.InputMethodImpl + * #initializeInternal(IBinder, int, IInputMethodPrivilegedOperations, int)} ()} + * @param handledConfigChanges Configuration changes declared handled by IME + * {@link android.R.styleable#InputMethod_configChanges}. + */ + @MainThread + public void onInitialize(int handledConfigChanges) { + Preconditions.checkState(!mInitialized, "onInitialize can be called only once."); + mInitialized = true; + mHandledConfigChanges = handledConfigChanges; + } + + /** + * Called from {@link InputMethodService.InputMethodImpl#onBindInput()} + */ + @MainThread + public void onBindInput(@Nullable Resources resources) { + Preconditions.checkState(mInitialized, + "onBindInput can be called only after onInitialize()."); + if (mLastKnownConfig == null && resources != null) { + mLastKnownConfig = new Configuration(resources.getConfiguration()); + } + } + + /** + * Dynamically set handled configChanges. + * Note: this method is public for testing only. + */ + public void setHandledConfigChanges(int configChanges) { + mHandledConfigChanges = configChanges; + } + + /** + * Called from {@link InputMethodService.InputMethodImpl#onConfigurationChanged(Configuration)}} + */ + @MainThread + public void onConfigurationChanged(@NonNull Configuration newConfig, + @NonNull Runnable resetStateForNewConfigurationRunner) { + if (!mInitialized) { + return; + } + final int diff = mLastKnownConfig != null + ? mLastKnownConfig.diffPublicOnly(newConfig) : CONFIG_CHANGED; + // If the new config is the same as the config this Service is already running with, + // then don't bother calling resetStateForNewConfiguration. + final int unhandledDiff = (diff & ~mHandledConfigChanges); + if (unhandledDiff != 0) { + resetStateForNewConfigurationRunner.run(); + } + if (diff != 0) { + mLastKnownConfig = new Configuration(newConfig); + } + } +} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 7e2be01feb01..4ee5383a56be 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -513,6 +513,7 @@ public class InputMethodService extends AbstractInputMethodService { private boolean mIsAutomotive; private Handler mHandler; private boolean mImeSurfaceScheduledForRemoval; + private ImsConfigurationTracker mConfigTracker = new ImsConfigurationTracker(); /** * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} @@ -588,12 +589,13 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public final void initializeInternal(@NonNull IBinder token, int displayId, - IInputMethodPrivilegedOperations privilegedOperations) { + IInputMethodPrivilegedOperations privilegedOperations, int configChanges) { if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) { Log.w(TAG, "The token has already registered, ignore this initialization."); return; } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal"); + mConfigTracker.onInitialize(configChanges); mPrivOps.set(privilegedOperations); InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps); updateInputMethodDisplay(displayId); @@ -663,6 +665,7 @@ public class InputMethodService extends AbstractInputMethodService { reportFullscreenMode(); initialize(); onBindInput(); + mConfigTracker.onBindInput(getResources()); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -938,7 +941,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void setImeWindowStatus(int visibilityFlags, int backDisposition) { - mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition); + mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition); } /** Set region of the keyboard to be avoided from back gesture */ @@ -1428,10 +1431,13 @@ public class InputMethodService extends AbstractInputMethodService { * state: {@link #onStartInput} if input is active, and * {@link #onCreateInputView} and {@link #onStartInputView} and related * appropriate functions if the UI is displayed. + * <p>Starting with {@link Build.VERSION_CODES#S}, IMEs can opt into handling configuration + * changes themselves instead of being restarted with + * {@link android.R.styleable#InputMethod_configChanges}. */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - resetStateForNewConfiguration(); + mConfigTracker.onConfigurationChanged(newConfig, this::resetStateForNewConfiguration); } private void resetStateForNewConfiguration() { @@ -3181,7 +3187,7 @@ public class InputMethodService extends AbstractInputMethodService { requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS); } } - + void startExtractingText(boolean inputChanged) { final ExtractEditText eet = mExtractEditText; if (eet != null && getCurrentInputStarted() diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 171c6a2c6a19..f50aa991a67c 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -78,5 +78,4 @@ interface INetworkPolicyManager { boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork); boolean isUidRestrictedOnMeteredNetworks(int uid); - boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted); } diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index b037261f0bc2..1eef7d9a5337 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -198,15 +198,11 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { final int oemManaged = getOemBitfield(snapshot.networkCapabilities); if (legacyType == TYPE_WIFI) { - if (snapshot.networkCapabilities.getSsid() != null) { - networkId = snapshot.networkCapabilities.getSsid(); - if (networkId == null) { - // TODO: Figure out if this code path never runs. If so, remove them. - final WifiManager wifi = (WifiManager) context.getSystemService( - Context.WIFI_SERVICE); - final WifiInfo info = wifi.getConnectionInfo(); - networkId = info != null ? info.getSSID() : null; - } + networkId = snapshot.networkCapabilities.getSsid(); + if (networkId == null) { + final WifiManager wifi = context.getSystemService(WifiManager.class); + final WifiInfo info = wifi.getConnectionInfo(); + networkId = info != null ? info.getSSID() : null; } } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 68606ec90dc9..632eb15ecdde 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -564,31 +564,6 @@ public class NetworkPolicyManager { } /** - * Figure out if networking is blocked for a given set of conditions. - * - * This is used by ConnectivityService via passing stale copies of conditions, so it must not - * take any locks. - * - * @param uid The target uid. - * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService. - * @param isNetworkMetered True if the network is metered. - * @param isBackgroundRestricted True if data saver is enabled. - * - * @return true if networking is blocked for the UID under the specified conditions. - * - * @hide - */ - public boolean checkUidNetworkingBlocked(int uid, int uidRules, - boolean isNetworkMetered, boolean isBackgroundRestricted) { - try { - return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered, - isBackgroundRestricted); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Check that the given uid is restricted from doing networking on metered networks. * * @param uid The target uid. @@ -794,7 +769,7 @@ public class NetworkPolicyManager { @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull public static String blockedReasonsToString(int blockedReasons) { - return DebugUtils.flagsToString(NetworkPolicyManager.class, "BLOCKED_", blockedReasons); + return DebugUtils.flagsToString(ConnectivityManager.class, "BLOCKED_", blockedReasons); } /** diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl index 236ae8bb11b2..11bc443c9dd6 100644 --- a/core/java/android/net/vcn/IVcnStatusCallback.aidl +++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl @@ -20,7 +20,7 @@ package android.net.vcn; oneway interface IVcnStatusCallback { void onVcnStatusChanged(int statusCode); void onGatewayConnectionError( - in int[] gatewayNetworkCapabilities, + in String gatewayConnectionName, int errorCode, in String exceptionClass, in String exceptionMessage); diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java index 52cc2182b094..d41c0b4fbdb3 100644 --- a/core/java/android/net/vcn/VcnConfig.java +++ b/core/java/android/net/vcn/VcnConfig.java @@ -183,12 +183,25 @@ public final class VcnConfig implements Parcelable { * * @param gatewayConnectionConfig the configuration for an individual gateway connection * @return this {@link Builder} instance, for chaining + * @throws IllegalArgumentException if a VcnGatewayConnectionConfig has already been set for + * this {@link VcnConfig} with the same GatewayConnection name (as returned via {@link + * VcnGatewayConnectionConfig#getGatewayConnectionName()}). */ @NonNull public Builder addGatewayConnectionConfig( @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) { Objects.requireNonNull(gatewayConnectionConfig, "gatewayConnectionConfig was null"); + for (final VcnGatewayConnectionConfig vcnGatewayConnectionConfig : + mGatewayConnectionConfigs) { + if (vcnGatewayConnectionConfig + .getGatewayConnectionName() + .equals(gatewayConnectionConfig.getGatewayConnectionName())) { + throw new IllegalArgumentException( + "GatewayConnection for specified name already exists"); + } + } + mGatewayConnectionConfigs.add(gatewayConnectionConfig); return this; } diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index d4e8e2dca296..75db3820f5e7 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -148,6 +148,8 @@ public final class VcnGatewayConnectionConfig { TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(15) }; + private static final String GATEWAY_CONNECTION_NAME_KEY = "mGatewayConnectionName"; + @NonNull private final String mGatewayConnectionName; private static final String CTRL_PLANE_CONFIG_KEY = "mCtrlPlaneConfig"; @NonNull private VcnControlPlaneConfig mCtrlPlaneConfig; @@ -166,11 +168,13 @@ public final class VcnGatewayConnectionConfig { /** Builds a VcnGatewayConnectionConfig with the specified parameters. */ private VcnGatewayConnectionConfig( + @NonNull String gatewayConnectionName, @NonNull VcnControlPlaneConfig ctrlPlaneConfig, @NonNull Set<Integer> exposedCapabilities, @NonNull Set<Integer> underlyingCapabilities, @NonNull long[] retryIntervalsMs, @IntRange(from = MIN_MTU_V6) int maxMtu) { + mGatewayConnectionName = gatewayConnectionName; mCtrlPlaneConfig = ctrlPlaneConfig; mExposedCapabilities = new TreeSet(exposedCapabilities); mUnderlyingCapabilities = new TreeSet(underlyingCapabilities); @@ -192,6 +196,7 @@ public final class VcnGatewayConnectionConfig { final PersistableBundle underlyingCapsBundle = in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY); + mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY); mCtrlPlaneConfig = VcnControlPlaneConfig.fromPersistableBundle(ctrlPlaneConfigBundle); mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList( exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); @@ -204,6 +209,7 @@ public final class VcnGatewayConnectionConfig { } private void validate() { + Objects.requireNonNull(mGatewayConnectionName, "gatewayConnectionName was null"); Objects.requireNonNull(mCtrlPlaneConfig, "control plane config was null"); Preconditions.checkArgument( @@ -242,6 +248,20 @@ public final class VcnGatewayConnectionConfig { } /** + * Returns the configured Gateway Connection name. + * + * <p>This name is used by the configuring apps to distinguish between + * VcnGatewayConnectionConfigs configured on a single {@link VcnConfig}. This will be used as + * the identifier in VcnStatusCallback invocations. + * + * @see VcnManager.VcnStatusCallback#onGatewayConnectionError + */ + @NonNull + public String getGatewayConnectionName() { + return mGatewayConnectionName; + } + + /** * Returns control plane configuration. * * @hide @@ -364,6 +384,7 @@ public final class VcnGatewayConnectionConfig { new ArrayList<>(mUnderlyingCapabilities), PersistableBundleUtils.INTEGER_SERIALIZER); + result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName); result.putPersistableBundle(CTRL_PLANE_CONFIG_KEY, ctrlPlaneConfigBundle); result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle); result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle); @@ -376,6 +397,7 @@ public final class VcnGatewayConnectionConfig { @Override public int hashCode() { return Objects.hash( + mGatewayConnectionName, mExposedCapabilities, mUnderlyingCapabilities, Arrays.hashCode(mRetryIntervalsMs), @@ -389,7 +411,8 @@ public final class VcnGatewayConnectionConfig { } final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other; - return mExposedCapabilities.equals(rhs.mExposedCapabilities) + return mGatewayConnectionName.equals(rhs.mGatewayConnectionName) + && mExposedCapabilities.equals(rhs.mExposedCapabilities) && mUnderlyingCapabilities.equals(rhs.mUnderlyingCapabilities) && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs) && mMaxMtu == rhs.mMaxMtu; @@ -399,6 +422,7 @@ public final class VcnGatewayConnectionConfig { * This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */ public static final class Builder { + @NonNull private final String mGatewayConnectionName; @NonNull private final VcnControlPlaneConfig mCtrlPlaneConfig; @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); @NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet(); @@ -412,12 +436,22 @@ public final class VcnGatewayConnectionConfig { /** * Construct a Builder object. * + * @param gatewayConnectionName the String GatewayConnection name for this + * VcnGatewayConnectionConfig. Each VcnGatewayConnectionConfig within a {@link + * VcnConfig} must be given a unique name. This name is used by the caller to + * distinguish between VcnGatewayConnectionConfigs configured on a single {@link + * VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations. * @param ctrlPlaneConfig the control plane configuration * @see VcnControlPlaneConfig + * @see VcnManager.VcnStatusCallback#onGatewayConnectionError */ - public Builder(@NonNull VcnControlPlaneConfig ctrlPlaneConfig) { + public Builder( + @NonNull String gatewayConnectionName, + @NonNull VcnControlPlaneConfig ctrlPlaneConfig) { + Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null"); Objects.requireNonNull(ctrlPlaneConfig, "ctrlPlaneConfig was null"); + mGatewayConnectionName = gatewayConnectionName; mCtrlPlaneConfig = ctrlPlaneConfig; } @@ -562,6 +596,7 @@ public final class VcnGatewayConnectionConfig { @NonNull public VcnGatewayConnectionConfig build() { return new VcnGatewayConnectionConfig( + mGatewayConnectionName, mCtrlPlaneConfig, mExposedCapabilities, mUnderlyingCapabilities, diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index abd41dacdeb6..344b20c036a0 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -437,7 +437,7 @@ public class VcnManager { * Invoked when status of the VCN for this callback's subscription group changes. * * @param statusCode the code for the status change encountered by this {@link - * VcnStatusCallback}'s subscription group. + * VcnStatusCallback}'s subscription group. This value will be one of VCN_STATUS_CODE_*. */ public abstract void onStatusChanged(@VcnStatusCode int statusCode); @@ -445,18 +445,17 @@ public class VcnManager { * Invoked when a VCN Gateway Connection corresponding to this callback's subscription group * encounters an error. * - * @param networkCapabilities an array of NetworkCapabilities.NET_CAPABILITY_* capabilities - * for the Gateway Connection that encountered the error, for identification purposes. - * These will be a sorted list with no duplicates and will match {@link - * VcnGatewayConnectionConfig#getExposedCapabilities()} for one of the {@link - * VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription - * group. - * @param errorCode the code to indicate the error that occurred + * @param gatewayConnectionName the String GatewayConnection name for the GatewayConnection + * encountering an error. This will match the name for exactly one {@link + * VcnGatewayConnectionConfig} for the {@link VcnConfig} configured for this callback's + * subscription group + * @param errorCode the code to indicate the error that occurred. This value will be one of + * VCN_ERROR_CODE_*. * @param detail Throwable to provide additional information about the error, or {@code * null} if none */ public abstract void onGatewayConnectionError( - @NonNull int[] networkCapabilities, + @NonNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable Throwable detail); } @@ -586,7 +585,7 @@ public class VcnManager { // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling' @Override public void onGatewayConnectionError( - @NonNull int[] networkCapabilities, + @NonNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable String exceptionClass, @Nullable String exceptionMessage) { @@ -597,7 +596,7 @@ public class VcnManager { mExecutor.execute( () -> mCallback.onGatewayConnectionError( - networkCapabilities, errorCode, cause))); + gatewayConnectionName, errorCode, cause))); } private static Throwable createThrowableByClassName( diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 2b6f336848c3..033148379041 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -124,6 +124,30 @@ public abstract class BatteryConsumer { public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999; + /** + * Identifiers of models used for power estimation. + * + * @hide + */ + @IntDef(prefix = {"POWER_MODEL_"}, value = { + POWER_MODEL_POWER_PROFILE, + POWER_MODEL_MEASURED_ENERGY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PowerModel { + } + + /** + * Power model that is based on average consumption rates that hardware components + * consume in various states. + */ + public static final int POWER_MODEL_POWER_PROFILE = 0; + + /** + * Power model that is based on energy consumption measured by on-device power monitors. + */ + public static final int POWER_MODEL_MEASURED_ENERGY = 1; + private final PowerComponents mPowerComponents; protected BatteryConsumer(@NonNull PowerComponents powerComponents) { @@ -149,6 +173,16 @@ public abstract class BatteryConsumer { } /** + * Returns the ID of the model that was used for power estimation. + * + * @param componentId The ID of the power component, e.g. + * {@link BatteryConsumer#POWER_COMPONENT_CPU}. + */ + public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) { + return mPowerComponents.getPowerModel(componentId); + } + + /** * Returns the amount of drain attributed to the specified custom drain type. * * @param componentId The ID of the custom power component. @@ -188,9 +222,10 @@ public abstract class BatteryConsumer { protected abstract static class BaseBuilder<T extends BaseBuilder<?>> { final PowerComponents.Builder mPowerComponentsBuilder; - public BaseBuilder(int customPowerComponentCount, int customTimeComponentCount) { + public BaseBuilder(int customPowerComponentCount, int customTimeComponentCount, + boolean includePowerModels) { mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentCount, - customTimeComponentCount); + customTimeComponentCount, includePowerModels); } /** @@ -200,10 +235,23 @@ public abstract class BatteryConsumer { * {@link BatteryConsumer#POWER_COMPONENT_CPU}. * @param componentPower Amount of consumed power in mAh. */ - @SuppressWarnings("unchecked") @NonNull public T setConsumedPower(@PowerComponent int componentId, double componentPower) { - mPowerComponentsBuilder.setConsumedPower(componentId, componentPower); + return setConsumedPower(componentId, componentPower, POWER_MODEL_POWER_PROFILE); + } + + /** + * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc. + * + * @param componentId The ID of the power component, e.g. + * {@link BatteryConsumer#POWER_COMPONENT_CPU}. + * @param componentPower Amount of consumed power in mAh. + */ + @SuppressWarnings("unchecked") + @NonNull + public T setConsumedPower(@PowerComponent int componentId, double componentPower, + @PowerModel int powerModel) { + mPowerComponentsBuilder.setConsumedPower(componentId, componentPower, powerModel); return (T) this; } diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index a0721c32fc2a..f2887748f460 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -74,7 +74,7 @@ public final class BatteryUsageStats implements Parcelable { for (int i = 0; i < systemBatteryConsumerCount; i++) { final SystemBatteryConsumer consumer = builder.mSystemBatteryConsumerBuilders.valueAt(i).build(); - totalPower += consumer.getConsumedPower(); + totalPower += consumer.getConsumedPower() - consumer.getPowerConsumedByApps(); mSystemBatteryConsumers.add(consumer); } @@ -261,6 +261,7 @@ public final class BatteryUsageStats implements Parcelable { public static final class Builder { private final int mCustomPowerComponentCount; private final int mCustomTimeComponentCount; + private final boolean mIncludePowerModels; private long mStatsStartTimestampMs; private int mDischargePercentage; private double mDischargedPowerLowerBoundMah; @@ -277,8 +278,14 @@ public final class BatteryUsageStats implements Parcelable { private List<BatteryStats.HistoryTag> mHistoryTagPool; public Builder(int customPowerComponentCount, int customTimeComponentCount) { + this(customPowerComponentCount, customTimeComponentCount, false); + } + + public Builder(int customPowerComponentCount, int customTimeComponentCount, + boolean includePowerModels) { mCustomPowerComponentCount = customPowerComponentCount; mCustomTimeComponentCount = customTimeComponentCount; + mIncludePowerModels = includePowerModels; } /** @@ -360,7 +367,7 @@ public final class BatteryUsageStats implements Parcelable { UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid); if (builder == null) { builder = new UidBatteryConsumer.Builder(mCustomPowerComponentCount, - mCustomTimeComponentCount, batteryStatsUid); + mCustomTimeComponentCount, mIncludePowerModels, batteryStatsUid); mUidBatteryConsumerBuilders.put(uid, builder); } return builder; @@ -376,7 +383,7 @@ public final class BatteryUsageStats implements Parcelable { SystemBatteryConsumer.Builder builder = mSystemBatteryConsumerBuilders.get(drainType); if (builder == null) { builder = new SystemBatteryConsumer.Builder(mCustomPowerComponentCount, - mCustomTimeComponentCount, drainType); + mCustomTimeComponentCount, mIncludePowerModels, drainType); mSystemBatteryConsumerBuilders.put(drainType, builder); } return builder; @@ -391,7 +398,7 @@ public final class BatteryUsageStats implements Parcelable { UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId); if (builder == null) { builder = new UserBatteryConsumer.Builder(mCustomPowerComponentCount, - mCustomTimeComponentCount, userId); + mCustomTimeComponentCount, mIncludePowerModels, userId); mUserBatteryConsumerBuilders.put(userId, builder); } return builder; diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 85861bc1d2aa..50804422e92f 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -60,6 +60,12 @@ public final class BatteryUsageStatsQuery implements Parcelable { */ public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 2; + /** + * Indicates that identifiers of power models used for computations of power + * consumption should be included in the BatteryUsageStats. + */ + public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS = 4; + private static final long DEFAULT_MAX_STATS_AGE_MS = 5 * 60 * 1000; private final int mFlags; @@ -187,6 +193,17 @@ public final class BatteryUsageStatsQuery implements Parcelable { } /** + * Requests to return identifiers of models that were used for estimation + * of power consumption. + * + * Should only be used for testing and debugging. + */ + public Builder includePowerModels() { + mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS; + return this; + } + + /** * Set the client's tolerance for stale battery stats. The data may be up to * this many milliseconds out-of-date. */ diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a2edc93c6e5e..8adb79b60ffd 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -117,15 +117,20 @@ public class Build { public static final String HARDWARE = getString("ro.hardware"); /** - * The SKU of the hardware (from the kernel command line). The SKU is reported by the bootloader - * to configure system software features. + * The SKU of the hardware (from the kernel command line). + * + * <p>The SKU is reported by the bootloader to configure system software features. + * If no value is supplied by the bootloader, this is reported as {@link #UNKNOWN}. + */ @NonNull public static final String SKU = getString("ro.boot.hardware.sku"); /** - * The SKU of the device as set by the original design manufacturer (ODM). This is a - * runtime-initialized property set during startup to configure device services. + * The SKU of the device as set by the original design manufacturer (ODM). + * + * <p>This is a runtime-initialized property set during startup to configure device + * services. If no value is set, this is reported as {@link #UNKNOWN}. * * <p>The ODM SKU may have multiple variants for the same system SKU in case a manufacturer * produces variants of the same design. For example, the same build may be released with diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index b12dad038ce3..40c658f01e28 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -46,8 +46,6 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; -import android.content.res.AssetFileDescriptor; -import android.net.Uri; import android.provider.DocumentsContract.Document; import android.provider.MediaStore; import android.system.ErrnoException; @@ -83,7 +81,6 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; -import java.util.Locale; import java.util.Objects; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -1446,46 +1443,22 @@ public final class FileUtils { } } - // TODO(b/170488060): Consider better approach /** {@hide} */ @VisibleForTesting - public static FileDescriptor convertToModernFd(FileDescriptor fd) { + public static ParcelFileDescriptor convertToModernFd(FileDescriptor fd) { try { Context context = AppGlobals.getInitialApplication(); - // /mnt/user paths are not accessible directly so convert to a /storage path - String filePath = Os.readlink("/proc/self/fd/" + fd.getInt$()).replace( - "/mnt/user/" + UserHandle.myUserId(), "/storage"); - File realFile = new File(filePath); - String fileName = realFile.getName(); - boolean isCameraVideo = !fileName.startsWith(".") && fileName.endsWith(".mp4") - && contains(CAMERA_DIR_LOWER_CASE, filePath.toLowerCase(Locale.ROOT)); - - if (!SystemProperties.getBoolean("sys.fuse.transcode_enabled", false) - || UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context) - || !isCameraVideo) { - // 1. If transcode is enabled we optimize by default, unless explicitly disabled. - // 2. Never convert modern fd for MediaProvider, because this requires + if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) { + // Never convert modern fd for MediaProvider, because this requires // MediaStore#scanFile and can cause infinite loops when MediaProvider scans - // 3. Only convert published mp4 videos in the DCIM/Camera dir return null; } - Log.i(TAG, "Changing to modern format dataSource for: " + realFile); - ContentResolver resolver = context.getContentResolver(); - - Uri uri = MediaStore.scanFile(resolver, realFile); - if (uri != null) { - Bundle opts = new Bundle(); - opts.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true); - AssetFileDescriptor afd = resolver.openTypedAssetFileDescriptor(uri, "*/*", opts); - Log.i(TAG, "Changed to modern format dataSource for: " + realFile); - return afd.getFileDescriptor(); - } else { - Log.i(TAG, "Failed to change to modern format dataSource for: " + realFile); - } + return MediaStore.getOriginalMediaFormatFileDescriptor(context, + ParcelFileDescriptor.dup(fd)); } catch (Exception e) { - Log.w(TAG, "Failed to change to modern format dataSource", e); + Log.w(TAG, "Failed to convert to modern format file descriptor", e); + return null; } - return null; } private static int getMediaProviderAppId(Context context) { diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index d239b23b6ff6..238f4518b7d3 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -33,12 +33,14 @@ class PowerComponents { private final double[] mPowerComponentsMah; private final long[] mTimeComponentsMs; private final int mCustomPowerComponentCount; + private final byte[] mPowerModels; PowerComponents(@NonNull Builder builder) { mCustomPowerComponentCount = builder.mCustomPowerComponentCount; mPowerComponentsMah = builder.mPowerComponentsMah; mTimeComponentsMs = builder.mTimeComponentsMs; mTotalConsumedPowerMah = builder.getTotalPower(); + mPowerModels = builder.mPowerModels; } PowerComponents(@NonNull Parcel source) { @@ -46,6 +48,12 @@ class PowerComponents { mCustomPowerComponentCount = source.readInt(); mPowerComponentsMah = source.createDoubleArray(); mTimeComponentsMs = source.createLongArray(); + if (source.readBoolean()) { + mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT]; + source.readByteArray(mPowerModels); + } else { + mPowerModels = null; + } } /** Writes contents to Parcel */ @@ -54,6 +62,12 @@ class PowerComponents { dest.writeInt(mCustomPowerComponentCount); dest.writeDoubleArray(mPowerComponentsMah); dest.writeLongArray(mTimeComponentsMs); + if (mPowerModels != null) { + dest.writeBoolean(true); + dest.writeByteArray(mPowerModels); + } else { + dest.writeBoolean(false); + } } /** @@ -103,6 +117,15 @@ class PowerComponents { } } + @BatteryConsumer.PowerModel + int getPowerModel(@BatteryConsumer.PowerComponent int component) { + if (mPowerModels == null) { + throw new IllegalStateException( + "Power model IDs were not requested in the BatteryUsageStatsQuery"); + } + return mPowerModels[component]; + } + /** * Returns the amount of time used by the specified component, e.g. CPU, WiFi etc. * @@ -148,14 +171,21 @@ class PowerComponents { private final double[] mPowerComponentsMah; private final int mCustomPowerComponentCount; private final long[] mTimeComponentsMs; + private final byte[] mPowerModels; - Builder(int customPowerComponentCount, int customTimeComponentCount) { + Builder(int customPowerComponentCount, int customTimeComponentCount, + boolean includePowerModels) { mCustomPowerComponentCount = customPowerComponentCount; int powerComponentCount = BatteryConsumer.POWER_COMPONENT_COUNT + customPowerComponentCount; mPowerComponentsMah = new double[powerComponentCount]; mTimeComponentsMs = new long[BatteryConsumer.TIME_COMPONENT_COUNT + customTimeComponentCount]; + if (includePowerModels) { + mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT]; + } else { + mPowerModels = null; + } } /** @@ -167,7 +197,7 @@ class PowerComponents { */ @NonNull public Builder setConsumedPower(@BatteryConsumer.PowerComponent int componentId, - double componentPower) { + double componentPower, @BatteryConsumer.PowerModel int powerModel) { if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { throw new IllegalArgumentException( "Unsupported power component ID: " + componentId); @@ -178,6 +208,9 @@ class PowerComponents { throw new IllegalArgumentException( "Unsupported power component ID: " + componentId); } + if (mPowerModels != null) { + mPowerModels[componentId] = (byte) powerModel; + } return this; } diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java index 5f35332f4818..e973e4c1044e 100644 --- a/core/java/android/os/SystemBatteryConsumer.java +++ b/core/java/android/os/SystemBatteryConsumer.java @@ -141,8 +141,8 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable private List<UidBatteryConsumer.Builder> mUidBatteryConsumers; Builder(int customPowerComponentCount, int customTimeComponentCount, - @DrainType int drainType) { - super(customPowerComponentCount, customTimeComponentCount); + boolean includePowerModels, @DrainType int drainType) { + super(customPowerComponentCount, customTimeComponentCount, includePowerModels); mDrainType = drainType; } diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java index 841aad556d6a..5d8190213119 100644 --- a/core/java/android/os/SystemVibratorManager.java +++ b/core/java/android/os/SystemVibratorManager.java @@ -199,6 +199,16 @@ public class SystemVibratorManager extends VibratorManager { return mVibratorInfo.hasAmplitudeControl(); } + @Override + public float getResonantFrequency() { + return mVibratorInfo.getResonantFrequency(); + } + + @Override + public float getQFactor() { + return mVibratorInfo.getQFactor(); + } + @NonNull @Override public int[] areEffectsSupported(@NonNull int... effectIds) { diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index dfa0c396485d..87c263b3dd38 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -140,8 +140,8 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela private boolean mExcludeFromBatteryUsageStats; public Builder(int customPowerComponentCount, int customTimeComponentCount, - @NonNull BatteryStats.Uid batteryStatsUid) { - super(customPowerComponentCount, customTimeComponentCount); + boolean includePowerModels, @NonNull BatteryStats.Uid batteryStatsUid) { + super(customPowerComponentCount, customTimeComponentCount, includePowerModels); mBatteryStatsUid = batteryStatsUid; mUid = batteryStatsUid.getUid(); } diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java index 94e567f368b0..78322088a3a4 100644 --- a/core/java/android/os/UserBatteryConsumer.java +++ b/core/java/android/os/UserBatteryConsumer.java @@ -77,8 +77,9 @@ public class UserBatteryConsumer extends BatteryConsumer implements Parcelable { private final int mUserId; private List<UidBatteryConsumer.Builder> mUidBatteryConsumers; - Builder(int customPowerComponentCount, int customTimeComponentCount, int userId) { - super(customPowerComponentCount, customTimeComponentCount); + Builder(int customPowerComponentCount, int customTimeComponentCount, + boolean includePowerModels, int userId) { + super(customPowerComponentCount, customTimeComponentCount, includePowerModels); mUserId = userId; } diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 047c05a8734b..7fb9ae0d86cc 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -25,6 +25,7 @@ import android.content.pm.DataLoaderParams; import android.content.pm.IPackageLoadingProgressCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -285,7 +286,7 @@ public final class IncrementalManager { * Unbinds the target dir and deletes the corresponding storage instance. * Deletes the package name and associated storage id from maps. */ - public void onPackageRemoved(@NonNull File codeFile) { + public void rmPackageDir(@NonNull File codeFile) { try { final String codePath = codeFile.getAbsolutePath(); final IncrementalStorage storage = openStorage(codePath); @@ -294,11 +295,9 @@ public final class IncrementalManager { } mLoadingProgressCallbacks.cleanUpCallbacks(storage); unregisterHealthListener(codePath); - - // Parent since we bind-mount a folder one level above. - mService.deleteBindMount(storage.getId(), codeFile.getParent()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + storage.unBind(codePath); + } catch (IOException e) { + Slog.w(TAG, "Failed to remove code path", e); } } diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 6d677f35b563..66e1c5a93f16 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -48,4 +48,10 @@ oneway interface IPermissionController { void getPrivilegesDescriptionStringForProfile( in String deviceProfileName, in AndroidFuture<String> callback); + void getPlatformPermissionsForGroup( + in String permissionGroupName, + in AndroidFuture<List<String>> callback); + void getGroupOfPlatformPermission( + in String permissionName, + in AndroidFuture<String> callback); } diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 913b827332bf..05eb23ad705d 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -45,6 +45,7 @@ import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Pair; @@ -66,6 +67,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -720,4 +722,46 @@ public final class PermissionControllerManager { mRemoteService.run( service -> service.notifyOneTimePermissionSessionTimeout(packageName)); } + + /** + * Get the platform permissions which belong to a particular permission group + * + * @param permissionGroupName The permission group whose permissions are desired + * @return A list of the platform permissions in the group, or empty if the group is not a valid + * platform group. + */ + public @NonNull Set<String> getPlatformPermissionsForGroup( + @NonNull String permissionGroupName) { + try { + return new ArraySet<>(mRemoteService.postAsync(service -> { + AndroidFuture<List<String>> future = new AndroidFuture<>(); + service.getPlatformPermissionsForGroup(permissionGroupName, future); + return future; + }).get(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)); + } catch (Exception e) { + Log.e(TAG, "Failed to get permissions of " + permissionGroupName, e); + return null; + } + } + + /** + * Get the platform group of a particular permission, if the permission is a platform permission + * + * @param permissionName The permission name whose group is desired + * @return The name of the permission group this permission belongs to, or null if it has no + * group, or is not a platform permission + */ + public @Nullable String getGroupOfPlatformPermission( + @NonNull String permissionName) { + try { + return mRemoteService.postAsync(service -> { + AndroidFuture<String> future = new AndroidFuture<>(); + service.getGroupOfPlatformPermission(permissionName, future); + return future; + }).get(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (Exception e) { + Log.e(TAG, "Failed to get group of " + permissionName, e); + return null; + } + } } diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index ad9e8b3d6dd4..0b99b85fdf24 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -301,6 +301,29 @@ public abstract class PermissionControllerService extends Service { } /** + * Get the platform permissions which belong to a particular permission group + * + * @param permissionGroupName The permission group whose permissions are desired + * @param callback A callback the permission names will be passed to + */ + @BinderThread + public void onGetPlatformPermissionsForGroup(@NonNull String permissionGroupName, + @NonNull Consumer<List<String>> callback) { + throw new AbstractMethodError("Must be overridden in implementing class"); + } + + /** + * Get the platform group of a particular permission, if the permission is a platform permission + * + * @param permissionName The permission name whose group is desired + * @param callback A callback the group name will be passed to + */ + @BinderThread + public void onGetGroupOfPlatformPermission(@NonNull String permissionName, + @NonNull Consumer<String> callback) { + throw new AbstractMethodError("Must be overridden in implementing class"); + } + /** * Get a user-readable sentence, describing the set of privileges that are to be granted to a * companion app managing a device of the given profile. * @@ -563,6 +586,36 @@ public abstract class PermissionControllerService extends Service { callback.completeExceptionally(t); } } + + @Override + public void getPlatformPermissionsForGroup( + @NonNull String permissionName, + @NonNull AndroidFuture<List<String>> callback) { + try { + Objects.requireNonNull(permissionName); + Objects.requireNonNull(callback); + PermissionControllerService.this.onGetPlatformPermissionsForGroup( + permissionName, callback::complete); + } catch (Throwable t) { + callback.completeExceptionally(t); + } + } + + @Override + public void getGroupOfPlatformPermission( + @NonNull String permissionGroupName, + @NonNull AndroidFuture<String> callback) { + try { + Objects.requireNonNull(permissionGroupName); + Objects.requireNonNull(callback); + PermissionControllerService + .this + .onGetGroupOfPlatformPermission( + permissionGroupName, callback::complete); + } catch (Throwable t) { + callback.completeExceptionally(t); + } + } }; } } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 936cbfc70708..751e9aafdf09 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -1114,6 +1114,7 @@ public final class PermissionManager { * * @hide */ + @TestApi public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) { try { return mPermissionManager.registerAttributionSource(source); diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 03b5a2e2568a..d7d1902b9b9f 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -800,10 +800,24 @@ public final class DeviceConfig { } /** - * Reset properties to their default values. + * Reset properties to their default values by removing the underlying values. * <p> * The method accepts an optional namespace parameter. If provided, only properties set within * that namespace will be reset. Otherwise, all properties will be reset. + * <p> + * Note: This method should only be used by {@link com.android.server.RescueParty}. It was + * designed to be used in the event of boot or crash loops caused by flag changes. It does not + * revert flag values to defaults - instead it removes the property entirely which causes the + * consumer of the flag to use hardcoded defaults upon retrieval. + * <p> + * To clear values for a namespace without removing the underlying properties, construct a + * {@link Properties} object with the caller's namespace and either an empty flag map, or some + * snapshot of flag values. Then use {@link #setProperties(Properties)} to remove all flags + * under the namespace, or set them to the values in the snapshot. + * <p> + * To revert values for testing, one should mock DeviceConfig using + * {@link com.android.server.testables.TestableDeviceConfig} where possible. Otherwise, fallback + * to using {@link #setProperties(Properties)} as outlined above. * * @param resetMode The reset mode to use. * @param namespace Optionally, the specific namespace which resets will be limited to. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 94384e9578e8..5093b95b5297 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -471,10 +471,9 @@ public final class Settings { * <p> * Output: Nothing. */ - @SuppressLint("ActionValue") @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = - "com.android.settings.APP_OPEN_BY_DEFAULT_SETTINGS"; + "android.settings.APP_OPEN_BY_DEFAULT_SETTINGS"; /** * Activity Action: Show trusted credentials settings, opening to the user tab, @@ -6756,9 +6755,8 @@ public final class Settings { /** * The current location time zone detection enabled state for the user. * - * See {@link - * android.app.timezonedetector.TimeZoneDetector#getCapabilities} for access. See {@link - * android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update. + * See {@link android.app.time.TimeManager#getTimeZoneCapabilitiesAndConfig} for access. + * See {@link android.app.time.TimeManager#updateTimeZoneConfiguration} to update. * @hide */ public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED = diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java index 232903724d82..d8c44adcc322 100644 --- a/core/java/android/security/ConfirmationPrompt.java +++ b/core/java/android/security/ConfirmationPrompt.java @@ -21,7 +21,6 @@ import android.content.ContentResolver; import android.content.Context; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; -import android.security.keystore.AndroidKeyStoreProvider; import android.text.TextUtils; import android.util.Log; @@ -106,32 +105,6 @@ public class ConfirmationPrompt { private void doCallback(int responseCode, byte[] dataThatWasConfirmed, ConfirmationCallback callback) { switch (responseCode) { - case KeyStore.CONFIRMATIONUI_OK: - callback.onConfirmed(dataThatWasConfirmed); - break; - - case KeyStore.CONFIRMATIONUI_CANCELED: - callback.onDismissed(); - break; - - case KeyStore.CONFIRMATIONUI_ABORTED: - callback.onCanceled(); - break; - - case KeyStore.CONFIRMATIONUI_SYSTEM_ERROR: - callback.onError(new Exception("System error returned by ConfirmationUI.")); - break; - - default: - callback.onError(new Exception("Unexpected responseCode=" + responseCode - + " from onConfirmtionPromptCompleted() callback.")); - break; - } - } - - private void doCallback2(int responseCode, byte[] dataThatWasConfirmed, - ConfirmationCallback callback) { - switch (responseCode) { case AndroidProtectedConfirmation.ERROR_OK: callback.onConfirmed(dataThatWasConfirmed); break; @@ -155,31 +128,6 @@ public class ConfirmationPrompt { } } - private final android.os.IBinder mCallbackBinder = - new android.security.IConfirmationPromptCallback.Stub() { - @Override - public void onConfirmationPromptCompleted( - int responseCode, final byte[] dataThatWasConfirmed) - throws android.os.RemoteException { - if (mCallback != null) { - ConfirmationCallback callback = mCallback; - Executor executor = mExecutor; - mCallback = null; - mExecutor = null; - if (executor == null) { - doCallback(responseCode, dataThatWasConfirmed, callback); - } else { - executor.execute(new Runnable() { - @Override - public void run() { - doCallback(responseCode, dataThatWasConfirmed, callback); - } - }); - } - } - } - }; - private final android.security.apc.IConfirmationCallback mConfirmationCallback = new android.security.apc.IConfirmationCallback.Stub() { @Override @@ -191,11 +139,11 @@ public class ConfirmationPrompt { mCallback = null; mExecutor = null; if (executor == null) { - doCallback2(result, dataThatWasConfirmed, callback); + doCallback(result, dataThatWasConfirmed, callback); } else { executor.execute(new Runnable() { @Override public void run() { - doCallback2(result, dataThatWasConfirmed, callback); + doCallback(result, dataThatWasConfirmed, callback); } }); } @@ -266,29 +214,7 @@ public class ConfirmationPrompt { mExtraData = extraData; } - private static final int UI_OPTION_ACCESSIBILITY_INVERTED_FLAG = 1 << 0; - private static final int UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG = 1 << 1; - private int getUiOptionsAsFlags() { - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - return getUiOptionsAsFlags2(); - } - int uiOptionsAsFlags = 0; - ContentResolver contentResolver = mContext.getContentResolver(); - int inversionEnabled = Settings.Secure.getInt(contentResolver, - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0); - if (inversionEnabled == 1) { - uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG; - } - float fontScale = Settings.System.getFloat(contentResolver, - Settings.System.FONT_SCALE, (float) 1.0); - if (fontScale > 1.0) { - uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG; - } - return uiOptionsAsFlags; - } - - private int getUiOptionsAsFlags2() { int uiOptionsAsFlags = 0; ContentResolver contentResolver = mContext.getContentResolver(); int inversionEnabled = Settings.Secure.getInt(contentResolver, @@ -349,52 +275,26 @@ public class ConfirmationPrompt { mExecutor = executor; String locale = Locale.getDefault().toLanguageTag(); - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - int uiOptionsAsFlags = getUiOptionsAsFlags2(); - int responseCode = getService().presentConfirmationPrompt( - mConfirmationCallback, mPromptText.toString(), mExtraData, locale, - uiOptionsAsFlags); - switch (responseCode) { - case AndroidProtectedConfirmation.ERROR_OK: - return; - - case AndroidProtectedConfirmation.ERROR_OPERATION_PENDING: - throw new ConfirmationAlreadyPresentingException(); - - case AndroidProtectedConfirmation.ERROR_UNIMPLEMENTED: - throw new ConfirmationNotAvailableException(); - - default: - // Unexpected error code. - Log.w(TAG, - "Unexpected responseCode=" + responseCode - + " from presentConfirmationPrompt() call."); - throw new IllegalArgumentException(); - } - } else { - int uiOptionsAsFlags = getUiOptionsAsFlags(); - int responseCode = mKeyStore.presentConfirmationPrompt( - mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags); - switch (responseCode) { - case KeyStore.CONFIRMATIONUI_OK: - return; - - case KeyStore.CONFIRMATIONUI_OPERATION_PENDING: - throw new ConfirmationAlreadyPresentingException(); + int uiOptionsAsFlags = getUiOptionsAsFlags(); + int responseCode = getService().presentConfirmationPrompt( + mConfirmationCallback, mPromptText.toString(), mExtraData, locale, + uiOptionsAsFlags); + switch (responseCode) { + case AndroidProtectedConfirmation.ERROR_OK: + return; - case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED: - throw new ConfirmationNotAvailableException(); + case AndroidProtectedConfirmation.ERROR_OPERATION_PENDING: + throw new ConfirmationAlreadyPresentingException(); - case KeyStore.CONFIRMATIONUI_UIERROR: - throw new IllegalArgumentException(); + case AndroidProtectedConfirmation.ERROR_UNIMPLEMENTED: + throw new ConfirmationNotAvailableException(); - default: - // Unexpected error code. - Log.w(TAG, - "Unexpected responseCode=" + responseCode - + " from presentConfirmationPrompt() call."); - throw new IllegalArgumentException(); - } + default: + // Unexpected error code. + Log.w(TAG, + "Unexpected responseCode=" + responseCode + + " from presentConfirmationPrompt() call."); + throw new IllegalArgumentException(); } } @@ -408,33 +308,18 @@ public class ConfirmationPrompt { * @throws IllegalStateException if no prompt is currently being presented. */ public void cancelPrompt() { - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - int responseCode = - getService().cancelConfirmationPrompt(mConfirmationCallback); - if (responseCode == AndroidProtectedConfirmation.ERROR_OK) { - return; - } else if (responseCode == AndroidProtectedConfirmation.ERROR_OPERATION_PENDING) { - throw new IllegalStateException(); - } else { - // Unexpected error code. - Log.w(TAG, - "Unexpected responseCode=" + responseCode - + " from cancelConfirmationPrompt() call."); - throw new IllegalStateException(); - } + int responseCode = + getService().cancelConfirmationPrompt(mConfirmationCallback); + if (responseCode == AndroidProtectedConfirmation.ERROR_OK) { + return; + } else if (responseCode == AndroidProtectedConfirmation.ERROR_OPERATION_PENDING) { + throw new IllegalStateException(); } else { - int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder); - if (responseCode == KeyStore.CONFIRMATIONUI_OK) { - return; - } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) { - throw new IllegalStateException(); - } else { - // Unexpected error code. - Log.w(TAG, - "Unexpected responseCode=" + responseCode - + " from cancelConfirmationPrompt() call."); - throw new IllegalStateException(); - } + // Unexpected error code. + Log.w(TAG, + "Unexpected responseCode=" + responseCode + + " from cancelConfirmationPrompt() call."); + throw new IllegalStateException(); } } @@ -448,9 +333,6 @@ public class ConfirmationPrompt { if (isAccessibilityServiceRunning(context)) { return false; } - if (AndroidKeyStoreProvider.isKeystore2Enabled()) { - return new AndroidProtectedConfirmation().isConfirmationPromptSupported(); - } - return KeyStore.getInstance().isConfirmationPromptSupported(); + return new AndroidProtectedConfirmation().isConfirmationPromptSupported(); } } diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl new file mode 100644 index 000000000000..e01db7acce99 --- /dev/null +++ b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keymaster; + +parcelable KeymasterCertificateChain; diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index 6788353ba2fb..ad9b79606455 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -712,18 +712,10 @@ public class RecoveryController { */ @NonNull Key getKeyFromGrant(@NonNull String grantAlias) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - if (grantAlias.startsWith(APPLICATION_KEY_GRANT_PREFIX)) { - return AndroidKeyStoreProvider - .loadAndroidKeyStoreSecretKeyFromKeystore( - KeyStore2.getInstance(), - getGrantDescriptor(grantAlias)); - } - // TODO(b/171305545): remove KeyStore1 logic. - return android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( - mKeyStore, - grantAlias, - KeyStore.UID_SELF); - + return AndroidKeyStoreProvider + .loadAndroidKeyStoreSecretKeyFromKeystore( + KeyStore2.getInstance(), + getGrantDescriptor(grantAlias)); } private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:"; diff --git a/core/java/android/service/notification/NotificationListenerFilter.java b/core/java/android/service/notification/NotificationListenerFilter.java index 9de75cac159a..a69d33c17e9d 100644 --- a/core/java/android/service/notification/NotificationListenerFilter.java +++ b/core/java/android/service/notification/NotificationListenerFilter.java @@ -31,15 +31,17 @@ import android.util.ArraySet; * @hide */ public class NotificationListenerFilter implements Parcelable { + + private static final int DEFAULT_TYPES = FLAG_FILTER_TYPE_CONVERSATIONS + | FLAG_FILTER_TYPE_ALERTING + | FLAG_FILTER_TYPE_SILENT + | FLAG_FILTER_TYPE_ONGOING; private int mAllowedNotificationTypes; // VersionedPackage is holding the pkg name and pkg uid private ArraySet<VersionedPackage> mDisallowedPackages; public NotificationListenerFilter() { - mAllowedNotificationTypes = FLAG_FILTER_TYPE_CONVERSATIONS - | FLAG_FILTER_TYPE_ALERTING - | FLAG_FILTER_TYPE_SILENT - | FLAG_FILTER_TYPE_ONGOING; + mAllowedNotificationTypes = DEFAULT_TYPES; mDisallowedPackages = new ArraySet<>(); } @@ -80,6 +82,10 @@ public class NotificationListenerFilter implements Parcelable { return (mAllowedNotificationTypes & type) != 0; } + public boolean areAllTypesAllowed() { + return DEFAULT_TYPES == mAllowedNotificationTypes; + } + public boolean isPackageAllowed(VersionedPackage pkg) { return !mDisallowedPackages.contains(pkg); } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 09c3b2effcb0..6c775852fcee 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -792,6 +792,29 @@ public abstract class NotificationListenerService extends Service { } } + /** + * Lets an app migrate notification filters from its app into the OS. + * + * <p>This call will be ignored if the app has already migrated these settings or the user + * has set filters in the UI. This method is intended for user specific settings; if an app has + * already specified defaults types in its manifest with + * {@link #META_DATA_DEFAULT_FILTER_TYPES}, the defaultTypes option will be ignored.</p> + * @param defaultTypes A value representing the types of notifications that this listener should + * receive by default + * @param disallowedPkgs A list of package names whose notifications should not be seen by this + * listener, by default, because the listener does not process or display them, or because a + * user had previously disallowed these packages in the listener app's UI + */ + public final void migrateNotificationFilter(@NotificationFilterTypes int defaultTypes, + @Nullable List<String> disallowedPkgs) { + if (!isBound()) return; + try { + getNotificationInterface().migrateNotificationFilter( + mWrapper, defaultTypes, disallowedPkgs); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } /** * Inform the notification manager that these notifications have been viewed by the diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java index 8dec0922b097..8c7db4003235 100644 --- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java +++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java @@ -50,7 +50,7 @@ public final class RotationResolutionRequest implements Parcelable { /** The proposed screen rotation in the system. */ @Surface.Rotation private final int mProposedRotation; - /** Whether should use camera signal to resolver rotation. */ + /** Whether should use the camera sensor to resolve screen rotation. */ private final boolean mShouldUseCamera; /** The timeout of the request. */ @@ -59,7 +59,7 @@ public final class RotationResolutionRequest implements Parcelable { - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -82,7 +82,7 @@ public final class RotationResolutionRequest implements Parcelable { * @param proposedRotation * The proposed screen rotation in the system. * @param shouldUseCamera - * Whether should use camera signal to resolver rotation. + * Whether should use the camera sensor to resolve screen rotation. * @param timeoutMillis * The timeout of the request. */ @@ -135,7 +135,7 @@ public final class RotationResolutionRequest implements Parcelable { } /** - * Whether should use camera signal to resolver rotation. + * Whether should use the camera sensor to resolve screen rotation. */ @DataClass.Generated.Member public boolean shouldUseCamera() { @@ -230,8 +230,8 @@ public final class RotationResolutionRequest implements Parcelable { }; @DataClass.Generated( - time = 1615402421314L, - codegenVersion = "1.0.22", + time = 1617213094231L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/rotationresolver/RotationResolutionRequest.java", inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.view.Surface.Rotation int mCurrentRotation\nprivate final @android.view.Surface.Rotation int mProposedRotation\nprivate final boolean mShouldUseCamera\nprivate final @android.annotation.DurationMillisLong long mTimeoutMillis\nclass RotationResolutionRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true)") @Deprecated diff --git a/core/java/android/speech/OWNERS b/core/java/android/speech/OWNERS new file mode 100644 index 000000000000..32f482264103 --- /dev/null +++ b/core/java/android/speech/OWNERS @@ -0,0 +1,3 @@ +volnov@google.com +eugeniom@google.com +schfan@google.com diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index 0cbcb16762f8..42e9bfdae809 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -214,6 +214,17 @@ public class SpeechRecognizer { * command to the created {@code SpeechRecognizer}, otherwise no notifications will be * received. * + * <p>For apps targeting Android 11 (API level 30) interaction with a speech recognition + * service requires <queries> element to be added to the manifest file: + * <pre>{@code + * <queries> + * <intent> + * <action + * android:name="android.speech.RecognitionService" /> + * </intent> + * </queries> + * }</pre> + * * @param context in which to create {@code SpeechRecognizer} * @return a new {@code SpeechRecognizer} */ @@ -231,7 +242,18 @@ public class SpeechRecognizer { * {@link SpeechRecognizer} to. Normally you would not use this; use * {@link #createSpeechRecognizer(Context)} instead to use the system default recognition * service. - * + * + * <p>For apps targeting Android 11 (API level 30) interaction with a speech recognition + * service requires <queries> element to be added to the manifest file: + * <pre>{@code + * <queries> + * <intent> + * <action + * android:name="android.speech.RecognitionService" /> + * </intent> + * </queries> + * }</pre> + * * @param context in which to create {@code SpeechRecognizer} * @param serviceComponent the {@link ComponentName} of a specific service to direct this * {@code SpeechRecognizer} to diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java index 54c0ffcdbb65..7db999a89fbe 100644 --- a/core/java/android/text/method/TranslationTransformationMethod.java +++ b/core/java/android/text/method/TranslationTransformationMethod.java @@ -78,11 +78,24 @@ public class TranslationTransformationMethod implements TransformationMethod2 { if (TextUtils.isEmpty(translatedText) || isWhitespace(translatedText.toString())) { return source; } else { + // TODO(b/179693024): Remove this once we have the fix to pad the view text instead. + translatedText = ellipsize(translatedText, ((TextView) view).getText().length()); // TODO(b/174283799): apply the spans to the text return translatedText; } } + private static CharSequence ellipsize(CharSequence text, int newLength) { + if (text.length() <= newLength) { + return text; + } + String ellipsis = String.valueOf('\u2026'); + if (newLength == 1) { + return ellipsis; + } + return TextUtils.concat(TextUtils.trimToSize(text, newLength - 1), ellipsis); + } + @Override public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 913ceae63a94..572e50a8ce34 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -70,7 +70,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_tether_all_in_one", "false"); DEFAULT_FLAGS.put("settings_silky_home", "true"); DEFAULT_FLAGS.put("settings_contextual_home", "false"); - DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false"); + DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true"); DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "false"); } diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java index 2a43222b9e48..78c4739968e3 100644 --- a/core/java/android/util/Slog.java +++ b/core/java/android/util/Slog.java @@ -61,7 +61,10 @@ public final class Slog { * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're * calling this method in a critical path, make sure to explicitly do the check before calling * it. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void v(String tag, String format, @Nullable Object... args) { if (!Log.isLoggable(tag, Log.VERBOSE)) return; @@ -87,7 +90,10 @@ public final class Slog { * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're * calling this method in a critical path, make sure to explicitly do the check before calling * it. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void d(String tag, String format, @Nullable Object... args) { if (!Log.isLoggable(tag, Log.DEBUG)) return; @@ -112,7 +118,10 @@ public final class Slog { * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're * calling this method in a critical path, make sure to explicitly do the check before calling * it. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void i(String tag, String format, @Nullable Object... args) { if (!Log.isLoggable(tag, Log.INFO)) return; @@ -142,7 +151,10 @@ public final class Slog { * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're * calling this method in a critical path, make sure to explicitly do the check before calling * it. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void w(String tag, String format, @Nullable Object... args) { if (!Log.isLoggable(tag, Log.WARN)) return; @@ -157,7 +169,10 @@ public final class Slog { * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're * calling this method in a critical path, make sure to explicitly do the check before calling * it. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void w(String tag, Exception exception, String format, @Nullable Object... args) { if (!Log.isLoggable(tag, Log.WARN)) return; @@ -183,7 +198,10 @@ public final class Slog { * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're * calling this method in a critical path, make sure to explicitly do the check before calling * it. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void e(String tag, String format, @Nullable Object... args) { if (!Log.isLoggable(tag, Log.ERROR)) return; @@ -198,7 +216,10 @@ public final class Slog { * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're * calling this method in a critical path, make sure to explicitly do the check before calling * it. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void e(String tag, Exception exception, String format, @Nullable Object... args) { if (!Log.isLoggable(tag, Log.ERROR)) return; @@ -217,14 +238,20 @@ public final class Slog { /** * Logs a {@code wtf} message. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void wtf(String tag, String format, @Nullable Object... args) { wtf(tag, getMessage(format, args)); } /** * Logs a {@code wtf} message with an exception. + * + * @deprecated use {@code com.android.server.utils.SLogF} instead. */ + @Deprecated public static void wtf(String tag, Exception exception, String format, @Nullable Object... args) { wtf(tag, getMessage(format, args), exception); diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index cd6585cd3fa1..5fd0c33ca205 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -30,6 +30,7 @@ import com.android.i18n.timezone.ZoneInfoDb; import java.io.PrintWriter; import java.text.SimpleDateFormat; +import java.time.Instant; import java.time.LocalTime; import java.util.ArrayList; import java.util.Calendar; @@ -43,11 +44,39 @@ import java.util.List; public class TimeUtils { /** @hide */ public TimeUtils() {} /** {@hide} */ - private static SimpleDateFormat sLoggingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static final SimpleDateFormat sLoggingFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** @hide */ public static final SimpleDateFormat sDumpDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + /** + * This timestamp is used in TimeUtils methods and by the SettingsUI to filter time zones + * to only "effective" ones in a country. It is compared against the notUsedAfter metadata that + * Android records for some time zones. + * + * <p>What is notUsedAfter?</p> + * Android chooses to avoid making users choose between functionally identical time zones at the + * expense of not being able to represent local times in the past. + * + * notUsedAfter exists because some time zones can "merge" with other time zones after a given + * point in time (i.e. they change to have identical transitions, offsets, display names, etc.). + * From the notUsedAfter time, the zone will express the same local time as the one it merged + * with. + * + * <p>Why hardcoded?</p> + * Rather than using System.currentTimeMillis(), a timestamp known to be in the recent past is + * used to ensure consistent behavior across devices and time, and avoid assumptions that the + * system clock on a device is currently set correctly. The fixed value should be updated + * occasionally, but it doesn't have to be very often as effective time zones for a country + * don't change very often. + * + * @hide + */ + public static final Instant MIN_USE_DATE_OF_TIMEZONE = + Instant.ofEpochMilli(1546300800000L); // 1/1/2019 00:00 UTC + /** * Tries to return a time zone that would have had the specified offset * and DST value at the specified moment in the specified country. @@ -109,7 +138,7 @@ public class TimeUtils { List<String> timeZoneIds = new ArrayList<>(); for (TimeZoneMapping timeZoneMapping : countryTimeZones.getTimeZoneMappings()) { - if (timeZoneMapping.isShownInPicker()) { + if (timeZoneMapping.isShownInPickerAt(MIN_USE_DATE_OF_TIMEZONE)) { timeZoneIds.add(timeZoneMapping.getTimeZoneId()); } } diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java index 85f2c1ccc180..5c7f0f5cb1e0 100644 --- a/core/java/android/uwb/RangingManager.java +++ b/core/java/android/uwb/RangingManager.java @@ -17,6 +17,7 @@ package android.uwb; import android.annotation.NonNull; +import android.content.AttributionSource; import android.os.CancellationSignal; import android.os.PersistableBundle; import android.os.RemoteException; @@ -42,6 +43,9 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { /** * Open a new ranging session * + * @param attributionSource Attribution source to use for the enforcement of + * {@link android.Manifest.permission#ULTRAWIDEBAND_RANGING} runtime + * permission. * @param params the parameters that define the ranging session * @param executor {@link Executor} to run callbacks * @param callbacks {@link RangingSession.Callback} to associate with the {@link RangingSession} @@ -49,7 +53,8 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { * @return a {@link CancellationSignal} that may be used to cancel the opening of the * {@link RangingSession}. */ - public CancellationSignal openSession(@NonNull PersistableBundle params, + public CancellationSignal openSession(@NonNull AttributionSource attributionSource, + @NonNull PersistableBundle params, @NonNull Executor executor, @NonNull RangingSession.Callback callbacks) { synchronized (this) { @@ -58,6 +63,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { new RangingSession(executor, callbacks, mAdapter, sessionHandle); mRangingSessionTable.put(sessionHandle, session); try { + // TODO: Pass in the attributionSource to the service. mAdapter.openRanging(sessionHandle, this, params); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 844bbbe7970b..bed77e664337 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -16,7 +16,7 @@ package android.uwb; -import android.Manifest; +import android.Manifest.permission; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; @@ -24,7 +24,9 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.content.AttributionSource; import android.content.Context; +import android.content.ContextParams; import android.os.CancellationSignal; import android.os.IBinder; import android.os.PersistableBundle; @@ -47,9 +49,10 @@ import java.util.concurrent.Executor; @SystemApi @SystemService(Context.UWB_SERVICE) public final class UwbManager { - private IUwbAdapter mUwbAdapter; private static final String SERVICE_NAME = "uwb"; + private final Context mContext; + private final IUwbAdapter mUwbAdapter; private final AdapterStateListener mAdapterStateListener; private final RangingManager mRangingManager; @@ -116,9 +119,11 @@ public final class UwbManager { /** * Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance. * + * @param ctx Context of the client. * @param adapter an instance of an {@link android.uwb.IUwbAdapter} */ - private UwbManager(IUwbAdapter adapter) { + private UwbManager(@NonNull Context ctx, @NonNull IUwbAdapter adapter) { + mContext = ctx; mUwbAdapter = adapter; mAdapterStateListener = new AdapterStateListener(adapter); mRangingManager = new RangingManager(adapter); @@ -127,7 +132,7 @@ public final class UwbManager { /** * @hide */ - public static UwbManager getInstance() { + public static UwbManager getInstance(@NonNull Context ctx) { IBinder b = ServiceManager.getService(SERVICE_NAME); if (b == null) { return null; @@ -138,7 +143,7 @@ public final class UwbManager { return null; } - return new UwbManager(adapter); + return new UwbManager(ctx, adapter); } /** @@ -153,7 +158,7 @@ public final class UwbManager { * @param executor an {@link Executor} to execute given callback * @param callback user implementation of the {@link AdapterStateCallback} */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) + @RequiresPermission(permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor, @NonNull AdapterStateCallback callback) { mAdapterStateListener.register(executor, callback); @@ -168,7 +173,7 @@ public final class UwbManager { * * @param callback user implementation of the {@link AdapterStateCallback} */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) + @RequiresPermission(permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) { mAdapterStateListener.unregister(callback); } @@ -182,7 +187,7 @@ public final class UwbManager { * @return {@link PersistableBundle} of the device's supported UWB protocols and parameters */ @NonNull - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) + @RequiresPermission(permission.UWB_PRIVILEGED) public PersistableBundle getSpecificationInfo() { try { return mUwbAdapter.getSpecificationInfo(); @@ -199,7 +204,7 @@ public final class UwbManager { * @return the timestamp resolution in nanoseconds */ @SuppressLint("MethodNameUnits") - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) + @RequiresPermission(permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos() { try { return mUwbAdapter.getTimestampResolutionNanos(); @@ -235,10 +240,14 @@ public final class UwbManager { * {@link RangingSession.Callback#onOpened(RangingSession)}. */ @NonNull - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) + @RequiresPermission(allOf = { + permission.UWB_PRIVILEGED, + permission.UWB_RANGING + }) public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters, @NonNull @CallbackExecutor Executor executor, @NonNull RangingSession.Callback callbacks) { - return mRangingManager.openSession(parameters, executor, callbacks); + return mRangingManager.openSession( + mContext.getAttributionSource(), parameters, executor, callbacks); } } diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java index c62e93463048..9f63500fc853 100644 --- a/core/java/android/view/HapticFeedbackConstants.java +++ b/core/java/android/view/HapticFeedbackConstants.java @@ -129,6 +129,12 @@ public class HapticFeedbackConstants { public static final int SAFE_MODE_ENABLED = 10001; /** + * Invocation of the voice assistant via hardware button. + * @hide + */ + public static final int ASSISTANT_BUTTON = 10002; + + /** * Flag for {@link View#performHapticFeedback(int, int) * View.performHapticFeedback(int, int)}: Ignore the setting in the * view for whether to perform haptic feedback, do it always. diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 0812cdcb7c0a..f28242aa8215 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -55,6 +55,8 @@ public abstract class InputEventReceiver { InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(long receiverPtr); private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled); + private static native void nativeReportTimeline(long receiverPtr, int inputEventId, + long gpuCompletedTime, long presentTime); private static native boolean nativeConsumeBatchedInputEvents(long receiverPtr, long frameTimeNanos); private static native String nativeDump(long receiverPtr, String prefix); @@ -210,11 +212,11 @@ public abstract class InputEventReceiver { } /** - * Report the latency information for a specific input event. + * Report the timing / latency information for a specific input event. */ - public final void reportLatencyInfo(int inputEventId, long gpuCompletedTime, long presentTime) { - Trace.traceBegin(Trace.TRACE_TAG_INPUT, "reportLatencyInfo"); - // TODO(b/169866723) : send this data to InputDispatcher via InputChannel + public final void reportTimeline(int inputEventId, long gpuCompletedTime, long presentTime) { + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "reportTimeline"); + nativeReportTimeline(mReceiverPtr, inputEventId, gpuCompletedTime, presentTime); Trace.traceEnd(Trace.TRACE_TAG_INPUT); } diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java index 40eb438262a1..d14421897860 100644 --- a/core/java/android/view/InputEventSender.java +++ b/core/java/android/view/InputEventSender.java @@ -112,6 +112,16 @@ public abstract class InputEventSender { } /** + * Called when timeline is sent to the publisher. + * + * @param inputEventId The id of the input event that caused the frame being reported + * @param gpuCompletedTime The time when the frame left the app process + * @param presentTime The time when the frame was presented on screen + */ + public void onTimelineReported(int inputEventId, long gpuCompletedTime, long presentTime) { + } + + /** * Sends an input event. * Must be called on the same Looper thread to which the sender is attached. * @@ -143,4 +153,11 @@ public abstract class InputEventSender { private void dispatchInputEventFinished(int seq, boolean handled) { onInputEventFinished(seq, handled); } + + // Called from native code. + @SuppressWarnings("unused") + private void dispatchTimelineReported( + int inputEventId, long gpuCompletedTime, long presentTime) { + onTimelineReported(inputEventId, gpuCompletedTime, presentTime); + } } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 3d1c8ff515c7..f487c6c61973 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.graphics.Insets; @@ -808,7 +809,7 @@ public class InsetsState implements Parcelable { dest.writeTypedObject(mRoundedCorners, flags); } - public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() { + public static final @NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() { public InsetsState createFromParcel(Parcel in) { return new InsetsState(in); @@ -842,5 +843,16 @@ public class InsetsState implements Parcelable { + ", mSources= { " + joiner + " }"; } + + public @NonNull String toSourceVisibilityString() { + StringJoiner joiner = new StringJoiner(", "); + for (int i = 0; i < SIZE; i++) { + InsetsSource source = mSources[i]; + if (source != null) { + joiner.add(typeToString(i) + ": " + (source.isVisible() ? "visible" : "invisible")); + } + } + return joiner.toString(); + } } diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index 4a1d68547f70..cdf1e46ac5db 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -59,6 +59,10 @@ per-file ViewRootImpl.java = file:/services/core/java/com/android/server/input/O per-file ViewRootImpl.java = file:/services/core/java/com/android/server/wm/OWNERS per-file ViewRootImpl.java = file:/core/java/android/view/inputmethod/OWNERS per-file AccessibilityInteractionController.java = file:/services/accessibility/OWNERS +per-file OnReceiveContentListener.java = file:/core/java/android/service/autofill/OWNERS +per-file OnReceiveContentListener.java = file:/core/java/android/widget/OWNERS +per-file ContentInfo.java = file:/core/java/android/service/autofill/OWNERS +per-file ContentInfo.java = file:/core/java/android/widget/OWNERS # WindowManager per-file DisplayCutout.aidl = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index 14dcdad8e2b5..cdc099b8e2ea 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -34,7 +34,8 @@ import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import android.annotation.IntDef; -import android.app.PictureInPictureParams; +import android.app.ActivityManager; +import android.app.TaskInfo; import android.app.WindowConfiguration; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Point; @@ -189,12 +190,11 @@ public class RemoteAnimationTarget implements Parcelable { public boolean isNotInRecents; /** - * {@link PictureInPictureParams} to allow launcher to determine if an app should - * automatically enter PiP on swiping up to home. + * {@link TaskInfo} to allow the controller to identify information about the task. * * TODO: add this to proto dump */ - public PictureInPictureParams pictureInPictureParams; + public ActivityManager.RunningTaskInfo taskInfo; /** * The {@link android.view.WindowManager.LayoutParams.WindowType} of this window. It's only used @@ -206,11 +206,10 @@ public class RemoteAnimationTarget implements Parcelable { Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, Rect localBounds, Rect screenSpaceBounds, WindowConfiguration windowConfig, boolean isNotInRecents, - SurfaceControl startLeash, Rect startBounds, - PictureInPictureParams pictureInPictureParams) { + SurfaceControl startLeash, Rect startBounds, ActivityManager.RunningTaskInfo taskInfo) { this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex, position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash, - startBounds, pictureInPictureParams, INVALID_WINDOW_TYPE); + startBounds, taskInfo, INVALID_WINDOW_TYPE); } public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, @@ -218,7 +217,7 @@ public class RemoteAnimationTarget implements Parcelable { Rect localBounds, Rect screenSpaceBounds, WindowConfiguration windowConfig, boolean isNotInRecents, SurfaceControl startLeash, Rect startBounds, - PictureInPictureParams pictureInPictureParams, + ActivityManager.RunningTaskInfo taskInfo, @WindowManager.LayoutParams.WindowType int windowType) { this.mode = mode; this.taskId = taskId; @@ -235,7 +234,7 @@ public class RemoteAnimationTarget implements Parcelable { this.isNotInRecents = isNotInRecents; this.startLeash = startLeash; this.startBounds = startBounds == null ? null : new Rect(startBounds); - this.pictureInPictureParams = pictureInPictureParams; + this.taskInfo = taskInfo; this.windowType = windowType; } @@ -255,7 +254,7 @@ public class RemoteAnimationTarget implements Parcelable { isNotInRecents = in.readBoolean(); startLeash = in.readTypedObject(SurfaceControl.CREATOR); startBounds = in.readTypedObject(Rect.CREATOR); - pictureInPictureParams = in.readTypedObject(PictureInPictureParams.CREATOR); + taskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR); windowType = in.readInt(); } @@ -281,7 +280,7 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeBoolean(isNotInRecents); dest.writeTypedObject(startLeash, 0 /* flags */); dest.writeTypedObject(startBounds, 0 /* flags */); - dest.writeTypedObject(pictureInPictureParams, 0 /* flags */); + dest.writeTypedObject(taskInfo, 0 /* flags */); dest.writeInt(windowType); } @@ -299,7 +298,7 @@ public class RemoteAnimationTarget implements Parcelable { pw.println(); pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration); pw.print(prefix); pw.print("leash="); pw.println(leash); - pw.print(prefix); pw.print("pictureInPictureParams="); pw.println(pictureInPictureParams); + pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo); pw.print(prefix); pw.print("windowType="); pw.print(windowType); } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 65b8afa4e27f..48a0e80d9464 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -99,7 +99,7 @@ public class Surface implements Parcelable { private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled); private static native int nativeSetFrameRate( - long nativeObject, float frameRate, int compatibility, boolean shouldBeSeamless); + long nativeObject, float frameRate, int compatibility, int changeFrameRateStrategy); public static final @android.annotation.NonNull Parcelable.Creator<Surface> CREATOR = new Parcelable.Creator<Surface>() { @@ -228,6 +228,26 @@ public class Surface implements Parcelable { */ public static final int FRAME_RATE_COMPATIBILITY_EXACT = 100; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CHANGE_FRAME_RATE_"}, + value = {CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, CHANGE_FRAME_RATE_ALWAYS}) + public @interface ChangeFrameRateStrategy {} + + /** + * Change the frame rate only if the transition is going to be seamless. + */ + public static final int CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0; + + /** + * Change the frame rate even if the transition is going to be non-seamless, i.e. with visual + * interruptions for the user. Non-seamless switches might be used when the benefit of matching + * the content's frame rate outweighs the cost of the transition, for example when + * displaying long-running video content. + */ + public static final int CHANGE_FRAME_RATE_ALWAYS = 1; + /** * Create an empty surface, which will later be filled in by readFromParcel(). * @hide @@ -945,25 +965,21 @@ public class Surface implements Parcelable { * the display at 60fps. * * @param compatibility The frame rate compatibility of this surface. The - * compatibility value may influence the system's choice of display frame rate. See - * the FRAME_RATE_COMPATIBILITY_* values for more info. + * compatibility value may influence the system's choice of display frame rate. * - * @param shouldBeSeamless Whether display refresh rate transitions should be seamless. A + * @param changeFrameRateStrategy Whether display refresh rate transitions should be seamless. A * seamless transition is one that doesn't have any visual interruptions, such as a black - * screen for a second or two. True indicates that any frame rate changes caused by this - * request should be seamless. False indicates that non-seamless refresh rates are also - * acceptable. Non-seamless switches might be used when the benefit of matching the content's - * frame rate outweighs the cost of the transition, for example when displaying - * long-running video content. + * screen for a second or two. * * @throws IllegalArgumentException If frameRate or compatibility are invalid. */ public void setFrameRate(@FloatRange(from = 0.0) float frameRate, - @FrameRateCompatibility int compatibility, boolean shouldBeSeamless) { + @FrameRateCompatibility int compatibility, + @ChangeFrameRateStrategy int changeFrameRateStrategy) { synchronized (mLock) { checkNotReleasedLocked(); int error = nativeSetFrameRate(mNativeObject, frameRate, compatibility, - shouldBeSeamless); + changeFrameRateStrategy); if (error == -EINVAL) { throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()"); } else if (error != 0) { @@ -976,11 +992,11 @@ public class Surface implements Parcelable { * Sets the intended frame rate for this surface. Any switching of refresh rates is * most probably going to be seamless. * - * @see #setFrameRate(float, int, boolean) + * @see #setFrameRate(float, int, int) */ public void setFrameRate( @FloatRange(from = 0.0) float frameRate, @FrameRateCompatibility int compatibility) { - setFrameRate(frameRate, compatibility, /* shouldBeSeamless = */ true); + setFrameRate(frameRate, compatibility, CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); } /** diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index fd8bdfd5b0b0..7a9cfd4737ab 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -211,7 +211,7 @@ public final class SurfaceControl implements Parcelable { @Size(4) float[] spotColor, float lightPosY, float lightPosZ, float lightRadius); private static native void nativeSetFrameRate(long transactionObj, long nativeObject, - float frameRate, int compatibility, boolean shouldBeSeamless); + float frameRate, int compatibility, int changeFrameRateStrategy); private static native long nativeGetHandle(long nativeObject); private static native long nativeAcquireFrameRateFlexibilityToken(); @@ -3268,13 +3268,14 @@ public final class SurfaceControl implements Parcelable { * Sets the intended frame rate for this surface. Any switching of refresh rates is * most probably going to be seamless. * - * @see #setFrameRate(SurfaceControl, float, int, boolean) + * @see #setFrameRate(SurfaceControl, float, int, int) */ @NonNull public Transaction setFrameRate(@NonNull SurfaceControl sc, @FloatRange(from = 0.0) float frameRate, @Surface.FrameRateCompatibility int compatibility) { - return setFrameRate(sc, frameRate, compatibility, /*shouldBeSeamless*/ true); + return setFrameRate(sc, frameRate, compatibility, + Surface.CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); } /** @@ -3295,27 +3296,21 @@ public final class SurfaceControl implements Parcelable { * refresh rate for this device's display - e.g., it's fine to pass 30fps * to a device that can only run the display at 60fps. * @param compatibility The frame rate compatibility of this surface. The compatibility - * value may influence the system's choice of display frame rate. See - * the Surface.FRAME_RATE_COMPATIBILITY_* values for more info. - * @param shouldBeSeamless Whether display refresh rate transitions should be seamless. A - * seamless transition is one that doesn't have any visual - * interruptions, such as a black screen for a second or two. True - * indicates that any frame rate changes caused by this request - * should be seamless. False indicates that non-seamless refresh - * rates are also acceptable. Non-seamless switches might be - * used when the benefit of matching the content's frame rate - * outweighs the cost of the transition, for example when - * displaying long-running video content. + * value may influence the system's choice of display frame rate. + * @param changeFrameRateStrategy Whether display refresh rate transitions should be + * seamless. A seamless transition is one that doesn't have + * any visual interruptions, such as a black screen for a + * second or two. * @return This transaction object. */ @NonNull public Transaction setFrameRate(@NonNull SurfaceControl sc, @FloatRange(from = 0.0) float frameRate, @Surface.FrameRateCompatibility int compatibility, - boolean shouldBeSeamless) { + @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy) { checkPreconditions(sc); nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate, compatibility, - shouldBeSeamless); + changeFrameRateStrategy); return this; } diff --git a/core/java/android/view/SurfaceControlHdrLayerInfoListener.java b/core/java/android/view/SurfaceControlHdrLayerInfoListener.java new file mode 100644 index 000000000000..13d68d021e31 --- /dev/null +++ b/core/java/android/view/SurfaceControlHdrLayerInfoListener.java @@ -0,0 +1,102 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.RequiresPermission; +import android.os.IBinder; +import android.util.ArrayMap; + +import libcore.util.NativeAllocationRegistry; + +import java.util.Objects; + +/** + * Allows for the monitoring of layers with HDR content + * + * @hide */ +public abstract class SurfaceControlHdrLayerInfoListener { + private static final NativeAllocationRegistry sRegistry = + NativeAllocationRegistry.createMalloced( + SurfaceControlHdrLayerInfoListener.class.getClassLoader(), nGetDestructor()); + + /** + * Callback when the HDR information about the given display has changed + * + * @param displayToken The display this callback is about + * @param numberOfHdrLayers How many HDR layers are visible on the display + * @param maxW The width of the HDR layer with the largest area + * @param maxH The height of the HDR layer with the largest area + * @param flags Additional metadata flags, currently always 0 + * TODO(b/182312559): Add some flags + * + * @hide */ + public abstract void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, + int maxW, int maxH, int flags); + + /** + * Registers this as an HDR info listener on the provided display + * @param displayToken + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS) + public void register(IBinder displayToken) { + Objects.requireNonNull(displayToken); + synchronized (this) { + if (mRegisteredListeners.containsKey(displayToken)) { + return; + } + long nativePtr = nRegister(displayToken); + Runnable destructor = sRegistry.registerNativeAllocation(this, nativePtr); + mRegisteredListeners.put(displayToken, destructor); + } + } + + /** + * Unregisters this as an HDR info listener on the provided display + * @param displayToken + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS) + public void unregister(IBinder displayToken) { + Objects.requireNonNull(displayToken); + final Runnable destructor; + synchronized (this) { + destructor = mRegisteredListeners.remove(displayToken); + } + if (destructor != null) { + destructor.run(); + } + } + + /** + * Unregisters this on all previously registered displays + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS) + public void unregisterAll() { + final ArrayMap<IBinder, Runnable> toDestroy; + synchronized (this) { + toDestroy = mRegisteredListeners; + mRegisteredListeners = new ArrayMap<>(); + } + for (Runnable destructor : toDestroy.values()) { + destructor.run(); + } + } + + private ArrayMap<IBinder, Runnable> mRegisteredListeners = new ArrayMap<>(); + + private static native long nGetDestructor(); + private native long nRegister(IBinder displayToken); +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 840b69de3054..ec69d1cc6a2b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5255,7 +5255,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE; @Nullable - private String[] mOnReceiveContentMimeTypes; + private String[] mReceiveContentMimeTypes; @Nullable private ViewTranslationCallback mViewTranslationCallback; @@ -8847,7 +8847,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, structure.setAutofillValue(getAutofillValue()); } structure.setImportantForAutofill(getImportantForAutofill()); - structure.setOnReceiveContentMimeTypes(getOnReceiveContentMimeTypes()); + structure.setReceiveContentMimeTypes(getReceiveContentMimeTypes()); } int ignoredParentLeft = 0; @@ -9066,7 +9066,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Preconditions.checkArgument(Arrays.stream(mimeTypes).noneMatch(t -> t.startsWith("*")), "A MIME type set here must not start with *: " + Arrays.toString(mimeTypes)); } - mOnReceiveContentMimeTypes = ArrayUtils.isEmpty(mimeTypes) ? null : mimeTypes; + mReceiveContentMimeTypes = ArrayUtils.isEmpty(mimeTypes) ? null : mimeTypes; getListenerInfo().mOnReceiveContentListener = listener; } @@ -9134,8 +9134,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @SuppressLint("NullableCollection") @Nullable - public String[] getOnReceiveContentMimeTypes() { - return mOnReceiveContentMimeTypes; + public String[] getReceiveContentMimeTypes() { + return mReceiveContentMimeTypes; } /** @@ -19786,6 +19786,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } + if (mAttachInfo == null) { + // View is not attached. + return; + } + final int h = dr.getIntrinsicHeight(); final int w = dr.getIntrinsicWidth(); final Rect rect = mAttachInfo.mTmpInvalRect; @@ -29547,6 +29552,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mScrollCaptureInternal; } + ViewRoot getViewRoot() { + return mViewRootImpl; + } + public void dump(String prefix, PrintWriter writer) { String innerPrefix = prefix + " "; writer.println(prefix + "AttachInfo:"); @@ -30759,10 +30768,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * view. * @param supportedFormats the supported translation formats. For now, the only possible value * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}. - * @param requestsCollector a {@link ViewTranslationRequest} collector that will be called + * @param requestsCollector a {@link ViewTranslationRequest} collector that can be called * multiple times to collect the information to be translated in the virtual view. One * {@link ViewTranslationRequest} per virtual child. The {@link ViewTranslationRequest} must - * contains the {@link AutofillId} corresponding to the virtualChildIds. + * contains the {@link AutofillId} corresponding to the virtualChildIds. Do not keep this + * Consumer after the method returns. */ @SuppressLint("NullableCollection") public void onCreateTranslationRequests(@NonNull long[] virtualChildIds, @@ -30835,7 +30845,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * ui translation, the system will call this method to traverse the view hierarchy to call * {@link View#onCreateTranslationRequest} to build {@link ViewTranslationRequest}s and create a * {@link android.view.translation.Translator} to translate the requests. All the - * {@link ViewTranslationRequest}s will be added when the traversal is done. + * {@link ViewTranslationRequest}s must be added when the traversal is done. * * <p> The default implementation will call {@link View#onCreateTranslationRequest} to build * {@link ViewTranslationRequest} if the view should be translated. </p> @@ -30934,4 +30944,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); } } + + /** + * @return The {@link android.view.ViewRoot} interface for this View. This will only + * return a non-null value when called between {@link #onAttachedToWindow} and + * {@link #onDetachedFromWindow}. + * + * The ViewRoot itself is not a View, it is just the interface to the windowing-system + * object that contains the entire view hierarchy. For the root View of a given hierarchy + * see {@link #getRootView}. + */ + public @Nullable ViewRoot getViewRoot() { + if (mAttachInfo != null) { + return mAttachInfo.getViewRoot(); + } + return null; + } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 8198254958f4..5b695f4c425a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2053,26 +2053,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); - View childWithAccessibilityFocus = - event.isTargetAccessibilityFocus() - ? findChildWithAccessibilityFocus() - : null; if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { - - // If there is a view that has accessibility focus we want it - // to get the event first and if not handled we will perform a - // normal dispatch. We may do a double iteration but this is - // safer given the timeframe. - if (childWithAccessibilityFocus != null) { - if (childWithAccessibilityFocus != child) { - continue; - } - childWithAccessibilityFocus = null; - i = childrenCount - 1; - } - event.setTargetAccessibilityFocus(false); continue; } final PointerIcon pointerIcon = @@ -2730,8 +2713,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); + + // If there is a view that has accessibility focus we want it + // to get the event first and if not handled we will perform a + // normal dispatch. We may do a double iteration but this is + // safer given the timeframe. + if (childWithAccessibilityFocus != null) { + if (childWithAccessibilityFocus != child) { + continue; + } + childWithAccessibilityFocus = null; + i = childrenCount - 1; + } + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { + ev.setTargetAccessibilityFocus(false); continue; } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java new file mode 100644 index 000000000000..3c755981a440 --- /dev/null +++ b/core/java/android/view/ViewRoot.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UiThread; + +/** + * Provides an interface to the root-Surface of a View Hierarchy or Window. This + * is used in combination with the {@link android.view.SurfaceControl} API to enable + * attaching app created SurfaceControl to the ViewRoot's surface hierarchy, and enable + * SurfaceTransactions to be performed in sync with the ViewRoot drawing. This object + * is obtained from {@link android.view.View#getViewRoot} and + * {@link android.view.Window#getViewRoot}. It must be used from the UI thread of + * the object it was obtained from. + */ +@UiThread +public interface ViewRoot { + /** + * Create a transaction which will reparent {@param child} to the ViewRoot. See + * {@link SurfaceControl.Transaction#reparent}. This transacton must be applied + * or merged in to another transaction by the caller, otherwise it will have + * no effect. + * + * @param child The SurfaceControl to reparent. + * @return A new transaction which performs the reparent operation when applied. + */ + @Nullable SurfaceControl.Transaction buildReparentTransaction(@NonNull SurfaceControl child); + + /** + * Consume the passed in transaction, and request the ViewRoot to apply it with the + * next draw. This transaction will be merged with the buffer transaction from the ViewRoot + * and they will show up on-screen atomically synced. + * + * This will not cause a draw to be scheduled, and if there are no other changes + * to the View hierarchy you may need to call {@link android.view.View#invalidate} + */ + boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t); +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index ded5fb4aabf0..54572285bd01 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -225,7 +225,7 @@ import java.util.concurrent.CountDownLatch; */ @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) public final class ViewRootImpl implements ViewParent, - View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks { + View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, ViewRoot { private static final String TAG = "ViewRootImpl"; private static final boolean DBG = false; private static final boolean LOCAL_LOGV = false; @@ -1195,8 +1195,7 @@ public final class ViewRootImpl implements ViewParent, Looper.myLooper()); if (mAttachInfo.mThreadedRenderer != null) { - InputMetricsListener listener = - new InputMetricsListener(mInputEventReceiver); + InputMetricsListener listener = new InputMetricsListener(); mHardwareRendererObserver = new HardwareRendererObserver( listener, listener.data, mHandler, true /*waitForPresentTime*/); mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); @@ -1392,6 +1391,9 @@ public final class ViewRootImpl implements ViewParent, if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; + if (mHardwareRendererObserver != null) { + mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); + } } } } @@ -3209,7 +3211,9 @@ public final class ViewRootImpl implements ViewParent, Log.d(mTag, "Relayout called with blastSync"); } reportNextDraw(); - mNextDrawUseBlastSync = true; + if (isHardwareEnabled()) { + mNextDrawUseBlastSync = true; + } } boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; @@ -8083,6 +8087,9 @@ public final class ViewRootImpl implements ViewParent, ThreadedRenderer hardwareRenderer = mAttachInfo.mThreadedRenderer; if (hardwareRenderer != null) { + if (mHardwareRendererObserver != null) { + hardwareRenderer.removeObserver(mHardwareRendererObserver); + } if (mView != null) { hardwareRenderer.destroyHardwareResources(mView); } @@ -8584,18 +8591,12 @@ public final class ViewRootImpl implements ViewParent, super.dispose(); } } - WindowInputEventReceiver mInputEventReceiver; + private WindowInputEventReceiver mInputEventReceiver; final class InputMetricsListener implements HardwareRendererObserver.OnFrameMetricsAvailableListener { public long[] data = new long[FrameMetrics.Index.FRAME_STATS_COUNT]; - private InputEventReceiver mReceiver; - - InputMetricsListener(InputEventReceiver receiver) { - mReceiver = receiver; - } - @Override public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { final int inputEventId = (int) data[FrameMetrics.Index.INPUT_EVENT_ID]; @@ -8609,7 +8610,10 @@ public final class ViewRootImpl implements ViewParent, return; } final long gpuCompletedTime = data[FrameMetrics.Index.GPU_COMPLETED]; - mReceiver.reportLatencyInfo(inputEventId, gpuCompletedTime, presentTime); + if (mInputEventReceiver == null) { + return; + } + mInputEventReceiver.reportTimeline(inputEventId, gpuCompletedTime, presentTime); } } HardwareRendererObserver mHardwareRendererObserver; @@ -10212,9 +10216,6 @@ public final class ViewRootImpl implements ViewParent, if (useBLAST() && mBlastBufferQueue != null) { mBlastBufferQueue.mergeWithNextTransaction(transaction, frameNumber); - } else { - transaction.deferTransactionUntil(surfaceControl, surfaceControl, frameNumber); - transaction.apply(); } } @@ -10257,4 +10258,21 @@ public final class ViewRootImpl implements ViewParent, t.apply(); } } + + @Override + @Nullable public SurfaceControl.Transaction buildReparentTransaction( + @NonNull SurfaceControl child) { + if (mSurfaceControl.isValid()) { + return new SurfaceControl.Transaction().reparent(child, mSurfaceControl); + } + return null; + } + + @Override + public boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t) { + registerRtFrameCallback(frame -> { + mergeWithNextTransaction(t, frame); + }); + return true; + } } diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 8b3fb2e9e751..e2466345ff4a 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -373,12 +373,12 @@ public abstract class ViewStructure { public void setImportantForAutofill(@AutofillImportance int mode) {} /** - * Sets the MIME types accepted by this view. See {@link View#getOnReceiveContentMimeTypes()}. + * Sets the MIME types accepted by this view. See {@link View#getReceiveContentMimeTypes()}. * * <p>Should only be set when the node is used for Autofill or Content Capture purposes - it * will be ignored when used for Assist. */ - public void setOnReceiveContentMimeTypes( + public void setReceiveContentMimeTypes( @SuppressLint("NullableCollection") @Nullable String[] mimeTypes) {} /** diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index c814e5add1b7..52a09701803d 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1703,29 +1703,31 @@ public abstract class Window { public abstract void setBackgroundDrawable(Drawable drawable); /** + * <p> * Blurs the screen behind the window within the bounds of the window. - * + * </p><p> * The density of the blur is set by the blur radius. The radius defines the size * of the neighbouring area, from which pixels will be averaged to form the final * color for each pixel. The operation approximates a Gaussian blur. * A radius of 0 means no blur. The higher the radius, the denser the blur. - * + * </p><p> * The window background drawable is drawn on top of the blurred region. The blur * region bounds and rounded corners will mimic those of the background drawable. - * + * </p><p> * For the blur region to be visible, the window has to be translucent. See * {@link android.R.styleable#Window_windowIsTranslucent}. - * + * </p><p> * Note the difference with {@link WindowManager.LayoutParams#setBlurBehindRadius}, * which blurs the whole screen behind the window. Background blur blurs the screen behind * only within the bounds of the window. - * + * </p><p> * Some devices might not support cross-window blur due to GPU limitations. It can also be * disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or * when minimal post processing is requested. In such situations, no blur will be computed or * drawn, resulting in a transparent window background. To avoid this, the app might want to * change its theme to one that does not use blurs. To listen for cross-window blur * enabled/disabled events, use {@link WindowManager#addCrossWindowBlurEnabledListener}. + * </p> * * @param blurRadius The blur radius to use for window background blur in pixels * @@ -2717,4 +2719,14 @@ public abstract class Window { public @Nullable WindowInsetsController getInsetsController() { return null; } + + /** + * This will be null before a content view is added, e.g. via + * {@link #setContentView} or {@link #addContentView}. + * + * @return The {@link android.view.ViewRoot} interface for this Window + */ + public @Nullable ViewRoot getViewRoot() { + return null; + } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 93c3cab5b787..4b56fd740e8d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -818,16 +818,18 @@ public interface WindowManager extends ViewManager { } /** + * <p> * Returns whether cross-window blur is currently enabled. This affects both window blur behind * (see {@link LayoutParams#setBlurBehindRadius}) and window background blur (see * {@link Window#setBackgroundBlurRadius}). - * + * </p><p> * Cross-window blur might not be supported by some devices due to GPU limitations. It can also * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or * when minimal post processing is requested. In such situations, no blur will be computed or * drawn, so the blur target area will not be blurred. To handle this, the app might want to * change its theme to one that does not use blurs. To listen for cross-window blur * enabled/disabled events, use {@link #addCrossWindowBlurEnabledListener}. + * </p> * * @see #addCrossWindowBlurEnabledListener * @see LayoutParams#setBlurBehindRadius @@ -838,21 +840,22 @@ public interface WindowManager extends ViewManager { } /** + * <p> * Adds a listener, which will be called when cross-window blurs are enabled/disabled at * runtime. This affects both window blur behind (see {@link LayoutParams#setBlurBehindRadius}) * and window background blur (see {@link Window#setBackgroundBlurRadius}). - * + * </p><p> * Cross-window blur might not be supported by some devices due to GPU limitations. It can also * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or * when minimal post processing is requested. In such situations, no blur will be computed or * drawn, so the blur target area will not be blurred. To handle this, the app might want to * change its theme to one that does not use blurs. - * + * </p><p> * The listener will be called on the main thread. - * + * </p><p> * If the listener is added successfully, it will be called immediately with the current * cross-window blur enabled state. - * + * </p> * * @param listener the listener to be added. It will be called back with a boolean parameter, * which is true if cross-window blur is enabled and false if it is disabled @@ -866,18 +869,20 @@ public interface WindowManager extends ViewManager { } /** + * <p> * Adds a listener, which will be called when cross-window blurs are enabled/disabled at * runtime. This affects both window blur behind (see {@link LayoutParams#setBlurBehindRadius}) * and window background blur (see {@link Window#setBackgroundBlurRadius}). - * + * </p><p> * Cross-window blur might not be supported by some devices due to GPU limitations. It can also * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or * when minimal post processing is requested. In such situations, no blur will be computed or * drawn, so the blur target area will not be blurred. To handle this, the app might want to * change its theme to one that does not use blurs. - * + * </p><p> * If the listener is added successfully, it will be called immediately with the current * cross-window blur enabled state. + * </p> * * @param executor {@link Executor} to handle the listener callback * @param listener the listener to be added. It will be called back with a boolean parameter, @@ -3627,21 +3632,22 @@ public interface WindowManager extends ViewManager { } /** + * <p> * Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount}, * but instead of dimmed, the content behind the window will be blurred (or combined with * the dim amount, if such is specified). - * + * </p><p> * The density of the blur is set by the blur radius. The radius defines the size * of the neighbouring area, from which pixels will be averaged to form the final * color for each pixel. The operation approximates a Gaussian blur. * A radius of 0 means no blur. The higher the radius, the denser the blur. - * + * </p><p> * Note the difference with {@link android.view.Window#setBackgroundBlurRadius}, * which blurs only within the bounds of the window. Blur behind blurs the whole screen * behind the window. - * + * </p><p> * Requires {@link #FLAG_BLUR_BEHIND} to be set. - * + * </p><p> * Cross-window blur might not be supported by some devices due to GPU limitations. It can * also be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling * is used or when minimal post processing is requested. In such situations, no blur will @@ -3649,7 +3655,7 @@ public interface WindowManager extends ViewManager { * and the content behind it. To avoid this, the app might want to use more * {@link #dimAmount} on its window. To listen for cross-window blur enabled/disabled * events, use {@link #addCrossWindowBlurEnabledListener}. - * + * </p> * @param blurBehindRadius The blur radius to use for blur behind in pixels * * @see #FLAG_BLUR_BEHIND diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java index 9ab2c2b8bcb1..d14dc6e71c00 100644 --- a/core/java/android/view/accessibility/AccessibilityCache.java +++ b/core/java/android/view/accessibility/AccessibilityCache.java @@ -158,6 +158,7 @@ public class AccessibilityCache { * @param event An event. */ public void onAccessibilityEvent(AccessibilityEvent event) { + AccessibilityNodeInfo nodeToRefresh = null; synchronized (mLock) { if (DEBUG) { Log.i(LOG_TAG, "onAccessibilityEvent(" + event + ")"); @@ -166,17 +167,19 @@ public class AccessibilityCache { switch (eventType) { case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus); + removeCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus); } mAccessibilityFocus = event.getSourceNodeId(); mAccessibilityFocusedWindow = event.getWindowId(); - refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus); + nodeToRefresh = removeCachedNodeLocked(mAccessibilityFocusedWindow, + mAccessibilityFocus); } break; case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { if (mAccessibilityFocus == event.getSourceNodeId() && mAccessibilityFocusedWindow == event.getWindowId()) { - refreshCachedNodeLocked(mAccessibilityFocusedWindow, mAccessibilityFocus); + nodeToRefresh = removeCachedNodeLocked(mAccessibilityFocusedWindow, + mAccessibilityFocus); mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } @@ -184,17 +187,18 @@ public class AccessibilityCache { case AccessibilityEvent.TYPE_VIEW_FOCUSED: { if (mInputFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - refreshCachedNodeLocked(event.getWindowId(), mInputFocus); + removeCachedNodeLocked(event.getWindowId(), mInputFocus); } mInputFocus = event.getSourceNodeId(); - refreshCachedNodeLocked(event.getWindowId(), mInputFocus); + nodeToRefresh = removeCachedNodeLocked(event.getWindowId(), mInputFocus); } break; case AccessibilityEvent.TYPE_VIEW_SELECTED: case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: case AccessibilityEvent.TYPE_VIEW_CLICKED: case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: { - refreshCachedNodeLocked(event.getWindowId(), event.getSourceNodeId()); + nodeToRefresh = removeCachedNodeLocked(event.getWindowId(), + event.getSourceNodeId()); } break; case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: { @@ -205,7 +209,7 @@ public class AccessibilityCache { & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) { clearSubTreeLocked(windowId, sourceId); } else { - refreshCachedNodeLocked(windowId, sourceId); + nodeToRefresh = removeCachedNodeLocked(windowId, sourceId); } } } break; @@ -218,8 +222,8 @@ public class AccessibilityCache { if (event.getWindowChanges() == AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) { // Don't need to clear all cache. Unless the changes are related to - // content, we won't clear all cache here. - refreshCachedWindowLocked(event.getWindowId()); + // content, we won't clear all cache here with clear(). + clearWindowCacheLocked(); break; } case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: { @@ -228,59 +232,34 @@ public class AccessibilityCache { } } + if (nodeToRefresh != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Refreshing and re-adding cached node."); + } + if (mAccessibilityNodeRefresher.refreshNode(nodeToRefresh, true)) { + add(nodeToRefresh); + } + } if (CHECK_INTEGRITY) { checkIntegrity(); } } - private void refreshCachedNodeLocked(int windowId, long sourceId) { + private AccessibilityNodeInfo removeCachedNodeLocked(int windowId, long sourceId) { if (DEBUG) { - Log.i(LOG_TAG, "Refreshing cached node."); + Log.i(LOG_TAG, "Removing cached node."); } - LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId); if (nodes == null) { - return; + return null; } AccessibilityNodeInfo cachedInfo = nodes.get(sourceId); // If the source is not in the cache - nothing to do. if (cachedInfo == null) { - return; - } - // The node changed so we will just refresh it right now. - if (mAccessibilityNodeRefresher.refreshNode(cachedInfo, true)) { - return; - } - // Weird, we could not refresh. Just evict the entire sub-tree. - clearSubTreeLocked(windowId, sourceId); - } - - private void refreshCachedWindowLocked(int windowId) { - if (DEBUG) { - Log.i(LOG_TAG, "Refreshing cached window."); - } - - if (windowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { - return; - } - - final int displayCounts = mWindowCacheByDisplay.size(); - for (int i = 0; i < displayCounts; i++) { - final SparseArray<AccessibilityWindowInfo> windowsOfDisplay = - mWindowCacheByDisplay.valueAt(i); - if (windowsOfDisplay == null) { - continue; - } - final AccessibilityWindowInfo window = windowsOfDisplay.get(windowId); - if (window == null) { - continue; - } - if (!mAccessibilityNodeRefresher.refreshWindow(window)) { - // If we fail to refresh the window, clear all windows. - clearWindowCacheLocked(); - } - return; + return null; } + nodes.remove(sourceId); + return cachedInfo; } /** @@ -450,7 +429,7 @@ public class AccessibilityCache { if (clone.isAccessibilityFocused()) { if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID && mAccessibilityFocus != sourceId) { - refreshCachedNodeLocked(windowId, mAccessibilityFocus); + removeCachedNodeLocked(windowId, mAccessibilityFocus); } mAccessibilityFocus = sourceId; mAccessibilityFocusedWindow = windowId; diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index f63749be6df2..e64157734fdb 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -20,6 +20,7 @@ import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -39,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; @@ -83,6 +85,8 @@ public final class AccessibilityInteractionClient public static final int NO_ID = -1; + public static final String CALL_STACK = "call_stack"; + private static final String LOG_TAG = "AccessibilityInteractionClient"; private static final boolean DEBUG = false; @@ -113,7 +117,10 @@ public final class AccessibilityInteractionClient private final Object mInstanceLock = new Object(); + private final AccessibilityManager mAccessibilityManager; + private volatile int mInteractionId = -1; + private volatile int mCallingUid = Process.INVALID_UID; private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult; @@ -152,6 +159,37 @@ public final class AccessibilityInteractionClient } /** + * @return The client for the current thread. + */ + public static AccessibilityInteractionClient getInstance(Context context) { + final long threadId = Thread.currentThread().getId(); + if (context != null) { + return getInstanceForThread(threadId, context); + } + return getInstanceForThread(threadId); + } + + /** + * <strong>Note:</strong> We keep one instance per interrogating thread since + * the instance contains state which can lead to undesired thread interleavings. + * We do not have a thread local variable since other threads should be able to + * look up the correct client knowing a thread id. See ViewRootImpl for details. + * + * @return The client for a given <code>threadId</code>. + */ + public static AccessibilityInteractionClient getInstanceForThread( + long threadId, Context context) { + synchronized (sStaticLock) { + AccessibilityInteractionClient client = sClients.get(threadId); + if (client == null) { + client = new AccessibilityInteractionClient(context); + sClients.put(threadId, client); + } + return client; + } + } + + /** * Gets a cached accessibility service connection. * * @param connectionId The connection id. @@ -197,6 +235,11 @@ public final class AccessibilityInteractionClient private AccessibilityInteractionClient() { /* reducing constructor visibility */ + mAccessibilityManager = null; + } + + private AccessibilityInteractionClient(Context context) { + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); } /** @@ -453,6 +496,15 @@ public final class AccessibilityInteractionClient if (packageNames != null) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "findAccessibilityNodeInfoByAccessibilityId", + "InteractionId:" + interactionId + ";Result: " + infos + + ";connectionId=" + connectionId + ";accessibilityWindowId=" + + accessibilityWindowId + ";accessibilityNodeId=" + + accessibilityNodeId + ";bypassCache=" + bypassCache + + ";prefetchFlags=" + prefetchFlags + ";arguments=" + arguments); + } finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, bypassCache, packageNames); if (infos != null && !infos.isEmpty()) { @@ -514,6 +566,14 @@ public final class AccessibilityInteractionClient if (packageNames != null) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "findAccessibilityNodeInfosByViewId", "InteractionId=" + + interactionId + ":Result: " + infos + ";connectionId=" + + connectionId + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId=" + + viewId); + } if (infos != null) { finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false, packageNames); @@ -568,6 +628,13 @@ public final class AccessibilityInteractionClient if (packageNames != null) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "findAccessibilityNodeInfosByText", "InteractionId=" + + interactionId + ":Result: " + infos + ";connectionId=" + + connectionId + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text); + } if (infos != null) { finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false, packageNames); @@ -621,6 +688,14 @@ public final class AccessibilityInteractionClient if (packageNames != null) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "findFocus", "InteractionId=" + interactionId + + ":Result: " + info + ";connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType=" + + focusType); + } finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); return info; } @@ -671,6 +746,14 @@ public final class AccessibilityInteractionClient AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "focusSearch", "InteractionId=" + interactionId + + ":Result: " + info + ";connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";direction=" + + direction); + } return info; } } else { @@ -716,7 +799,17 @@ public final class AccessibilityInteractionClient } if (success) { - return getPerformAccessibilityActionResultAndClear(interactionId); + final boolean result = + getPerformAccessibilityActionResultAndClear(interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "performAccessibilityAction", "InteractionId=" + + interactionId + ":Result: " + result + ";connectionId=" + + connectionId + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";action=" + + action + ";arguments=" + arguments); + } + return result; } } else { if (DEBUG) { @@ -774,6 +867,7 @@ public final class AccessibilityInteractionClient if (interactionId > mInteractionId) { mFindAccessibilityNodeInfoResult = info; mInteractionId = interactionId; + mCallingUid = Binder.getCallingUid(); } mInstanceLock.notifyAll(); } @@ -823,6 +917,7 @@ public final class AccessibilityInteractionClient mFindAccessibilityNodeInfosResult = Collections.emptyList(); } mInteractionId = interactionId; + mCallingUid = Binder.getCallingUid(); } mInstanceLock.notifyAll(); } @@ -851,6 +946,7 @@ public final class AccessibilityInteractionClient if (interactionId > mInteractionId) { mPerformAccessibilityActionResult = succeeded; mInteractionId = interactionId; + mCallingUid = Binder.getCallingUid(); } mInstanceLock.notifyAll(); } @@ -1059,4 +1155,19 @@ public final class AccessibilityInteractionClient } return true; } + + private void logTrace( + IAccessibilityServiceConnection connection, String method, String params) { + try { + Bundle b = new Bundle(); + ArrayList<StackTraceElement> callStack = new ArrayList<StackTraceElement>( + Arrays.asList(Thread.currentThread().getStackTrace())); + b.putSerializable(CALL_STACK, callStack); + connection.logTrace(SystemClock.elapsedRealtimeNanos(), + LOG_TAG + ".callback for " + method, params, Process.myPid(), + Thread.currentThread().getId(), mCallingUid, b); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Failed to log trace. " + e); + } + } } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 56dcd5951e5e..fc9e5e2ef04e 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -111,6 +111,9 @@ public final class AccessibilityManager { public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010; /** @hide */ + public static final int STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED = 0x00000020; + + /** @hide */ public static final int DALTONIZER_DISABLED = -1; /** @hide */ @@ -232,6 +235,9 @@ public final class AccessibilityManager { @UnsupportedAppUsage(trackingBug = 123768939L) boolean mIsHighTextContrastEnabled; + // Whether accessibility tracing is enabled or not + boolean mIsAccessibilityTracingEnabled = false; + AccessibilityPolicy mAccessibilityPolicy; private int mPerformingAction = 0; @@ -1004,6 +1010,17 @@ public final class AccessibilityManager { } /** + * Gets accessibility tracing enabled state. + * + * @hide + */ + public boolean isAccessibilityTracingEnabled() { + synchronized (mLock) { + return mIsAccessibilityTracingEnabled; + } + } + + /** * Get the preparers that are registered for an accessibility ID * * @param id The ID of interest @@ -1197,6 +1214,8 @@ public final class AccessibilityManager { (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0; final boolean highTextContrastEnabled = (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0; + final boolean accessibilityTracingEnabled = + (stateFlags & STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED) != 0; final boolean wasEnabled = isEnabled(); final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled; @@ -1218,6 +1237,8 @@ public final class AccessibilityManager { if (wasHighTextContrastEnabled != highTextContrastEnabled) { notifyHighTextContrastStateChanged(); } + + updateAccessibilityTracingState(accessibilityTracingEnabled); } /** @@ -1675,6 +1696,15 @@ public final class AccessibilityManager { } /** + * Update mIsAccessibilityTracingEnabled. + */ + private void updateAccessibilityTracingState(boolean enabled) { + synchronized (mLock) { + mIsAccessibilityTracingEnabled = enabled; + } + } + + /** * Update interactive and non-interactive UI timeout. * * @param uiTimeout A pair of {@code int}s. First integer for interactive one, and second diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index 4e9c229e03a0..c882c6e3200d 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -114,7 +114,7 @@ public final class ViewNode extends AssistStructure.ViewNode { private String[] mAutofillHints; private AutofillValue mAutofillValue; private CharSequence[] mAutofillOptions; - private String[] mOnReceiveContentMimeTypes; + private String[] mReceiveContentMimeTypes; /** @hide */ public ViewNode() { @@ -172,7 +172,7 @@ public final class ViewNode extends AssistStructure.ViewNode { mLocaleList = parcel.readParcelable(null); } if ((nodeFlags & FLAGS_HAS_MIME_TYPES) != 0) { - mOnReceiveContentMimeTypes = parcel.readStringArray(); + mReceiveContentMimeTypes = parcel.readStringArray(); } if ((nodeFlags & FLAGS_HAS_INPUT_TYPE) != 0) { mInputType = parcel.readInt(); @@ -470,8 +470,8 @@ public final class ViewNode extends AssistStructure.ViewNode { @Override @Nullable - public String[] getOnReceiveContentMimeTypes() { - return mOnReceiveContentMimeTypes; + public String[] getReceiveContentMimeTypes() { + return mReceiveContentMimeTypes; } @Nullable @@ -519,7 +519,7 @@ public final class ViewNode extends AssistStructure.ViewNode { if (mLocaleList != null) { nodeFlags |= FLAGS_HAS_LOCALE_LIST; } - if (mOnReceiveContentMimeTypes != null) { + if (mReceiveContentMimeTypes != null) { nodeFlags |= FLAGS_HAS_MIME_TYPES; } if (mInputType != 0) { @@ -599,7 +599,7 @@ public final class ViewNode extends AssistStructure.ViewNode { parcel.writeParcelable(mLocaleList, 0); } if ((nodeFlags & FLAGS_HAS_MIME_TYPES) != 0) { - parcel.writeStringArray(mOnReceiveContentMimeTypes); + parcel.writeStringArray(mReceiveContentMimeTypes); } if ((nodeFlags & FLAGS_HAS_INPUT_TYPE) != 0) { parcel.writeInt(mInputType); @@ -929,8 +929,8 @@ public final class ViewNode extends AssistStructure.ViewNode { } @Override - public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) { - mNode.mOnReceiveContentMimeTypes = mimeTypes; + public void setReceiveContentMimeTypes(@Nullable String[] mimeTypes) { + mNode.mReceiveContentMimeTypes = mimeTypes; } @Override diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 9f796c068abe..bdd12063e6cf 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -938,12 +938,12 @@ public class BaseInputConnection implements InputConnection { /** * Default implementation which invokes {@link View#performReceiveContent} on the target - * view if the view {@link View#getOnReceiveContentMimeTypes allows} content insertion; + * view if the view {@link View#getReceiveContentMimeTypes allows} content insertion; * otherwise returns false without any side effects. */ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { ClipDescription description = inputContentInfo.getDescription(); - if (mTargetView.getOnReceiveContentMimeTypes() == null) { + if (mTargetView.getReceiveContentMimeTypes() == null) { if (DEBUG) { Log.d(TAG, "Can't insert content from IME: content=" + description); } diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index bde4cb7c4fc3..4a52b1f01b46 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -725,7 +725,8 @@ public class EditorInfo implements InputType, Parcelable { * protocol. */ @Nullable - public CharSequence getInitialTextBeforeCursor(int length, int flags) { + public CharSequence getInitialTextBeforeCursor( + @IntRange(from = 0) int length, @InputConnection.GetTextType int flags) { if (mInitialSurroundingText == null) { return null; } @@ -750,7 +751,7 @@ public class EditorInfo implements InputType, Parcelable { * this protocol is not supported. */ @Nullable - public CharSequence getInitialSelectedText(int flags) { + public CharSequence getInitialSelectedText(@InputConnection.GetTextType int flags) { if (mInitialSurroundingText == null) { return null; } @@ -792,7 +793,8 @@ public class EditorInfo implements InputType, Parcelable { * protocol. */ @Nullable - public CharSequence getInitialTextAfterCursor(int length, int flags) { + public CharSequence getInitialTextAfterCursor( + @IntRange(from = 0) int length, @InputConnection.GetTextType int flags) { if (mInitialSurroundingText == null) { return null; } diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index de4554b9e624..d2db0df6c597 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -101,11 +101,12 @@ public interface InputMethod { * @param privilegedOperations IPC endpoint to do some privileged * operations that are allowed only to the * current IME. + * @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME. * @hide */ @MainThread default void initializeInternal(IBinder token, int displayId, - IInputMethodPrivilegedOperations privilegedOperations) { + IInputMethodPrivilegedOperations privilegedOperations, int configChanges) { updateInputMethodDisplay(displayId); attachToken(token); } diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 6ba3b37ce214..c26b302db983 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -18,19 +18,23 @@ package android.view.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.inputmethodservice.InputMethodService; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -62,6 +66,7 @@ import java.util.List; * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions * @attr ref android.R.styleable#InputMethod_suppressesSpellChecker * @attr ref android.R.styleable#InputMethod_showInInputMethodPicker + * @attr ref android.R.styleable#InputMethod_configChanges */ public final class InputMethodInfo implements Parcelable { static final String TAG = "InputMethodInfo"; @@ -130,6 +135,12 @@ public final class InputMethodInfo implements Parcelable { private final boolean mShowInInputMethodPicker; /** + * The flag for configurations IME assumes the responsibility for handling in + * {@link InputMethodService#onConfigurationChanged(Configuration)}}. + */ + private final int mHandledConfigChanges; + + /** * @param service the {@link ResolveInfo} corresponds in which the IME is implemented. * @return a unique ID to be returned by {@link #getId()}. We have used * {@link ComponentName#flattenToShortString()} for this purpose (and it is already @@ -221,6 +232,8 @@ public final class InputMethodInfo implements Parcelable { com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false); showInInputMethodPicker = sa.getBoolean( com.android.internal.R.styleable.InputMethod_showInInputMethodPicker, true); + mHandledConfigChanges = sa.getInt( + com.android.internal.R.styleable.InputMethod_configChanges, 0); sa.recycle(); final int depth = parser.getDepth(); @@ -309,6 +322,7 @@ public final class InputMethodInfo implements Parcelable { mIsVrOnly = source.readBoolean(); mService = ResolveInfo.CREATOR.createFromParcel(source); mSubtypes = new InputMethodSubtypeArray(source); + mHandledConfigChanges = source.readInt(); mForceDefault = false; } @@ -320,7 +334,22 @@ public final class InputMethodInfo implements Parcelable { this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, - false /* inlineSuggestionsEnabled */, false /* isVrOnly */); + false /* inlineSuggestionsEnabled */, false /* isVrOnly */, + 0 /* handledConfigChanges */); + } + + /** + * Temporary API for creating a built-in input method for test. + * @hide + */ + @TestApi + public InputMethodInfo(@NonNull String packageName, @NonNull String className, + @NonNull CharSequence label, @NonNull String settingsActivity, + int handledConfigChanges) { + this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, + settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, + false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, + false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges); } /** @@ -332,7 +361,7 @@ public final class InputMethodInfo implements Parcelable { boolean forceDefault) { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, - false /* isVrOnly */); + false /* isVrOnly */, 0 /* handledconfigChanges */); } /** @@ -343,7 +372,8 @@ public final class InputMethodInfo implements Parcelable { List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, - supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly); + supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly, + 0 /* handledConfigChanges */); } /** @@ -353,7 +383,7 @@ public final class InputMethodInfo implements Parcelable { public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled, - boolean isVrOnly) { + boolean isVrOnly, int handledConfigChanges) { final ServiceInfo si = ri.serviceInfo; mService = ri; mId = new ComponentName(si.packageName, si.name).flattenToShortString(); @@ -367,6 +397,7 @@ public final class InputMethodInfo implements Parcelable { mSuppressesSpellChecker = false; mShowInInputMethodPicker = true; mIsVrOnly = isVrOnly; + mHandledConfigChanges = handledConfigChanges; } private static ResolveInfo buildFakeResolveInfo(String packageName, String className, @@ -513,6 +544,17 @@ public final class InputMethodInfo implements Parcelable { } } + /** + * Returns the bit mask of kinds of configuration changes that this IME + * can handle itself (without being restarted by the system). + * + * @attr ref android.R.styleable#InputMethod_configChanges + */ + @ActivityInfo.Config + public int getConfigChanges() { + return mHandledConfigChanges; + } + public void dump(Printer pw, String prefix) { pw.println(prefix + "mId=" + mId + " mSettingsActivityName=" + mSettingsActivityName @@ -622,6 +664,7 @@ public final class InputMethodInfo implements Parcelable { dest.writeBoolean(mIsVrOnly); mService.writeToParcel(dest, flags); mSubtypes.writeToParcel(dest); + dest.writeInt(mHandledConfigChanges); } /** diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 0fa6e16b2e03..53e354f8200e 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -30,6 +30,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Process; import android.util.ArrayMap; +import android.util.IntArray; import android.util.Log; import android.util.LongSparseArray; import android.util.Pair; @@ -100,7 +101,8 @@ public class UiTranslationController { */ public void updateUiTranslationState(@UiTranslationState int state, TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> views) { - if (!mActivity.isResumed()) { + if (!mActivity.isResumed() && (state == STATE_UI_TRANSLATION_STARTED + || state == STATE_UI_TRANSLATION_RESUMED)) { return; } Log.i(TAG, "updateUiTranslationState state: " + stateToString(state) @@ -241,13 +243,11 @@ public class UiTranslationController { final SparseArray<ViewTranslationResponse> viewsResult = new SparseArray<>(); final SparseArray<LongSparseArray<ViewTranslationResponse>> virtualViewsResult = new SparseArray<>(); - // TODO: use another structure to prevent autoboxing? - final List<Integer> viewIds = new ArrayList<>(); - + final IntArray viewIds = new IntArray(1); for (int i = 0; i < translatedResult.size(); i++) { final ViewTranslationResponse result = translatedResult.valueAt(i); final AutofillId autofillId = result.getAutofillId(); - if (!viewIds.contains(autofillId.getViewId())) { + if (viewIds.indexOf(autofillId.getViewId()) < 0) { viewIds.add(autofillId.getViewId()); } if (autofillId.isNonVirtual()) { @@ -405,23 +405,29 @@ public class UiTranslationController { // Filter the request views's AutofillId SparseIntArray virtualViewChildCount = getRequestVirtualViewChildCount(views); Map<AutofillId, long[]> viewIds = new ArrayMap<>(); + Map<AutofillId, Integer> unusedIndices = null; for (int i = 0; i < views.size(); i++) { AutofillId autofillId = views.get(i); if (autofillId.isNonVirtual()) { viewIds.put(autofillId, null); } else { + if (unusedIndices == null) { + unusedIndices = new ArrayMap<>(); + } // The virtual id get from content capture is long, see getVirtualChildLongId() // e.g. 1001, 1001:2, 1002:1 -> 1001, <1,2>; 1002, <1> AutofillId virtualViewAutofillId = new AutofillId(autofillId.getViewId()); long[] childs; + int end = 0; if (viewIds.containsKey(virtualViewAutofillId)) { childs = viewIds.get(virtualViewAutofillId); + end = unusedIndices.get(virtualViewAutofillId); } else { int childCount = virtualViewChildCount.get(autofillId.getViewId()); childs = new long[childCount]; viewIds.put(virtualViewAutofillId, childs); } - int end = childs.length - 1; + unusedIndices.put(virtualViewAutofillId, end + 1); childs[end] = autofillId.getVirtualChildLongId(); } } @@ -465,7 +471,7 @@ public class UiTranslationController { return new int[] {TranslationSpec.DATA_FORMAT_TEXT}; } - private void findViewsTraversalByAutofillIds(List<Integer> sourceViewIds) { + private void findViewsTraversalByAutofillIds(IntArray sourceViewIds) { final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken()); for (int rootNum = 0; rootNum < roots.size(); rootNum++) { @@ -479,7 +485,7 @@ public class UiTranslationController { } private void findViewsTraversalByAutofillIds(ViewGroup viewGroup, - List<Integer> sourceViewIds) { + IntArray sourceViewIds) { final int childCount = viewGroup.getChildCount(); for (int i = 0; i < childCount; ++i) { final View child = viewGroup.getChildAt(i); @@ -491,9 +497,10 @@ public class UiTranslationController { } } - private void addViewIfNeeded(List<Integer> sourceViewIds, View view) { + private void addViewIfNeeded(IntArray sourceViewIds, View view) { final AutofillId autofillId = view.getAutofillId(); - if (sourceViewIds.contains(autofillId.getViewId()) && !mViews.containsKey(autofillId)) { + if ((sourceViewIds.indexOf(autofillId.getViewId()) >= 0) + && !mViews.containsKey(autofillId)) { mViews.put(autofillId, new WeakReference<>(view)); } } diff --git a/core/java/android/view/translation/ViewTranslationRequest.java b/core/java/android/view/translation/ViewTranslationRequest.java index 180b1c2c166a..4d8fb99023c9 100644 --- a/core/java/android/view/translation/ViewTranslationRequest.java +++ b/core/java/android/view/translation/ViewTranslationRequest.java @@ -35,7 +35,8 @@ import java.util.Set; * Wrapper class representing a translation request associated with a {@link android.view.View} to * be used by {@link android.service.translation.TranslationService}. */ -@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true, genGetters = false) +@DataClass(genBuilder = false, genToString = true, genEqualsHashCode = true, genGetters = false, + genHiddenConstructor = true) public final class ViewTranslationRequest implements Parcelable { /** @@ -91,10 +92,39 @@ public final class ViewTranslationRequest implements Parcelable { return Collections.emptyMap(); } - @DataClass.Suppress({"addTranslationRequestValue", "setAutofillId"}) - abstract static class BaseBuilder { + /** + * A builder for building ViewTranslationRequest. + */ + public static final class Builder { + + private @NonNull AutofillId mAutofillId; + private @NonNull Map<String, TranslationRequestValue> mTranslationRequestValues; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @param autofillId The {@link AutofillId} of the view associated with this request. + */ + public Builder(@NonNull AutofillId autofillId) { + mAutofillId = autofillId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAutofillId); + } - abstract Builder setTranslationRequestValues(Map<String, TranslationRequestValue> value); + /** + * Creates a new Builder. + * + * @param autofillId the {@link AutofillId} of the non-virtual view hosting the virtual view + * hierarchy associated with this request. + * @param virtualChildId the id of the virtual child, relative to the parent. + */ + public Builder(@NonNull AutofillId autofillId, long virtualChildId) { + mAutofillId = new AutofillId(autofillId, virtualChildId, AutofillId.NO_SESSION); + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAutofillId); + } /** * Sets the corresponding {@link TranslationRequestValue} for the provided key. @@ -104,20 +134,50 @@ public final class ViewTranslationRequest implements Parcelable { * @return this builder. */ @SuppressLint("MissingGetterMatchingBuilder") - public Builder setValue(String key, - TranslationRequestValue value) { - final Builder builder = (Builder) this; - if (builder.mTranslationRequestValues == null) { + @NonNull + public Builder setValue(@NonNull String key, @NonNull TranslationRequestValue value) { + if (mTranslationRequestValues == null) { setTranslationRequestValues(new ArrayMap<>()); } - builder.mTranslationRequestValues.put(key, value); - return builder; + mTranslationRequestValues.put(key, value); + return this; + } + + /** + * Builds the instance. This builder should not be touched after calling this! + */ + @NonNull + public ViewTranslationRequest build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; // Mark builder used + + if ((mBuilderFieldsSet & 0x2) == 0) { + mTranslationRequestValues = defaultTranslationRequestValues(); + } + ViewTranslationRequest o = new ViewTranslationRequest( + mAutofillId, + mTranslationRequestValues); + return o; + } + + Builder setTranslationRequestValues(@NonNull Map<String, TranslationRequestValue> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mTranslationRequestValues = value; + return this; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } } } - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -130,8 +190,15 @@ public final class ViewTranslationRequest implements Parcelable { //@formatter:off + /** + * Creates a new ViewTranslationRequest. + * + * @param autofillId + * The {@link AutofillId} of the view associated with this request. + * @hide + */ @DataClass.Generated.Member - /* package-private */ ViewTranslationRequest( + public ViewTranslationRequest( @NonNull AutofillId autofillId, @NonNull Map<String,TranslationRequestValue> translationRequestValues) { this.mAutofillId = autofillId; @@ -234,67 +301,11 @@ public final class ViewTranslationRequest implements Parcelable { } }; - /** - * A builder for {@link ViewTranslationRequest} - */ - @SuppressWarnings("WeakerAccess") - @DataClass.Generated.Member - public static final class Builder extends BaseBuilder { - - private @NonNull AutofillId mAutofillId; - private @NonNull Map<String,TranslationRequestValue> mTranslationRequestValues; - - private long mBuilderFieldsSet = 0L; - - /** - * Creates a new Builder. - * - * @param autofillId - * The {@link AutofillId} of the view associated with this request. - */ - public Builder( - @NonNull AutofillId autofillId) { - mAutofillId = autofillId; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mAutofillId); - } - - @DataClass.Generated.Member - @Override - @NonNull Builder setTranslationRequestValues(@NonNull Map<String,TranslationRequestValue> value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x2; - mTranslationRequestValues = value; - return this; - } - - /** Builds the instance. This builder should not be touched after calling this! */ - public @NonNull ViewTranslationRequest build() { - checkNotUsed(); - mBuilderFieldsSet |= 0x4; // Mark builder used - - if ((mBuilderFieldsSet & 0x2) == 0) { - mTranslationRequestValues = defaultTranslationRequestValues(); - } - ViewTranslationRequest o = new ViewTranslationRequest( - mAutofillId, - mTranslationRequestValues); - return o; - } - - private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x4) != 0) { - throw new IllegalStateException( - "This Builder should not be reused. Use a new Builder instance instead"); - } - } - } - @DataClass.Generated( - time = 1614992269658L, - codegenVersion = "1.0.22", + time = 1617119791798L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/translation/ViewTranslationRequest.java", - inputSignatures = "public static final java.lang.String ID_TEXT\nprivate final @android.annotation.NonNull android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> mTranslationRequestValues\npublic @android.annotation.NonNull android.view.translation.TranslationRequestValue getValue(java.lang.String)\npublic @android.annotation.NonNull java.util.Set<java.lang.String> getKeys()\npublic @android.annotation.NonNull android.view.autofill.AutofillId getAutofillId()\nprivate static java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nclass ViewTranslationRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.translation.ViewTranslationRequest.Builder setTranslationRequestValues(java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue>)\npublic @android.annotation.SuppressLint android.view.translation.ViewTranslationRequest.Builder setValue(java.lang.String,android.view.translation.TranslationRequestValue)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true, genGetters=false)\nabstract android.view.translation.ViewTranslationRequest.Builder setTranslationRequestValues(java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue>)\npublic @android.annotation.SuppressLint android.view.translation.ViewTranslationRequest.Builder setValue(java.lang.String,android.view.translation.TranslationRequestValue)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final java.lang.String ID_TEXT\nprivate final @android.annotation.NonNull android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> mTranslationRequestValues\npublic @android.annotation.NonNull android.view.translation.TranslationRequestValue getValue(java.lang.String)\npublic @android.annotation.NonNull java.util.Set<java.lang.String> getKeys()\npublic @android.annotation.NonNull android.view.autofill.AutofillId getAutofillId()\nprivate static java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nclass ViewTranslationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.view.autofill.AutofillId mAutofillId\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> mTranslationRequestValues\nprivate long mBuilderFieldsSet\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.view.translation.ViewTranslationRequest.Builder setValue(java.lang.String,android.view.translation.TranslationRequestValue)\npublic @android.annotation.NonNull android.view.translation.ViewTranslationRequest build()\n android.view.translation.ViewTranslationRequest.Builder setTranslationRequestValues(java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue>)\nprivate void checkNotUsed()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genToString=true, genEqualsHashCode=true, genGetters=false, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index 264736008782..770a1568241c 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -365,7 +365,6 @@ public interface WebViewProvider { } @SuppressLint("NullableCollection") - @Nullable default void onCreateTranslationRequests( @NonNull @SuppressWarnings("unused") long[] virtualChildIds, @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats, diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index c31462581c9f..2ac0342b80d3 100755 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -4825,6 +4825,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE); mTouchMode = TOUCH_MODE_FLING; mSuppressIdleStateChangeCall = false; + removeCallbacks(this); postOnAnimation(this); if (PROFILE_FLINGING) { diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index 1951194816bd..ae426d2c897f 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -626,20 +626,31 @@ public class EdgeEffect { // assume rotations of increments of 90 degrees float x = mTmpPoints[10] - mTmpPoints[8]; float width = right - left; - float vecX = dampStretchVector(Math.max(-1f, Math.min(1f, x / width))); + float vecX = 0f; + if (width > 0) { + vecX = dampStretchVector(Math.max(-1f, Math.min(1f, x / width))); + } + float y = mTmpPoints[11] - mTmpPoints[9]; float height = bottom - top; - float vecY = dampStretchVector(Math.max(-1f, Math.min(1f, y / height))); - renderNode.stretch( - left, - top, - right, - bottom, - vecX, - vecY, - mWidth, - mHeight - ); + float vecY = 0f; + if (height > 0) { + vecY = dampStretchVector(Math.max(-1f, Math.min(1f, y / height))); + } + + boolean hasStretchVectors = Float.compare(vecX, 0) != 0 || Float.compare(vecY, 0) != 0; + if (right > left && bottom > top && mWidth > 0 && mHeight > 0 && hasStretchVectors) { + renderNode.stretch( + left, + top, + right, + bottom, + vecX, + vecY, + mWidth, + mHeight + ); + } } else { // This is TYPE_STRETCH and drawing into a Canvas that isn't a Recording Canvas, // so no effect can be shown. Just end the effect. diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ffaa31552a6a..40c79cf086fc 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8773,7 +8773,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener outAttrs.initialSelEnd = getSelectionEnd(); outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType()); outAttrs.setInitialSurroundingText(mText); - outAttrs.contentMimeTypes = getOnReceiveContentMimeTypes(); + outAttrs.contentMimeTypes = getReceiveContentMimeTypes(); return ic; } } @@ -11741,10 +11741,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } - String[] mimeTypes = getOnReceiveContentMimeTypes(); + String[] mimeTypes = getReceiveContentMimeTypes(); if (mimeTypes == null && mEditor != null) { // If the app hasn't set a listener for receiving content on this view (ie, - // getOnReceiveContentMimeTypes() returns null), check if it implements the + // getReceiveContentMimeTypes() returns null), check if it implements the // keyboard image API and, if possible, use those MIME types as fallback. // This fallback is only in place for autofill, not other mechanisms for // inserting content. See AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_LISTENER @@ -11752,7 +11752,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mimeTypes = mEditor.getDefaultOnReceiveContentListener() .getFallbackMimeTypesForAutofill(this); } - structure.setOnReceiveContentMimeTypes(mimeTypes); + structure.setReceiveContentMimeTypes(mimeTypes); } if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL diff --git a/core/java/android/widget/TextViewOnReceiveContentListener.java b/core/java/android/widget/TextViewOnReceiveContentListener.java index 91fac5511807..0d5bf71dc955 100644 --- a/core/java/android/widget/TextViewOnReceiveContentListener.java +++ b/core/java/android/widget/TextViewOnReceiveContentListener.java @@ -194,7 +194,7 @@ public final class TextViewOnReceiveContentListener implements OnReceiveContentL * non-text content. */ private static boolean isUsageOfImeCommitContentEnabled(@NonNull View view) { - if (view.getOnReceiveContentMimeTypes() != null) { + if (view.getReceiveContentMimeTypes() != null) { if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { Log.v(LOG_TAG, "Fallback to commitContent disabled (custom callback is set)"); } diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 01bb199a1a6a..8a6856e3b51b 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -36,9 +36,9 @@ interface IAppOpsService { // be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h // and not be reordered int checkOperation(int code, int uid, String packageName); - int noteOperation(int code, int uid, String packageName, @nullable String attributionTag, + SyncNotedAppOp noteOperation(int code, int uid, String packageName, @nullable String attributionTag, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage); - int startOperation(IBinder clientId, int code, int uid, String packageName, + SyncNotedAppOp startOperation(IBinder clientId, int code, int uid, String packageName, @nullable String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage); @UnsupportedAppUsage @@ -53,10 +53,10 @@ interface IAppOpsService { // End of methods also called by native code. // Any new method exposed to native must be added after the last one, do not reorder - int noteProxyOperation(int code, in AttributionSource attributionSource, + SyncNotedAppOp noteProxyOperation(int code, in AttributionSource attributionSource, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation); - int startProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource, + SyncNotedAppOp startProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation); void finishProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource); diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 957e416986e0..6bf46457a1de 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -34,7 +34,7 @@ import android.telephony.SignalStrength; interface IBatteryStats { // These first methods are also called by native code, so must - // be kept in sync with frameworks/native/libs/binder/include/binder/IBatteryStats.h + // be kept in sync with frameworks/native/libs/binder/include_batterystats/batterystats/IBatteryStats.h void noteStartSensor(int uid, int sensor); void noteStopSensor(int uid, int sensor); void noteStartVideo(int uid); diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl index e4dd7b0629b5..20e520efc761 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -29,7 +29,7 @@ import com.android.internal.inputmethod.IVoidResultCallback; * Actual operations are implemented and handled by InputMethodManagerService. */ oneway interface IInputMethodPrivilegedOperations { - void setImeWindowStatus(int vis, int backDisposition, in IVoidResultCallback resultCallback); + void setImeWindowStatusAsync(int vis, int backDisposition); void reportStartInput(in IBinder startInputToken, in IVoidResultCallback resultCallback); void createInputContentUriToken(in Uri contentUri, in String packageName, in IIInputContentUriTokenResultCallback resultCallback); diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index 04cf3f3e546f..98c75b9ef428 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -95,8 +95,7 @@ public final class InputMethodPrivilegedOperations { } /** - * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatus(int, int, - * IVoidResultCallback)}. + * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatusAsync(int, int}. * * @param vis visibility flags * @param backDisposition disposition flags @@ -107,15 +106,13 @@ public final class InputMethodPrivilegedOperations { * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING */ @AnyThread - public void setImeWindowStatus(int vis, int backDisposition) { + public void setImeWindowStatusAsync(int vis, int backDisposition) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { return; } try { - final Completable.Void value = Completable.createVoid(); - ops.setImeWindowStatus(vis, backDisposition, ResultCallbacks.of(value)); - Completable.getResult(value); + ops.setImeWindowStatusAsync(vis, backDisposition); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index 7f0178e29d85..594a1a72ffcf 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -26,7 +26,9 @@ import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING; import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED; import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN; +import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.HardwareRendererObserver; @@ -45,6 +47,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor.Session; import com.android.internal.util.FrameworkStatsLog; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A class that allows the app to get the frame metrics from HardwareRendererObserver. * @hide @@ -57,6 +62,26 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener private static final long INVALID_ID = -1; public static final int NANOS_IN_MILLISECOND = 1_000_000; + static final int REASON_END_UNKNOWN = -1; + static final int REASON_END_NORMAL = 0; + static final int REASON_END_SURFACE_DESTROYED = 1; + static final int REASON_CANCEL_NORMAL = 16; + static final int REASON_CANCEL_NOT_BEGUN = 17; + static final int REASON_CANCEL_SAME_VSYNC = 18; + + /** @hide */ + @IntDef({ + REASON_END_UNKNOWN, + REASON_END_NORMAL, + REASON_END_SURFACE_DESTROYED, + REASON_CANCEL_NORMAL, + REASON_CANCEL_NOT_BEGUN, + REASON_CANCEL_SAME_VSYNC, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Reasons { + } + private final HardwareRendererObserver mObserver; private SurfaceControl mSurfaceControl; private final int mTraceThresholdMissedFrames; @@ -156,7 +181,14 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener // force finish the session. mHandler.postDelayed(() -> { synchronized (FrameTracker.this) { + if (DEBUG) { + Log.d(TAG, "surfaceDestroyed: " + mSession.getName() + + ", finalized=" + mMetricsFinalized + + ", info=" + mJankInfos.size() + + ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId); + } if (!mMetricsFinalized) { + end(REASON_END_SURFACE_DESTROYED); finish(mJankInfos.size() - 1); } } @@ -176,6 +208,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mSession.setTimeStamp(System.nanoTime()); Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); mRendererWrapper.addObserver(mObserver); + if (DEBUG) { + Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId); + } if (mSurfaceControl != null) { mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); } @@ -187,15 +222,25 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener /** * End the trace session of the CUJ. */ - public synchronized void end() { + public synchronized void end(@Reasons int reason) { + if (mEndVsyncId != INVALID_ID) return; mEndVsyncId = mChoreographer.getVsyncId(); + // Cancel the session if: // 1. The session begins and ends at the same vsync id. // 2. The session never begun. - if (mEndVsyncId == mBeginVsyncId || mBeginVsyncId == INVALID_ID) { - cancel(); + if (mBeginVsyncId == INVALID_ID) { + cancel(REASON_CANCEL_NOT_BEGUN); + } else if (mEndVsyncId == mBeginVsyncId) { + cancel(REASON_CANCEL_SAME_VSYNC); } else { + if (DEBUG) { + Log.d(TAG, "end: " + mSession.getName() + + ", end=" + mEndVsyncId + ", reason=" + reason); + } Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); + mSession.setReason(reason); + InteractionJankMonitor.getInstance().removeTimeout(mSession.getCuj()); } // We don't remove observer here, // will remove it when all the frame metrics in this duration are called back. @@ -205,7 +250,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener /** * Cancel the trace session of the CUJ. */ - public synchronized void cancel() { + public synchronized void cancel(@Reasons int reason) { // We don't need to end the trace section if it never begun. if (mBeginVsyncId != INVALID_ID) { Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); @@ -215,10 +260,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener // Always remove the observers in cancel call to avoid leakage. removeObservers(); + if (DEBUG) { + Log.d(TAG, "cancel: " + mSession.getName() + + ", begin=" + mBeginVsyncId + ", end=" + mEndVsyncId + ", reason=" + reason); + } + + mSession.setReason(reason); // Notify the listener the session has been cancelled. // We don't notify the listeners if the session never begun. - if (mListener != null && mBeginVsyncId != INVALID_ID) { - mListener.onNotifyCujEvents(mSession, InteractionJankMonitor.ACTION_SESSION_CANCEL); + if (mListener != null) { + mListener.onNotifyCujEvents(mSession, ACTION_SESSION_CANCEL); } } @@ -399,6 +450,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } if (DEBUG) { Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName() + + " (" + mBeginVsyncId + "," + mEndVsyncId + ")" + " totalFrames=" + totalFramesCount + " missedAppFrames=" + missedAppFramesCount + " missedSfFrames=" + missedSfFramesCounts diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index fbc92c1f99c4..6c56d421a1fb 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -19,6 +19,7 @@ package com.android.internal.jank; import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; import static com.android.internal.jank.FrameTracker.ChoreographerWrapper; +import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN; import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME; @@ -104,7 +105,7 @@ public class InteractionJankMonitor { public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME"; public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP"; @VisibleForTesting - public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events"; + public static final String PROP_NOTIFY_CUJ_EVENT = "debug.jank.notify_cuj_events"; // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; @@ -287,6 +288,10 @@ public class InteractionJankMonitor { } private void notifyEvents(Context context, String action, Session session) { + if (action.equals(ACTION_SESSION_CANCEL) + && session.getReason() == REASON_CANCEL_NOT_BEGUN) { + return; + } Intent intent = new Intent(action); intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj())); intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp()); @@ -294,6 +299,16 @@ public class InteractionJankMonitor { context.sendBroadcast(intent); } + void removeTimeout(@CujType int cujType) { + synchronized (this) { + Runnable timeout = mTimeoutActions.get(cujType); + if (timeout != null) { + mWorker.getThreadHandler().removeCallbacks(timeout); + mTimeoutActions.remove(cujType); + } + } + } + /** * Begin a trace session. * @@ -351,16 +366,11 @@ public class InteractionJankMonitor { synchronized (this) { // remove the timeout action first. - Runnable timeout = mTimeoutActions.get(cujType); - if (timeout != null) { - mWorker.getThreadHandler().removeCallbacks(timeout); - mTimeoutActions.remove(cujType); - } - + removeTimeout(cujType); FrameTracker tracker = getTracker(cujType); // Skip this call since we haven't started a trace yet. if (tracker == null) return false; - tracker.end(); + tracker.end(FrameTracker.REASON_END_NORMAL); mRunningTrackers.remove(cujType); return true; } @@ -375,16 +385,11 @@ public class InteractionJankMonitor { //TODO (163505250): This should be no-op if not in droid food rom. synchronized (this) { // remove the timeout action first. - Runnable timeout = mTimeoutActions.get(cujType); - if (timeout != null) { - mWorker.getThreadHandler().removeCallbacks(timeout); - mTimeoutActions.remove(cujType); - } - + removeTimeout(cujType); FrameTracker tracker = getTracker(cujType); // Skip this call since we haven't started a trace yet. if (tracker == null) return false; - tracker.cancel(); + tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); mRunningTrackers.remove(cujType); return true; } @@ -509,6 +514,8 @@ public class InteractionJankMonitor { @CujType private int mCujType; private long mTimeStamp; + @FrameTracker.Reasons + private int mReason = FrameTracker.REASON_END_UNKNOWN; public Session(@CujType int cujType) { mCujType = cujType; @@ -543,5 +550,13 @@ public class InteractionJankMonitor { public long getTimeStamp() { return mTimeStamp; } + + public void setReason(@FrameTracker.Reasons int reason) { + mReason = reason; + } + + public int getReason() { + return mReason; + } } } diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java index 586607e413d9..2f4958223270 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -44,14 +44,15 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final long measuredEnergyUC = batteryStats.getScreenDozeMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(measuredEnergyUC, query); final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - final double powerMah = getMeasuredOrEstimatedPower( - batteryStats.getScreenDozeMeasuredBatteryConsumptionUC(), - mPowerEstimator, durationMs, query.shouldForceUsePowerProfileModel()); + final double powerMah = getMeasuredOrEstimatedPower(powerModel, + measuredEnergyUC, mPowerEstimator, durationMs); builder.getOrCreateSystemBatteryConsumerBuilder( SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah, powerModel) .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs); } @@ -64,10 +65,12 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { + final long measuredEnergyUC = batteryStats.getScreenDozeMeasuredBatteryConsumptionUC(); final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType); - final double powerMah = getMeasuredOrEstimatedPower( + final int powerModel = getPowerModel(measuredEnergyUC); + final double powerMah = getMeasuredOrEstimatedPower(powerModel, batteryStats.getScreenDozeMeasuredBatteryConsumptionUC(), - mPowerEstimator, durationMs, false); + mPowerEstimator, durationMs); if (powerMah > 0) { BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0); bs.usagePowerMah = powerMah; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index b0920b0100da..ac72d290b8b2 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -41,7 +41,6 @@ import android.net.NetworkStats; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.BatteryManager; -import android.os.BatteryProperty; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; @@ -58,7 +57,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; @@ -150,14 +148,6 @@ import java.util.concurrent.locks.ReentrantLock; public class BatteryStatsImpl extends BatteryStats { private static final String TAG = "BatteryStatsImpl"; private static final boolean DEBUG = false; - - // TODO(b/169376495): STOPSHIP if true - private static final boolean DEBUG_FOREGROUND_STATS = true; - - private static final boolean ENABLE_FOREGROUND_STATS_COLLECTION = - DEBUG_FOREGROUND_STATS && SystemProperties.getBoolean( - "debug.battery_foreground_stats_collection", false); - public static final boolean DEBUG_ENERGY = false; private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY; private static final boolean DEBUG_BINDER_STATS = false; @@ -752,37 +742,6 @@ public class BatteryStatsImpl extends BatteryStats { long mTrackRunningHistoryElapsedRealtimeMs = 0; long mTrackRunningHistoryUptimeMs = 0; - private static final int FOREGROUND_UID_INITIAL_CAPACITY = 10; - private static final int INVALID_UID = -1; - - private final IntArray mForegroundUids = ENABLE_FOREGROUND_STATS_COLLECTION - ? new IntArray(FOREGROUND_UID_INITIAL_CAPACITY) : null; - - // Last recorded battery energy capacity. - // This is used for computing foregrund power per application. - // See: PowerForUid below - private long mLastBatteryEnergyCapacityNwh = 0; - - private static final class PowerForUid { - public long energyNwh = 0; - // Same as energyNwh, but not tracked for the first 2 minutes; - public long filteredEnergyNwh = 0; - public double totalHours = 0; - public long baseTimeMs = 0; - - double computePower() { - // units in nW - return totalHours != 0 ? energyNwh / totalHours : -1.0; - } - - double computeFilteredPower() { - // units in nW - return totalHours != 0 ? filteredEnergyNwh / totalHours : -1.0; - } - } - private final HashMap<Integer, PowerForUid> mUidToPower = ENABLE_FOREGROUND_STATS_COLLECTION - ? new HashMap<>() : null; - @NonNull final BatteryStatsHistory mBatteryStatsHistory; @@ -1094,7 +1053,6 @@ public class BatteryStatsImpl extends BatteryStats { private int mNumConnectivityChange; private int mBatteryVoltageMv = -1; - private int mBatteryChargeUah = -1; private int mEstimatedBatteryCapacityMah = -1; private int mMinLearnedBatteryCapacityUah = -1; @@ -4020,49 +3978,6 @@ public class BatteryStatsImpl extends BatteryStats { // TODO(b/155216561): It is possible for isolated uids to be in a higher // state than its parent uid. We should track the highest state within the union of host // and isolated uids rather than only the parent uid. - - - int uidState = mapToInternalProcessState(state); - - boolean isForeground = (uidState == Uid.PROCESS_STATE_TOP) - || (uidState == Uid.PROCESS_STATE_FOREGROUND); - - - if (ENABLE_FOREGROUND_STATS_COLLECTION) { - boolean previouslyInForegrond = false; - for (int i = 0; i < mForegroundUids.size(); i++) { - if (mForegroundUids.get(i) == uid) { - previouslyInForegrond = true; - if (!isForeground) { - // If we were previously in the foreground, remove the uid - // from the foreground set and dirty the slot. - mForegroundUids.set(i, INVALID_UID); - final PowerForUid pfu = - mUidToPower.computeIfAbsent(uid, unused -> new PowerForUid()); - pfu.baseTimeMs = 0; - break; - } - } - } - - if (!previouslyInForegrond && isForeground) { - boolean addedToForeground = false; - // Check if we have a free slot to clobber... - for (int i = 0; i < mForegroundUids.size(); i++) { - if (mForegroundUids.get(i) == INVALID_UID) { - addedToForeground = true; - mForegroundUids.set(i, uid); - break; - } - } - - // ...if not, append to the end of the array. - if (!addedToForeground) { - mForegroundUids.add(uid); - } - } - } - FrameworkStatsLog.write(FrameworkStatsLog.UID_PROCESS_STATE_CHANGED, uid, ActivityManager.processStateAmToProto(state)); getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) @@ -13547,7 +13462,6 @@ public class BatteryStatsImpl extends BatteryStats { doWrite = true; resetAllStatsLocked(mSecUptime, mSecRealtime); if (chargeUah > 0 && level > 0) { - mBatteryChargeUah = chargeUah; // Only use the reported coulomb charge value if it is supported and reported. mEstimatedBatteryCapacityMah = (int) ((chargeUah / 1000) / (level / 100.0)); } @@ -13720,48 +13634,7 @@ public class BatteryStatsImpl extends BatteryStats { startRecordingHistory(elapsedRealtimeMs, uptimeMs, true); } } - mBatteryVoltageMv = voltageMv; - - if (ENABLE_FOREGROUND_STATS_COLLECTION) { - if (onBattery) { - final long energyNwh = (voltageMv * (long) chargeUah); - final long energyDelta = mLastBatteryEnergyCapacityNwh - energyNwh; - for (int i = 0; i < mForegroundUids.size(); i++) { - final int uid = mForegroundUids.get(i); - if (uid == INVALID_UID) { - continue; - } - final PowerForUid pfu = mUidToPower - .computeIfAbsent(uid, unused -> new PowerForUid()); - if (pfu.baseTimeMs <= 0) { - pfu.baseTimeMs = currentTimeMs; - } else { - // Check if mLastBatteryEnergyCapacityNwh > energyNwh, - // to make sure we only count discharges - if (energyDelta > 0) { - pfu.energyNwh += energyDelta; - // Convert from milliseconds to hours - // 1000 ms per second * 3600 seconds per hour - pfu.totalHours += ((double) (currentTimeMs - pfu.baseTimeMs) - / (1.0 * 1000 * 60 * 60)); - // Now convert from 2 minutes to hours - // 2 minutes = 1/30 of an hour - if (pfu.totalHours > (2.0 / 60)) { - pfu.filteredEnergyNwh += energyDelta; - } - - } - pfu.baseTimeMs = currentTimeMs; - } - } - mLastBatteryEnergyCapacityNwh = energyNwh; - } else if (onBattery != mOnBattery) { - // Transition to onBattery = false - mUidToPower.values().forEach(v -> v.baseTimeMs = 0); - } - } - mCurrentBatteryLevel = level; if (mDischargePlugLevel < 0) { mDischargePlugLevel = level; @@ -16679,48 +16552,6 @@ public class BatteryStatsImpl extends BatteryStats { } public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) { - if (ENABLE_FOREGROUND_STATS_COLLECTION) { - long actualChargeUah = -1; - long actualEnergyNwh = -1; - try { - IBatteryPropertiesRegistrar registrar = - IBatteryPropertiesRegistrar.Stub.asInterface( - ServiceManager.getService("batteryproperties")); - if (registrar != null) { - BatteryProperty prop = new BatteryProperty(); - if (registrar.getProperty( - BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, prop) == 0) { - actualChargeUah = prop.getLong(); - } - prop = new BatteryProperty(); - if (registrar.getProperty( - BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER, prop) == 0) { - actualEnergyNwh = prop.getLong(); - } - } - } catch (RemoteException e) { - // Ignore. - } - pw.printf("ActualCharge (uAh): %d\n", (int) actualChargeUah); - pw.printf("ActualEnergy (nWh): %d\n", actualEnergyNwh); - pw.printf("mBatteryCharge (uAh): %d\n", mBatteryChargeUah); - pw.printf("mBatteryVolts (mV): %d\n", mBatteryVoltageMv); - pw.printf("est energy (nWh): %d\n", mBatteryVoltageMv * (long) mBatteryChargeUah); - pw.printf("mEstimatedBatteryCapacity (mAh): %d\n", mEstimatedBatteryCapacityMah); - pw.printf("mMinLearnedBatteryCapacity (uAh): %d\n", mMinLearnedBatteryCapacityUah); - pw.printf("mMaxLearnedBatteryCapacity (uAh): %d\n", mMaxLearnedBatteryCapacityUah); - pw.printf("est. capacity: %f\n", - (float) actualChargeUah / (mEstimatedBatteryCapacityMah * 1000)); - pw.printf("mCurrentBatteryLevel: %d\n", mCurrentBatteryLevel); - pw.println("Total Power per app:"); - mUidToPower.entrySet().forEach(e -> - pw.printf("Uid: %d, Total watts (nW): %f\n", - e.getKey(), e.getValue().computePower())); - pw.println("Total Power per app after first 2 minutes initial launch:"); - mUidToPower.entrySet().forEach(e -> - pw.printf("Uid: %d, Total watts (nW): %f\n", - e.getKey(), e.getValue().computeFilteredPower())); - } if (DEBUG) { pw.println("mOnBatteryTimeBase:"); mOnBatteryTimeBase.dump(pw, " "); diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index f8ae0c1858f8..49c564b8a737 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -132,9 +132,12 @@ public class BatteryUsageStatsProvider { // TODO(b/174186358): read extra time component number from configuration final int customTimeComponentCount = 0; - final BatteryUsageStats.Builder batteryUsageStatsBuilder = - new BatteryUsageStats.Builder(customPowerComponentCount, customTimeComponentCount) - .setStatsStartTimestamp(mStats.getStartClockTime()); + final boolean includePowerModels = (query.getFlags() + & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; + + final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder( + customPowerComponentCount, customTimeComponentCount, includePowerModels); + batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime()); SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats(); for (int i = uidStats.size() - 1; i >= 0; i--) { diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 14b870575769..30a35367d6f5 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -57,6 +57,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false; public static final boolean DEFAULT_TRACK_DIRECT_CALLING_UID = true; public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false; + public static final boolean DEFAULT_COLLECT_LATENCY_DATA = false; public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 1500; private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_"; @@ -97,11 +98,13 @@ public class BinderCallsStats implements BinderInternal.Observer { private boolean mTrackDirectCallingUid = DEFAULT_TRACK_DIRECT_CALLING_UID; private boolean mTrackScreenInteractive = DEFAULT_TRACK_SCREEN_INTERACTIVE; private boolean mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS; + private boolean mCollectLatencyData = DEFAULT_COLLECT_LATENCY_DATA; private CachedDeviceState.Readonly mDeviceState; private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; private static final int CALL_STATS_OBSERVER_DEBOUNCE_MILLIS = 5000; + private BinderLatencyObserver mLatencyObserver; private BinderInternal.CallStatsObserver mCallStatsObserver; private ArraySet<Integer> mSendUidsToObserver = new ArraySet<>(32); private final Handler mCallStatsObserverHandler; @@ -153,11 +156,16 @@ public class BinderCallsStats implements BinderInternal.Observer { public Handler getHandler() { return new Handler(Looper.getMainLooper()); } + + public BinderLatencyObserver getLatencyObserver() { + return new BinderLatencyObserver(new BinderLatencyObserver.Injector()); + } } public BinderCallsStats(Injector injector) { this.mRandom = injector.getRandomGenerator(); this.mCallStatsObserverHandler = injector.getHandler(); + this.mLatencyObserver = injector.getLatencyObserver(); } public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) { @@ -207,7 +215,10 @@ public class BinderCallsStats implements BinderInternal.Observer { if (mRecordingAllTransactionsForUid || s.recordedCall) { s.cpuTimeStarted = getThreadTimeMicro(); s.timeStarted = getElapsedRealtimeMicro(); + } else if (mCollectLatencyData) { + s.timeStarted = getElapsedRealtimeMicro(); } + return s; } @@ -232,6 +243,10 @@ public class BinderCallsStats implements BinderInternal.Observer { private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize, int workSourceUid) { + if (mCollectLatencyData) { + mLatencyObserver.callEnded(s); + } + UidEntry uidEntry = null; final boolean recordCall; if (s.recordedCall) { @@ -757,6 +772,17 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + /** Whether to collect latency histograms. */ + public void setCollectLatencyData(boolean collectLatencyData) { + mCollectLatencyData = collectLatencyData; + } + + /** Whether to collect latency histograms. */ + @VisibleForTesting + public boolean getCollectLatencyData() { + return mCollectLatencyData; + } + public void reset() { synchronized (mLock) { mCallStatsCount = 0; @@ -768,6 +794,8 @@ public class BinderCallsStats implements BinderInternal.Observer { mBatteryStopwatch.reset(); } mRecordingAllTransactionsForUid = false; + // Do not reset the latency observer as binder stats and latency will be pushed to WW + // at different intervals so the resets should not be coupled. } } @@ -1009,6 +1037,10 @@ public class BinderCallsStats implements BinderInternal.Observer { return mExceptionCounts; } + public BinderLatencyObserver getLatencyObserver() { + return mLatencyObserver; + } + @VisibleForTesting public static <T> List<T> getHighestValues(List<T> list, ToDoubleFunction<T> toDouble, double percentile) { diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java new file mode 100644 index 000000000000..92b495284de2 --- /dev/null +++ b/core/java/com/android/internal/os/BinderLatencyObserver.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.annotation.Nullable; +import android.os.Binder; +import android.os.SystemClock; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BinderInternal.CallSession; + +import java.util.ArrayList; +import java.util.Random; + +/** Collects statistics about Binder call latency per calling API and method. */ +public class BinderLatencyObserver { + private static final String TAG = "BinderLatencyObserver"; + public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10; + + // This is not the final data structure - we will eventually store latency histograms instead of + // raw samples as it is much more memory / disk space efficient. + // TODO(b/179999191): change this to store the histogram. + // TODO(b/179999191): pre allocate the array size so we would not have to resize this. + @GuardedBy("mLock") + private final ArrayMap<LatencyDims, ArrayList<Long>> mLatencySamples = new ArrayMap<>(); + private final Object mLock = new Object(); + + // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out + // of 100 requests. + private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT; + private final Random mRandom; + + /** Injector for {@link BinderLatencyObserver}. */ + public static class Injector { + public Random getRandomGenerator() { + return new Random(); + } + } + + public BinderLatencyObserver(Injector injector) { + mRandom = injector.getRandomGenerator(); + } + + /** Should be called when a Binder call completes, will store latency data. */ + public void callEnded(@Nullable CallSession s) { + if (s == null || s.exceptionThrown || !shouldKeepSample()) { + return; + } + + LatencyDims dims = new LatencyDims(s.binderClass, s.transactionCode); + long callDuration = getElapsedRealtimeMicro() - s.timeStarted; + + synchronized (mLock) { + if (!mLatencySamples.containsKey(dims)) { + mLatencySamples.put(dims, new ArrayList<Long>()); + } + + mLatencySamples.get(dims).add(callDuration); + } + } + + protected long getElapsedRealtimeMicro() { + return SystemClock.elapsedRealtimeNanos() / 1000; + } + + protected boolean shouldKeepSample() { + return mRandom.nextInt() % mPeriodicSamplingInterval == 0; + } + + /** Updates the sampling interval. */ + public void setSamplingInterval(int samplingInterval) { + if (samplingInterval <= 0) { + Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): " + + samplingInterval); + return; + } + + synchronized (mLock) { + if (samplingInterval != mPeriodicSamplingInterval) { + mPeriodicSamplingInterval = samplingInterval; + reset(); + } + } + } + + /** Resets the sample collection. */ + public void reset() { + synchronized (mLock) { + mLatencySamples.clear(); + } + } + + /** Container for binder latency information. */ + public static class LatencyDims { + // Binder interface descriptor. + private Class<? extends Binder> mBinderClass; + // Binder transaction code. + private int mTransactionCode; + // Cached hash code, 0 if not set yet. + private int mHashCode = 0; + + public LatencyDims(Class<? extends Binder> binderClass, int transactionCode) { + this.mBinderClass = binderClass; + this.mTransactionCode = transactionCode; + } + + public Class<? extends Binder> getBinderClass() { + return mBinderClass; + } + + public int getTransactionCode() { + return mTransactionCode; + } + + @Override + public boolean equals(final Object other) { + if (other == null || !(other instanceof LatencyDims)) { + return false; + } + LatencyDims o = (LatencyDims) other; + return mTransactionCode == o.getTransactionCode() && mBinderClass == o.getBinderClass(); + } + + @Override + public int hashCode() { + if (mHashCode != 0) { + return mHashCode; + } + int hash = mTransactionCode; + hash = 31 * hash + mBinderClass.hashCode(); + mHashCode = hash; + return hash; + } + } + + @VisibleForTesting + public ArrayMap<LatencyDims, ArrayList<Long>> getLatencySamples() { + return mLatencySamples; + } +} diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index 75a98133a8f3..a418dfff5d8f 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -15,8 +15,6 @@ */ package com.android.internal.os; -import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; - import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.BatteryStats.ControllerActivityCounter; @@ -75,12 +73,13 @@ public class BluetoothPowerCalculator extends PowerCalculator { } } - final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ? - POWER_DATA_UNAVAILABLE : batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); + final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(measuredChargeUC, query); final ControllerActivityCounter activityCounter = batteryStats.getBluetoothControllerActivity(); final long systemDurationMs = calculateDuration(activityCounter); - final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter); + final double systemPowerMah = + calculatePowerMah(powerModel, measuredChargeUC, activityCounter); // Subtract what the apps used, but clamp to 0. final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs); @@ -92,23 +91,22 @@ public class BluetoothPowerCalculator extends PowerCalculator { .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, systemComponentDurationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, - Math.max(systemPowerMah, total.powerMah)) + Math.max(systemPowerMah, total.powerMah), powerModel) .setPowerConsumedByApps(total.powerMah); } private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total, BatteryUsageStatsQuery query) { - - final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ? - POWER_DATA_UNAVAILABLE : + final long measuredChargeUC = app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(measuredChargeUC, query); final ControllerActivityCounter activityCounter = app.getBatteryStatsUid().getBluetoothControllerActivity(); final long durationMs = calculateDuration(activityCounter); - final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter); + final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter); app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah); + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel); total.durationMs += durationMs; total.powerMah += powerMah; @@ -132,10 +130,12 @@ public class BluetoothPowerCalculator extends PowerCalculator { BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(measuredChargeUC); final ControllerActivityCounter activityCounter = batteryStats.getBluetoothControllerActivity(); final long systemDurationMs = calculateDuration(activityCounter); - final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter); + final double systemPowerMah = + calculatePowerMah(powerModel, measuredChargeUC, activityCounter); // Subtract what the apps used, but clamp to 0. final double powerMah = Math.max(0, systemPowerMah - total.powerMah); @@ -165,9 +165,10 @@ public class BluetoothPowerCalculator extends PowerCalculator { PowerAndDuration total) { final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(measuredChargeUC); final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity(); final long durationMs = calculateDuration(activityCounter); - final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter); + final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter); app.bluetoothRunningTimeMs = durationMs; app.bluetoothPowerMah = powerMah; @@ -189,10 +190,12 @@ public class BluetoothPowerCalculator extends PowerCalculator { } /** Returns bluetooth power usage based on the best data available. */ - private double calculatePowerMah(long measuredChargeUC, ControllerActivityCounter counter) { - if (measuredChargeUC != POWER_DATA_UNAVAILABLE) { + private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel, + long measuredChargeUC, ControllerActivityCounter counter) { + if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { return uCtoMah(measuredChargeUC); } + if (counter == null) { return 0; } diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index b15543a26b4c..4aafec555182 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -91,10 +91,12 @@ public class CpuPowerCalculator extends PowerCalculator { private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, BatteryUsageStatsQuery query, Result result) { - calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, - query.shouldForceUsePowerProfileModel(), result); + final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + calculatePowerAndDuration(u, powerModel, consumptionUC, BatteryStats.STATS_SINCE_CHARGED, + result); - app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah) + app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah, powerModel) .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs) .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, result.durationFgMs) @@ -114,7 +116,9 @@ public class CpuPowerCalculator extends PowerCalculator { } private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) { - calculatePowerAndDuration(u, statsType, false, result); + final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC); + calculatePowerAndDuration(u, powerModel, consumptionUC, statsType, result); app.cpuPowerMah = result.powerMah; app.cpuTimeMs = result.durationMs; @@ -122,16 +126,20 @@ public class CpuPowerCalculator extends PowerCalculator { app.packageWithHighestDrain = result.packageWithHighestDrain; } - private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, - boolean forceUsePowerProfileModel, Result result) { + private void calculatePowerAndDuration(BatteryStats.Uid u, + @BatteryConsumer.PowerModel int powerModel, long consumptionUC, int statsType, + Result result) { long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; final double powerMah; - final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC(); - if (forceUsePowerProfileModel || consumptionUC == BatteryStats.POWER_DATA_UNAVAILABLE) { - powerMah = calculateUidModeledPowerMah(u, statsType); - } else { - powerMah = uCtoMah(consumptionUC); + switch(powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + powerMah = uCtoMah(consumptionUC); + break; + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + powerMah = calculateUidModeledPowerMah(u, statsType); + break; } if (DEBUG && (durationMs != 0 || powerMah != 0)) { diff --git a/core/java/com/android/internal/os/DmabufInfoReader.java b/core/java/com/android/internal/os/DmabufInfoReader.java new file mode 100644 index 000000000000..786a6eedf343 --- /dev/null +++ b/core/java/com/android/internal/os/DmabufInfoReader.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.annotation.Nullable; + +/** Wrapper around libdmabufinfo. */ +public final class DmabufInfoReader { + private DmabufInfoReader() {} + + /** Process dma-buf stats. */ + public static final class ProcessDmabuf { + /** Size of buffers retained by the process. */ + public final int retainedSizeKb; + /** Number of buffers retained by the process. */ + public final int retainedBuffersCount; + /** Size of buffers mapped to the address space. */ + public final int mappedSizeKb; + /** Count of buffers mapped to the address space. */ + public final int mappedBuffersCount; + + ProcessDmabuf(int retainedSizeKb, int retainedBuffersCount, + int mappedSizeKb, int mappedBuffersCount) { + this.retainedSizeKb = retainedSizeKb; + this.retainedBuffersCount = retainedBuffersCount; + this.mappedSizeKb = mappedSizeKb; + this.mappedBuffersCount = mappedBuffersCount; + } + } + + /** + * Return stats for DMA-BUFs retained by process pid or null if the DMA-BUF + * stats could not be read. + */ + @Nullable + public static native ProcessDmabuf getProcessStats(int pid); +} diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java index 97c4fd8b7b7a..0e0870de4f5f 100644 --- a/core/java/com/android/internal/os/GnssPowerCalculator.java +++ b/core/java/com/android/internal/os/GnssPowerCalculator.java @@ -52,28 +52,30 @@ public class GnssPowerCalculator extends PowerCalculator { builder.getUidBatteryConsumerBuilders(); for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); - calculateApp(app, app.getBatteryStatsUid(), rawRealtimeUs, rawUptimeUs, query, - averageGnssPowerMa); + final long consumptionUC = + app.getBatteryStatsUid().getGnssMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + calculateApp(app, app.getBatteryStatsUid(), powerModel, rawRealtimeUs, + averageGnssPowerMa, consumptionUC); } } private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, - double averageGnssPowerMa) { + @BatteryConsumer.PowerModel int powerModel, long rawRealtimeUs, + double averageGnssPowerMa, long measuredChargeUC) { final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - - final long measuredChargeUC = u.getGnssMeasuredBatteryConsumptionUC(); - final boolean isMeasuredPowerAvailable = !query.shouldForceUsePowerProfileModel() - && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE; - final double powerMah; - if (isMeasuredPowerAvailable) { - powerMah = uCtoMah(measuredChargeUC); - } else { - powerMah = computePower(durationMs, averageGnssPowerMa); + switch (powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + powerMah = uCtoMah(measuredChargeUC); + break; + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + powerMah = computePower(durationMs, averageGnssPowerMa); } + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS, durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, powerMah); + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, powerMah, powerModel); } @Override @@ -83,24 +85,28 @@ public class GnssPowerCalculator extends PowerCalculator { for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper app = sippers.get(i); if (app.drainType == BatterySipper.DrainType.APP) { - calculateApp(app, app.uidObj, rawRealtimeUs, statsType, averageGnssPowerMa, false); + final long consumptionUC = + app.uidObj.getGnssMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC); + calculateApp(app, app.uidObj, powerModel, rawRealtimeUs, averageGnssPowerMa, + consumptionUC); } } } - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - int statsType, double averageGnssPowerMa, boolean shouldForceUsePowerProfileModel) { + private void calculateApp(BatterySipper app, BatteryStats.Uid u, + @BatteryConsumer.PowerModel int powerModel, long rawRealtimeUs, + double averageGnssPowerMa, long measuredChargeUC) { final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - final long measuredChargeUC = u.getGnssMeasuredBatteryConsumptionUC(); - final boolean isMeasuredPowerAvailable = shouldForceUsePowerProfileModel - && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE; - final double powerMah; - if (isMeasuredPowerAvailable) { - powerMah = uCtoMah(measuredChargeUC); - } else { - powerMah = computePower(durationMs, averageGnssPowerMa); + switch (powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + powerMah = uCtoMah(measuredChargeUC); + break; + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + powerMah = computePower(durationMs, averageGnssPowerMa); } app.gpsTimeMs = durationMs; diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java index f6ef30cba369..d441d4529aca 100644 --- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java +++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java @@ -97,37 +97,41 @@ public class MobileRadioPowerCalculator extends PowerCalculator { for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); final BatteryStats.Uid uid = app.getBatteryStatsUid(); - calculateApp(app, uid, powerPerPacketMah, total, - query.shouldForceUsePowerProfileModel()); + calculateApp(app, uid, powerPerPacketMah, total, query); } - calculateRemaining(total, batteryStats, rawRealtimeUs, - query.shouldForceUsePowerProfileModel()); + final long consumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + calculateRemaining(total, powerModel, batteryStats, rawRealtimeUs, consumptionUC); if (total.remainingPowerMah != 0 || total.totalAppPowerMah != 0) { builder.getOrCreateSystemBatteryConsumerBuilder( - SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO) + SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO) .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO, total.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, - total.remainingPowerMah + total.totalAppPowerMah) + total.remainingPowerMah + total.totalAppPowerMah, + powerModel) .setPowerConsumedByApps(total.totalAppPowerMah); } } private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, double powerPerPacketMah, PowerAndDuration total, - boolean shouldForceUsePowerProfileModel) { + BatteryUsageStatsQuery query) { final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED); total.totalAppDurationMs += radioActiveDurationMs; - final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs, - shouldForceUsePowerProfileModel); + final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + final double powerMah = calculatePower(u, powerModel, powerPerPacketMah, + radioActiveDurationMs, consumptionUC); total.totalAppPowerMah += powerMah; app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO, - radioActiveDurationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah); + radioActiveDurationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah, + powerModel); } @Override @@ -140,12 +144,14 @@ public class MobileRadioPowerCalculator extends PowerCalculator { final BatterySipper app = sippers.get(i); if (app.drainType == BatterySipper.DrainType.APP) { final BatteryStats.Uid u = app.uidObj; - calculateApp(app, u, statsType, mobilePowerPerPacket, total, false); + calculateApp(app, u, statsType, mobilePowerPerPacket, total); } } BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); - calculateRemaining(total, batteryStats, rawRealtimeUs, false); + final long consumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC); + calculateRemaining(total, powerModel, batteryStats, rawRealtimeUs, consumptionUC); if (total.remainingPowerMah != 0) { if (total.signalDurationMs != 0) { radio.noCoveragePercent = @@ -162,12 +168,13 @@ public class MobileRadioPowerCalculator extends PowerCalculator { } private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, - double powerPerPacketMah, PowerAndDuration total, - boolean shouldForceUsePowerProfileModel) { + double powerPerPacketMah, PowerAndDuration total) { app.mobileActive = calculateDuration(u, statsType); - app.mobileRadioPowerMah = calculatePower(u, powerPerPacketMah, app.mobileActive, - shouldForceUsePowerProfileModel); + final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC); + app.mobileRadioPowerMah = calculatePower(u, powerModel, powerPerPacketMah, app.mobileActive, + consumptionUC); total.totalAppDurationMs += app.mobileActive; // Add cost of mobile traffic. @@ -193,13 +200,9 @@ public class MobileRadioPowerCalculator extends PowerCalculator { return u.getMobileRadioActiveTime(statsType) / 1000; } - private double calculatePower(BatteryStats.Uid u, double powerPerPacketMah, - long radioActiveDurationMs, boolean shouldForceUsePowerProfileModel) { - - final long measuredChargeUC = u.getMobileRadioMeasuredBatteryConsumptionUC(); - final boolean isMeasuredPowerAvailable = !shouldForceUsePowerProfileModel - && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE; - if (isMeasuredPowerAvailable) { + private double calculatePower(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel, + double powerPerPacketMah, long radioActiveDurationMs, long measuredChargeUC) { + if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { return uCtoMah(measuredChargeUC); } @@ -220,23 +223,20 @@ public class MobileRadioPowerCalculator extends PowerCalculator { } } - private void calculateRemaining(MobileRadioPowerCalculator.PowerAndDuration total, - BatteryStats batteryStats, long rawRealtimeUs, - boolean shouldForceUsePowerProfileModel) { + private void calculateRemaining(PowerAndDuration total, + @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, + long rawRealtimeUs, long consumptionUC) { long signalTimeMs = 0; double powerMah = 0; - final long measuredChargeUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC(); - final boolean isMeasuredPowerAvailable = !shouldForceUsePowerProfileModel - && measuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE; - if (isMeasuredPowerAvailable) { - powerMah = uCtoMah(measuredChargeUC); + if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { + powerMah = uCtoMah(consumptionUC); } for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) { long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; - if (!isMeasuredPowerAvailable) { + if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { final double p = calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i); if (DEBUG && p != 0) { Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power=" @@ -256,7 +256,7 @@ public class MobileRadioPowerCalculator extends PowerCalculator { BatteryStats.STATS_SINCE_CHARGED) / 1000; long remainingActiveTimeMs = radioActiveTimeMs - total.totalAppDurationMs; - if (!isMeasuredPowerAvailable) { + if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { final double p = calcScanTimePowerMah(scanningTimeMs); if (DEBUG && p != 0) { Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge( diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java index 72385e273cf0..d139b4f0aa9a 100644 --- a/core/java/com/android/internal/os/PowerCalculator.java +++ b/core/java/com/android/internal/os/PowerCalculator.java @@ -15,6 +15,8 @@ */ package com.android.internal.os; +import android.annotation.NonNull; +import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; @@ -114,19 +116,48 @@ public abstract class PowerCalculator { public void reset() { } + protected static @BatteryConsumer.PowerModel int getPowerModel( + long measuredEnergyUC, @NonNull BatteryUsageStatsQuery query) { + if (measuredEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE + && !query.shouldForceUsePowerProfileModel()) { + return BatteryConsumer.POWER_MODEL_MEASURED_ENERGY; + } + return BatteryConsumer.POWER_MODEL_POWER_PROFILE; + } + + protected static @BatteryConsumer.PowerModel int getPowerModel(long measuredEnergyUC) { + return measuredEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE + ? BatteryConsumer.POWER_MODEL_MEASURED_ENERGY + : BatteryConsumer.POWER_MODEL_POWER_PROFILE; + } + /** * Returns either the measured energy converted to mAh or a usage-based estimate. */ - protected static double getMeasuredOrEstimatedPower(long measuredEnergyUC, - UsageBasedPowerEstimator powerEstimator, long durationMs, - boolean forceUsePowerProfileModel) { - if (measuredEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE - && !forceUsePowerProfileModel) { + protected static double getMeasuredOrEstimatedPower(@BatteryConsumer.PowerModel int powerModel, + long measuredEnergyUC, UsageBasedPowerEstimator powerEstimator, long durationMs) { + switch (powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + return uCtoMah(measuredEnergyUC); + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + return powerEstimator.calculatePower(durationMs); + } + } + + /** + * Returns either the measured energy converted to mAh or a usage-based estimate. + */ + protected static double getMeasuredOrEstimatedPower( + long measuredEnergyUC, UsageBasedPowerEstimator powerEstimator, long durationMs) { + if (measuredEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE) { return uCtoMah(measuredEnergyUC); + } else { + return powerEstimator.calculatePower(durationMs); } - return powerEstimator.calculatePower(durationMs); } + /** * Converts charge in mAh to string. */ diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index ad574440f216..0267def918c2 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -62,9 +62,10 @@ public class ScreenPowerCalculator extends PowerCalculator { long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); - final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration, - batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, - query.shouldForceUsePowerProfileModel()); + final long consumptionUC = batteryStats.getScreenOnMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + calculateTotalDurationAndPower(totalPowerAndDuration, powerModel, batteryStats, + rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, consumptionUC); double totalAppPower = 0; @@ -73,29 +74,32 @@ public class ScreenPowerCalculator extends PowerCalculator { // but the method depends on the data source. final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = builder.getUidBatteryConsumerBuilders(); - if (useEnergyData) { - final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); - for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { - final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); - calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.getBatteryStatsUid(), + switch (powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.getBatteryStatsUid(), + rawRealtimeUs); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, + appPowerAndDuration.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, + appPowerAndDuration.powerMah, powerModel); + totalAppPower += appPowerAndDuration.powerMah; + } + break; + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration, rawRealtimeUs); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, - appPowerAndDuration.durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, - appPowerAndDuration.powerMah); - totalAppPower += appPowerAndDuration.powerMah; - } - } else { - smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration, - rawRealtimeUs); - totalAppPower = totalPowerAndDuration.powerMah; + totalAppPower = totalPowerAndDuration.powerMah; } builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, totalPowerAndDuration.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, - Math.max(totalPowerAndDuration.powerMah, totalAppPower)) + Math.max(totalPowerAndDuration.powerMah, totalAppPower), powerModel) .setPowerConsumedByApps(totalAppPower); } @@ -106,8 +110,10 @@ public class ScreenPowerCalculator extends PowerCalculator { public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); - final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration, - batteryStats, rawRealtimeUs, statsType, false); + final long consumptionUC = batteryStats.getScreenOnMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC); + calculateTotalDurationAndPower(totalPowerAndDuration, powerModel, batteryStats, + rawRealtimeUs, statsType, consumptionUC); if (totalPowerAndDuration.powerMah == 0) { return; } @@ -121,41 +127,42 @@ public class ScreenPowerCalculator extends PowerCalculator { // Now deal with each app's BatterySipper. The results are stored in the screenPowerMah // field, which is considered smeared, but the method depends on the data source. - if (useEnergyData) { - final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); - for (int i = sippers.size() - 1; i >= 0; i--) { - final BatterySipper app = sippers.get(i); - if (app.drainType == BatterySipper.DrainType.APP) { - calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.uidObj, rawRealtimeUs); - app.screenPowerMah = appPowerAndDuration.powerMah; + switch (powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + final PowerAndDuration appPowerAndDuration = new PowerAndDuration(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.uidObj, + rawRealtimeUs); + app.screenPowerMah = appPowerAndDuration.powerMah; + } } - } - } else { - smearScreenBatterySipper(sippers, bs, rawRealtimeUs); + break; + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + smearScreenBatterySipper(sippers, bs, rawRealtimeUs); } } /** - * Stores duration and power information in totalPowerAndDuration and returns true if measured - * energy data is available and should be used by the model. + * Stores duration and power information in totalPowerAndDuration. */ - private boolean calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration, - BatteryStats batteryStats, long rawRealtimeUs, int statsType, - boolean forceUsePowerProfileModel) { + private void calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration, + @BatteryConsumer.PowerModel int powerModel, BatteryStats batteryStats, + long rawRealtimeUs, int statsType, long consumptionUC) { totalPowerAndDuration.durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType); - if (!forceUsePowerProfileModel) { - final long chargeUC = batteryStats.getScreenOnMeasuredBatteryConsumptionUC(); - if (chargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) { - totalPowerAndDuration.powerMah = uCtoMah(chargeUC); - return true; - } + switch (powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + totalPowerAndDuration.powerMah = uCtoMah(consumptionUC); + break; + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + totalPowerAndDuration.powerMah = calculateTotalPowerFromBrightness(batteryStats, + rawRealtimeUs, statsType, totalPowerAndDuration.durationMs); } - - totalPowerAndDuration.powerMah = calculateTotalPowerFromBrightness(batteryStats, - rawRealtimeUs, statsType, totalPowerAndDuration.durationMs); - return false; } private void calculateAppUsingMeasuredEnergy(PowerAndDuration appPowerAndDuration, @@ -245,7 +252,8 @@ public class ScreenPowerCalculator extends PowerCalculator { final long durationMs = activityTimeArray.get(app.getUid(), 0); final double powerMah = totalScreenPowerMah * durationMs / totalActivityTimeMs; app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah); + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah, + BatteryConsumer.POWER_MODEL_POWER_PROFILE); } } } diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index d95506b00677..11219eccddad 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -15,8 +15,6 @@ */ package com.android.internal.os; -import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; - import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.BatteryUsageStats; @@ -92,10 +90,12 @@ public class WifiPowerCalculator extends PowerCalculator { builder.getUidBatteryConsumerBuilders(); for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); - calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs, - BatteryStats.STATS_SINCE_CHARGED, - batteryStats.hasWifiActivityReporting(), - query.shouldForceUsePowerProfileModel()); + final long consumptionUC = + app.getBatteryStatsUid().getWifiMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), powerModel, + rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, + batteryStats.hasWifiActivityReporting(), consumptionUC); totalAppDurationMs += powerDurationAndTraffic.durationMs; totalAppPowerMah += powerDurationAndTraffic.powerMah; @@ -103,7 +103,7 @@ public class WifiPowerCalculator extends PowerCalculator { app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, powerDurationAndTraffic.durationMs); app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, - powerDurationAndTraffic.powerMah); + powerDurationAndTraffic.powerMah, powerModel); if (app.getUid() == Process.WIFI_UID) { systemBatteryConsumerBuilder.addUidBatteryConsumer(app); @@ -111,17 +111,17 @@ public class WifiPowerCalculator extends PowerCalculator { } } - calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, - BatteryStats.STATS_SINCE_CHARGED, - batteryStats.hasWifiActivityReporting(), - query.shouldForceUsePowerProfileModel(), - totalAppDurationMs, totalAppPowerMah); + final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + calculateRemaining(powerDurationAndTraffic, powerModel, batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED, batteryStats.hasWifiActivityReporting(), + totalAppDurationMs, totalAppPowerMah, consumptionUC); systemBatteryConsumerBuilder .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, powerDurationAndTraffic.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, - totalAppPowerMah + powerDurationAndTraffic.powerMah) + totalAppPowerMah + powerDurationAndTraffic.powerMah, powerModel) .setPowerConsumedByApps(totalAppPowerMah); } @@ -144,8 +144,11 @@ public class WifiPowerCalculator extends PowerCalculator { for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper app = sippers.get(i); if (app.drainType == BatterySipper.DrainType.APP) { - calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType, - batteryStats.hasWifiActivityReporting(), /* force use power model*/ false); + final long consumptionUC = + app.uidObj.getWifiMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC); + calculateApp(powerDurationAndTraffic, app.uidObj, powerModel, rawRealtimeUs, + statsType, batteryStats.hasWifiActivityReporting(), consumptionUC); totalAppDurationMs += powerDurationAndTraffic.durationMs; totalAppPowerMah += powerDurationAndTraffic.powerMah; @@ -164,9 +167,11 @@ public class WifiPowerCalculator extends PowerCalculator { } } - calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType, - batteryStats.hasWifiActivityReporting(), /* force use power model*/ false, - totalAppDurationMs, totalAppPowerMah); + final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC); + calculateRemaining(powerDurationAndTraffic, powerModel, batteryStats, rawRealtimeUs, + statsType, batteryStats.hasWifiActivityReporting(), totalAppDurationMs, + totalAppPowerMah, consumptionUC); bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs; bs.wifiPowerMah += powerDurationAndTraffic.powerMah; @@ -176,9 +181,10 @@ public class WifiPowerCalculator extends PowerCalculator { } } - private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u, - long rawRealtimeUs, int statsType, - boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel) { + private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, + BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel, + long rawRealtimeUs, int statsType, boolean hasWifiActivityReporting, + long consumptionUC) { powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets( BatteryStats.NETWORK_WIFI_RX_DATA, @@ -193,11 +199,8 @@ public class WifiPowerCalculator extends PowerCalculator { BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - final long measuredChargeUC = u.getWifiMeasuredBatteryConsumptionUC(); - final boolean isMeasuredPowerAvailable - = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE; - if (isMeasuredPowerAvailable) { - powerDurationAndTraffic.powerMah = uCtoMah(measuredChargeUC); + if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { + powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC); } if (hasWifiActivityReporting && mHasWifiPowerController) { @@ -208,7 +211,7 @@ public class WifiPowerCalculator extends PowerCalculator { final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime; - if (!isMeasuredPowerAvailable) { + if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { powerDurationAndTraffic.powerMah = calcPowerFromControllerDataMah(rxTime, txTime, idleTime); } @@ -223,7 +226,7 @@ public class WifiPowerCalculator extends PowerCalculator { final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; powerDurationAndTraffic.durationMs = wifiRunningTime; - if (!isMeasuredPowerAvailable) { + if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; long batchTimeMs = 0; for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { @@ -243,19 +246,17 @@ public class WifiPowerCalculator extends PowerCalculator { } private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic, - BatteryStats stats, long rawRealtimeUs, int statsType, - boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel, - long totalAppDurationMs, double totalAppPowerMah) { + @BatteryConsumer.PowerModel int powerModel, BatteryStats stats, long rawRealtimeUs, + int statsType, boolean hasWifiActivityReporting, long totalAppDurationMs, + double totalAppPowerMah, long consumptionUC) { long totalDurationMs; double totalPowerMah = 0; - final long measuredChargeUC = stats.getWifiMeasuredBatteryConsumptionUC(); - final boolean isMeasuredPowerAvailable - = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE; - if (isMeasuredPowerAvailable) { - totalPowerMah = uCtoMah(measuredChargeUC); + if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { + totalPowerMah = uCtoMah(consumptionUC); } + if (hasWifiActivityReporting && mHasWifiPowerController) { final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity(); @@ -266,7 +267,7 @@ public class WifiPowerCalculator extends PowerCalculator { totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs; - if (!isMeasuredPowerAvailable) { + if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { totalPowerMah = counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60); if (totalPowerMah == 0) { @@ -276,7 +277,7 @@ public class WifiPowerCalculator extends PowerCalculator { } } else { totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000; - if (!isMeasuredPowerAvailable) { + if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { totalPowerMah = calcGlobalPowerWithoutControllerDataMah(totalDurationMs); } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 5889f198a125..271c576c716e 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -41,6 +41,7 @@ import android.os.UserHandle; import android.os.ZygoteProcess; import android.os.storage.StorageManager; import android.provider.DeviceConfig; +import android.security.keystore2.AndroidKeyStoreProvider; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -222,10 +223,8 @@ public class ZygoteInit { long startTime = SystemClock.uptimeMillis(); Trace.traceBegin( Trace.TRACE_TAG_DALVIK, "Starting installation of AndroidKeyStoreProvider"); - // AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert - // preferred providers. Note this is not done via security.properties as the JCA providers - // are not on the classpath in the case of, for example, raw dalvikvm runtimes. - android.security.keystore2.AndroidKeyStoreProvider.install(); + + AndroidKeyStoreProvider.install(); Log.i(TAG, "Installed AndroidKeyStoreProvider in " + (SystemClock.uptimeMillis() - startTime) + "ms."); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index f71b31493035..4d2266b2eba5 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -547,7 +547,7 @@ class ZygoteServer { try { ZygoteConnection connection = peers.get(pollIndex); boolean multipleForksOK = !isUsapPoolEnabled() - && ZygoteHooks.indefiniteThreadSuspensionOK(); + && ZygoteHooks.isIndefiniteThreadSuspensionSafe(); final Runnable command = connection.processCommand(this, multipleForksOK); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 9a91d2028953..424632fe82eb 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -258,6 +258,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private Drawable mLastOriginalBackgroundDrawable; private Drawable mResizingBackgroundDrawable; private BackgroundBlurDrawable mBackgroundBlurDrawable; + private BackgroundBlurDrawable mLastBackgroundBlurDrawable; /** * Temporary holder for a window background when it is set before {@link #mWindow} is @@ -289,7 +290,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private int mOriginalBackgroundBlurRadius = 0; private int mBackgroundBlurRadius = 0; - private int mLastBackgroundBlurRadius = 0; private boolean mCrossWindowBlurEnabled; private final ViewTreeObserver.OnPreDrawListener mBackgroundBlurOnPreDrawListener = () -> { updateBackgroundBlurCorners(); @@ -1278,13 +1278,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } if (mBackgroundInsets.equals(mLastBackgroundInsets) - && mBackgroundBlurRadius == mLastBackgroundBlurRadius + && mBackgroundBlurDrawable == mLastBackgroundBlurDrawable && mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) { return; } Drawable destDrawable = mOriginalBackgroundDrawable; - if (mBackgroundBlurRadius > 0) { + if (mBackgroundBlurDrawable != null) { destDrawable = new LayerDrawable(new Drawable[] {mBackgroundBlurDrawable, mOriginalBackgroundDrawable}); } @@ -1309,7 +1309,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind super.setBackgroundDrawable(destDrawable); mLastBackgroundInsets = mBackgroundInsets; - mLastBackgroundBlurRadius = mBackgroundBlurRadius; + mLastBackgroundBlurDrawable = mBackgroundBlurDrawable; mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable; } @@ -1334,11 +1334,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind ? mOriginalBackgroundBlurRadius : 0; if (mBackgroundBlurDrawable == null && mBackgroundBlurRadius > 0) { mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable(); + updateBackgroundDrawable(); } if (mBackgroundBlurDrawable != null) { mBackgroundBlurDrawable.setBlurRadius(mBackgroundBlurRadius); - updateBackgroundDrawable(); } } @@ -1357,12 +1357,20 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind updateBackgroundBlurRadius(); } } else if (mCrossWindowBlurEnabledListener != null) { - mCrossWindowBlurEnabledListener = null; + updateBackgroundBlurRadius(); + removeBackgroundBlurDrawable(); + } + } + + void removeBackgroundBlurDrawable() { + if (mCrossWindowBlurEnabledListener != null) { getContext().getSystemService(WindowManager.class) .removeCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener); - getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener); - updateBackgroundBlurRadius(); + mCrossWindowBlurEnabledListener = null; } + getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener); + mBackgroundBlurDrawable = null; + updateBackgroundDrawable(); } @Override @@ -1847,6 +1855,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mFloatingToolbar = null; } + removeBackgroundBlurDrawable(); + PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false); if (st != null && st.menu != null && mFeatureId < 0) { st.menu.close(); diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 611fe29f4fcf..a6dc4e04bf36 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -75,6 +75,7 @@ import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.util.TypedValue; +import android.view.ViewRoot; import android.view.ContextThemeWrapper; import android.view.CrossWindowBlurListeners; import android.view.Gravity; @@ -3981,4 +3982,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public View getNavigationBarBackgroundView() { return mDecor != null ? mDecor.getNavigationBarBackgroundView() : null; } + + @Override + public ViewRoot getViewRoot() { + return getViewRootImplOrNull(); + } } diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index c33637353984..8d82e33dc29f 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -35,7 +35,8 @@ import com.android.internal.view.InlineSuggestionsRequestInfo; * {@hide} */ oneway interface IInputMethod { - void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps); + void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps, + int configChanges); void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo, in IInlineSuggestionsRequestCallback cb); diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java index 17ce75e63b6f..9ed5eb1d144e 100644 --- a/core/java/com/android/internal/widget/RecyclerView.java +++ b/core/java/com/android/internal/widget/RecyclerView.java @@ -2049,17 +2049,19 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro } if (!dispatchNestedPreFling(velocityX, velocityY)) { - final View firstChild = mLayout.getChildAt(0); - final View lastChild = mLayout.getChildAt(mLayout.getChildCount() - 1); boolean consumed = false; - if (velocityY < 0) { - consumed = getChildAdapterPosition(firstChild) > 0 - || firstChild.getTop() < getPaddingTop(); - } + if (mLayout.getChildCount() > 0) { + final View firstChild = mLayout.getChildAt(0); + final View lastChild = mLayout.getChildAt(mLayout.getChildCount() - 1); + if (velocityY < 0) { + consumed = getChildAdapterPosition(firstChild) > 0 + || firstChild.getTop() < getPaddingTop(); + } - if (velocityY > 0) { - consumed = getChildAdapterPosition(lastChild) < mAdapter.getItemCount() - 1 - || lastChild.getBottom() > getHeight() - getPaddingBottom(); + if (velocityY > 0) { + consumed = getChildAdapterPosition(lastChild) < mAdapter.getItemCount() - 1 + || lastChild.getBottom() > getHeight() - getPaddingBottom(); + } } dispatchNestedFling(velocityX, velocityY, consumed); |