diff options
154 files changed, 2737 insertions, 1458 deletions
diff --git a/ApiDocs.bp b/ApiDocs.bp index 029699efe3b9..ca921ff97c35 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -150,12 +150,10 @@ doc_defaults { ":current-support-api", ":current-androidx-api", ], - create_stubs: false, } doc_defaults { name: "framework-dokka-docs-default", - create_stubs: false, } droiddoc { diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp index 04b00cb837b2..14b74da0c616 100644 --- a/cmds/uiautomator/library/Android.bp +++ b/cmds/uiautomator/library/Android.bp @@ -54,7 +54,6 @@ droiddoc { ], installable: false, custom_template: "droiddoc-templates-sdk", - create_stubs: false, } java_library_static { @@ -66,6 +65,7 @@ java_library_static { "android.test.runner", "junit", ], + java_version: "1.8", } java_library_static { diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java index 71561c3c7023..39248730802f 100644 --- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java +++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java @@ -49,7 +49,7 @@ public class UiAutomationShellWrapper { } try { if (isSet) { - am.setActivityController(new DummyActivityController(), true); + am.setActivityController(new NoOpActivityController(), true); } else { am.setActivityController(null, true); } @@ -80,9 +80,9 @@ public class UiAutomationShellWrapper { } /** - * Dummy, no interference, activity controller. + * No-op, no interference, activity controller. */ - private class DummyActivityController extends IActivityController.Stub { + private class NoOpActivityController extends IActivityController.Stub { @Override public boolean activityStarting(Intent intent, String pkg) throws RemoteException { /* do nothing and let activity proceed normally */ diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java index d862e1c2babb..e6fb7aa76e58 100644 --- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java +++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java @@ -45,7 +45,7 @@ import java.util.List; public class UiAutomatorTestCase extends TestCase { private static final String DISABLE_IME = "disable_ime"; - private static final String DUMMY_IME_PACKAGE = "com.android.testing.dummyime"; + private static final String STUB_IME_PACKAGE = "com.android.testing.stubime"; private static final int NOT_A_SUBTYPE_ID = -1; private UiDevice mUiDevice; @@ -58,7 +58,7 @@ public class UiAutomatorTestCase extends TestCase { super.setUp(); mShouldDisableIme = "true".equals(mParams.getString(DISABLE_IME)); if (mShouldDisableIme) { - setDummyIme(); + setStubIme(); } } @@ -128,7 +128,7 @@ public class UiAutomatorTestCase extends TestCase { SystemClock.sleep(ms); } - private void setDummyIme() { + private void setStubIme() { Context context = ActivityThread.currentApplication(); if (context == null) { throw new RuntimeException("ActivityThread.currentApplication() is null."); @@ -138,13 +138,13 @@ public class UiAutomatorTestCase extends TestCase { List<InputMethodInfo> infos = im.getInputMethodList(); String id = null; for (InputMethodInfo info : infos) { - if (DUMMY_IME_PACKAGE.equals(info.getComponent().getPackageName())) { + if (STUB_IME_PACKAGE.equals(info.getComponent().getPackageName())) { id = info.getId(); } } if (id == null) { throw new RuntimeException(String.format( - "Required testing fixture missing: IME package (%s)", DUMMY_IME_PACKAGE)); + "Required testing fixture missing: IME package (%s)", STUB_IME_PACKAGE)); } if (context.checkSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) { diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blocked.txt index b328f2ac1955..b328f2ac1955 100644 --- a/config/hiddenapi-force-blacklist.txt +++ b/config/hiddenapi-force-blocked.txt diff --git a/config/hiddenapi-greylist-max-o.txt b/config/hiddenapi-max-target-o.txt index 023bf3876228..023bf3876228 100644 --- a/config/hiddenapi-greylist-max-o.txt +++ b/config/hiddenapi-max-target-o.txt diff --git a/config/hiddenapi-greylist-max-p.txt b/config/hiddenapi-max-target-p.txt index 351e71dd9538..351e71dd9538 100644 --- a/config/hiddenapi-greylist-max-p.txt +++ b/config/hiddenapi-max-target-p.txt diff --git a/config/hiddenapi-greylist-max-q.txt b/config/hiddenapi-max-target-q.txt index 4832dd184ec5..4832dd184ec5 100644 --- a/config/hiddenapi-greylist-max-q.txt +++ b/config/hiddenapi-max-target-q.txt diff --git a/config/hiddenapi-greylist-packages.txt b/config/hiddenapi-unsupported-packages.txt index 986d2591a007..986d2591a007 100644 --- a/config/hiddenapi-greylist-packages.txt +++ b/config/hiddenapi-unsupported-packages.txt diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-unsupported.txt index a3543dc7f4ee..a3543dc7f4ee 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-unsupported.txt diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index a9239b48bb3f..bc8db029e06d 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -183,7 +183,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim // This is to work around a bug in b/34736819. This needs to be removed once app team // fixes their side. - private AnimatorListenerAdapter mAnimationEndingListener = new AnimatorListenerAdapter() { + private AnimatorListenerAdapter mAnimationEndListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (mNodeMap.get(animation) == null) { @@ -1186,7 +1186,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } private void startAnimation() { - addAnimationEndingListener(); + addAnimationEndListener(); // Register animation callback addAnimationCallback(0); @@ -1243,15 +1243,15 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim // This is to work around the issue in b/34736819, as the old behavior in AnimatorSet had // masked a real bug in play movies. TODO: remove this and below once the root cause is fixed. - private void addAnimationEndingListener() { + private void addAnimationEndListener() { for (int i = 1; i < mNodes.size(); i++) { - mNodes.get(i).mAnimation.addListener(mAnimationEndingListener); + mNodes.get(i).mAnimation.addListener(mAnimationEndListener); } } - private void removeAnimationEndingListener() { + private void removeAnimationEndListener() { for (int i = 1; i < mNodes.size(); i++) { - mNodes.get(i).mAnimation.removeListener(mAnimationEndingListener); + mNodes.get(i).mAnimation.removeListener(mAnimationEndListener); } } @@ -1301,7 +1301,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim tmpListeners.get(i).onAnimationEnd(this, mReversing); } } - removeAnimationEndingListener(); + removeAnimationEndListener(); mSelfPulse = true; mReversing = false; } @@ -1346,7 +1346,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim anim.mNodeMap = new ArrayMap<Animator, Node>(); anim.mNodes = new ArrayList<Node>(nodeCount); anim.mEvents = new ArrayList<AnimationEvent>(); - anim.mAnimationEndingListener = new AnimatorListenerAdapter() { + anim.mAnimationEndListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (anim.mNodeMap.get(animation) == null) { @@ -1369,7 +1369,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim final Node node = mNodes.get(n); Node nodeClone = node.clone(); // Remove the old internal listener from the cloned child - nodeClone.mAnimation.removeListener(mAnimationEndingListener); + nodeClone.mAnimation.removeListener(mAnimationEndListener); clonesMap.put(node, nodeClone); anim.mNodes.add(nodeClone); anim.mNodeMap.put(nodeClone.mAnimation, nodeClone); diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 8a6cc95319fb..1345e66a355a 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -109,7 +109,7 @@ public final class UsageStatsManager { /** - * The app is whitelisted for some reason and the bucket cannot be changed. + * The app is exempted for some reason and the bucket cannot be changed. * {@hide} */ @SystemApi diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java index 2649fbee4246..cf9eeca0d739 100644 --- a/core/java/android/companion/BluetoothDeviceFilter.java +++ b/core/java/android/companion/BluetoothDeviceFilter.java @@ -142,6 +142,16 @@ public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice } @Override + public String toString() { + return "BluetoothDeviceFilter{" + + "mNamePattern=" + mNamePattern + + ", mAddress='" + mAddress + '\'' + + ", mServiceUuids=" + mServiceUuids + + ", mServiceUuidMasks=" + mServiceUuidMasks + + '}'; + } + + @Override public int describeContents() { return 0; } diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index ad9bf0745779..8d6e937488e4 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -260,6 +260,13 @@ public abstract class DisplayManagerInternal { int displayId, long maxFrames, long timestamp); /** + * Temporarily ignore proximity-sensor-based display behavior until there is a change + * to the proximity sensor state. This allows the display to turn back on even if something + * is obstructing the proximity sensor. + */ + public abstract void ignoreProximitySensorUntilChanged(); + + /** * Describes the requested power state of the display. * * This object is intended to describe the general characteristics of the diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 1539b6ed4ca7..a6b869d19867 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -570,7 +570,7 @@ public class GraphicsEnvironment { final ContentResolver contentResolver = context.getContentResolver(); final List<String> angleAllowlist = getGlobalSettingsString(contentResolver, bundle, - Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST); + Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST); if (DEBUG) Log.v(TAG, "ANGLE allowlist: " + angleAllowlist); diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 0ec4fb832801..40c291f14b67 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -16,6 +16,6 @@ per-file PowerManager.java = michaelwr@google.com, santoscordon@google.com per-file PowerManagerInternal.java = michaelwr@google.com, santoscordon@google.com # Zygote -per-file ZygoteProcess.java = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com +per-file ZygoteProcess.java = calin@google.com, chriswailes@google.com, maco@google.com, narayan@google.com, ngeoffray@google.com per-file GraphicsEnvironment.java = chrisforbes@google.com, cnorthrop@google.com, lpy@google.com, timvp@google.com, zzyiwei@google.com diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 653a5594f495..f9e146a9dbdc 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -17,6 +17,7 @@ package android.os; import android.view.Display; +import android.view.KeyEvent; import java.util.function.Consumer; @@ -319,4 +320,7 @@ public abstract class PowerManagerInternal { /** Returns information about the last wakeup event. */ public abstract PowerManager.WakeData getLastWakeup(); + + /** Allows power button to intercept a power key button press. */ + public abstract boolean interceptPowerKeyDown(KeyEvent event); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 39038f555044..ffede09f99d6 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -251,11 +251,11 @@ public class ZygoteProcess { private final Object mLock = new Object(); /** - * List of exemptions to the API blacklist. These are prefix matches on the runtime format + * List of exemptions to the API deny list. These are prefix matches on the runtime format * symbol signature. Any matching symbol is treated by the runtime as being on the light grey * list. */ - private List<String> mApiBlacklistExemptions = Collections.emptyList(); + private List<String> mApiDenylistExemptions = Collections.emptyList(); /** * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000. @@ -562,7 +562,7 @@ public class ZygoteProcess { "--preload-package", "--preload-app", "--start-child-zygote", - "--set-api-blacklist-exemptions", + "--set-api-denylist-exemptions", "--hidden-api-log-sampling-rate", "--hidden-api-statslog-sampling-rate", "--invoke-with" @@ -922,20 +922,20 @@ public class ZygoteProcess { } /** - * Push hidden API blacklisting exemptions into the zygote process(es). + * Push hidden API deny-listing exemptions into the zygote process(es). * * <p>The list of exemptions will take affect for all new processes forked from the zygote after * this call. * * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as - * whitelisted/public APIs (i.e. allowed, no logging of usage). + * allowed/public APIs (i.e. allowed, no logging of usage). */ - public boolean setApiBlacklistExemptions(List<String> exemptions) { + public boolean setApiDenylistExemptions(List<String> exemptions) { synchronized (mLock) { - mApiBlacklistExemptions = exemptions; - boolean ok = maybeSetApiBlacklistExemptions(primaryZygoteState, true); + mApiDenylistExemptions = exemptions; + boolean ok = maybeSetApiDenylistExemptions(primaryZygoteState, true); if (ok) { - ok = maybeSetApiBlacklistExemptions(secondaryZygoteState, true); + ok = maybeSetApiDenylistExemptions(secondaryZygoteState, true); } return ok; } @@ -972,32 +972,32 @@ public class ZygoteProcess { } @GuardedBy("mLock") - private boolean maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) { + private boolean maybeSetApiDenylistExemptions(ZygoteState state, boolean sendIfEmpty) { if (state == null || state.isClosed()) { - Slog.e(LOG_TAG, "Can't set API blacklist exemptions: no zygote connection"); + Slog.e(LOG_TAG, "Can't set API denylist exemptions: no zygote connection"); return false; - } else if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) { + } else if (!sendIfEmpty && mApiDenylistExemptions.isEmpty()) { return true; } try { - state.mZygoteOutputWriter.write(Integer.toString(mApiBlacklistExemptions.size() + 1)); + state.mZygoteOutputWriter.write(Integer.toString(mApiDenylistExemptions.size() + 1)); state.mZygoteOutputWriter.newLine(); - state.mZygoteOutputWriter.write("--set-api-blacklist-exemptions"); + state.mZygoteOutputWriter.write("--set-api-denylist-exemptions"); state.mZygoteOutputWriter.newLine(); - for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) { - state.mZygoteOutputWriter.write(mApiBlacklistExemptions.get(i)); + for (int i = 0; i < mApiDenylistExemptions.size(); ++i) { + state.mZygoteOutputWriter.write(mApiDenylistExemptions.get(i)); state.mZygoteOutputWriter.newLine(); } state.mZygoteOutputWriter.flush(); int status = state.mZygoteInputStream.readInt(); if (status != 0) { - Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status); + Slog.e(LOG_TAG, "Failed to set API denylist exemptions; status " + status); } return true; } catch (IOException ioe) { - Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe); - mApiBlacklistExemptions = Collections.emptyList(); + Slog.e(LOG_TAG, "Failed to set API denylist exemptions", ioe); + mApiDenylistExemptions = Collections.emptyList(); return false; } } @@ -1054,7 +1054,7 @@ public class ZygoteProcess { primaryZygoteState = ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress); - maybeSetApiBlacklistExemptions(primaryZygoteState, false); + maybeSetApiDenylistExemptions(primaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); } } @@ -1069,7 +1069,7 @@ public class ZygoteProcess { ZygoteState.connect(mZygoteSecondarySocketAddress, mUsapPoolSecondarySocketAddress); - maybeSetApiBlacklistExemptions(secondaryZygoteState, false); + maybeSetApiDenylistExemptions(secondaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4bb2c9502390..540efd30987a 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12268,8 +12268,8 @@ public final class Settings { * List of package names that should check ANGLE rules * @hide */ - public static final String GLOBAL_SETTINGS_ANGLE_WHITELIST = - "angle_whitelist"; + public static final String GLOBAL_SETTINGS_ANGLE_ALLOWLIST = + "angle_allowlist"; /** * Show the "ANGLE In Use" dialog box to the user when ANGLE is the OpenGL driver. diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java index cbe3734ce5fa..aa70d07ff787 100644 --- a/core/java/android/util/proto/ProtoInputStream.java +++ b/core/java/android/util/proto/ProtoInputStream.java @@ -96,7 +96,7 @@ public final class ProtoInputStream extends ProtoStream { private byte mState = 0; /** - * Keeps track of the currently read nested Objects, for end object sanity checking and debug + * Keeps track of the currently read nested Objects, for end object checking and debug */ private ArrayList<Long> mExpectedObjectTokenStack = null; @@ -513,7 +513,7 @@ public final class ProtoInputStream extends ProtoStream { (int) fieldId, getOffset() + messageSize)); } - // Sanity check + // Validation check if (mDepth > 0 && getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth)) > getOffsetFromToken(mExpectedObjectTokenStack.get(mDepth - 1))) { @@ -536,7 +536,7 @@ public final class ProtoInputStream extends ProtoStream { * @param token - token */ public void end(long token) { - // Sanity check to make sure user is keeping track of their embedded messages + // Make sure user is keeping track of their embedded messages if (mExpectedObjectTokenStack.get(mDepth) != token) { throw new ProtoParseException( "end token " + token + " does not match current message token " diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java index 5fcd38e2fd9c..afca4ab2c741 100644 --- a/core/java/android/util/proto/ProtoOutputStream.java +++ b/core/java/android/util/proto/ProtoOutputStream.java @@ -59,10 +59,10 @@ import java.io.UnsupportedEncodingException; * cache the size, and then write the size-prefixed buffers. * * We are trying to avoid too much generated code here, but this class still - * needs to have a somewhat sane API. We can't have the multiple passes be - * done by the calling code. In addition, we want to avoid the memory high - * water mark of duplicating all of the values into the traditional in-memory - * Message objects. We need to find another way. + * needs to have API. We can't have the multiple passes be done by the + * calling code. In addition, we want to avoid the memory high water mark + * of duplicating all of the values into the traditional in-memory Message + * objects. We need to find another way. * * So what we do here is to let the calling code write the data into a * byte[] (actually a collection of them wrapped in the EncodedBuffer class), diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6837b7adc86d..154c287049b8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -23231,7 +23231,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * displaying, else return the result of calling through to the * super class. * - * @return boolean If true than the Drawable is being displayed in the + * @return boolean If true then the Drawable is being displayed in the * view; else false and it is not allowed to animate. * * @see #unscheduleDrawable(android.graphics.drawable.Drawable) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7453f21d379b..bcf3b49c6644 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1824,19 +1824,13 @@ public final class ViewRootImpl implements ViewParent, /** * Called after window layout to update the bounds surface. If the surface insets have changed * or the surface has resized, update the bounds surface. - * - * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl. */ - private void updateBoundsLayer(boolean shouldReparent) { + private void updateBoundsLayer() { if (mBoundsLayer != null) { setBoundsLayerCrop(); - mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(), - mSurface.getNextFrameNumber()); - - if (shouldReparent) { - mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl()); - } - mTransaction.apply(); + mTransaction.deferTransactionUntil(mBoundsLayer, + getRenderSurfaceControl(), mSurface.getNextFrameNumber()) + .apply(); } } @@ -2919,16 +2913,7 @@ public final class ViewRootImpl implements ViewParent, } if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) { - // If the surface has been replaced, there's a chance the bounds layer is not parented - // to the new layer. When updating bounds layer, also reparent to the main VRI - // SurfaceControl to ensure it's correctly placed in the hierarchy. - // - // This needs to be done on the client side since WMS won't reparent the children to the - // new surface if it thinks the app is closing. WMS gets the signal that the app is - // stopping, but on the client side it doesn't get stopped since it's restarted quick - // enough. WMS doesn't want to keep around old children since they will leak when the - // client creates new children. - updateBoundsLayer(surfaceReplaced); + updateBoundsLayer(); } final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index d6fe99a1710b..8bc7f3c91d8c 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -1768,7 +1768,7 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Note:</strong> The primary usage of this API is for UI test automation * and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo} * the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} - * flag when configuring their {@link android.accessibilityservice.AccessibilityService}. + * flag when configuring the {@link android.accessibilityservice.AccessibilityService}. * </p> * <p> * <strong>Note:</strong> If this view hierarchy has a {@link SurfaceView} embedding another @@ -3206,7 +3206,7 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Note:</strong> The primary usage of this API is for UI test automation * and in order to report the source view id of an {@link AccessibilityNodeInfo} the * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS} - * flag when configuring their {@link android.accessibilityservice.AccessibilityService}. + * flag when configuring the {@link android.accessibilityservice.AccessibilityService}. * </p> * @return The id resource name. diff --git a/core/java/com/android/internal/os/ChildZygoteInit.java b/core/java/com/android/internal/os/ChildZygoteInit.java index 1f816c18f886..749ff84358d9 100644 --- a/core/java/com/android/internal/os/ChildZygoteInit.java +++ b/core/java/com/android/internal/os/ChildZygoteInit.java @@ -116,7 +116,7 @@ public class ChildZygoteInit { try { server.registerServerSocketAtAbstractName(socketName); - // Add the abstract socket to the FD whitelist so that the native zygote code + // Add the abstract socket to the FD allow list so that the native zygote code // can properly detach it after forking. Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName); diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS index 928310549e6e..afc94329dc4d 100644 --- a/core/java/com/android/internal/os/OWNERS +++ b/core/java/com/android/internal/os/OWNERS @@ -1 +1 @@ -per-file ZygoteArguments.java,ZygoteConnection.java,ZygoteInit.java,Zygote.java,ZygoteServer.java = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com +per-file ZygoteArguments.java,ZygoteConnection.java,ZygoteInit.java,Zygote.java,ZygoteServer.java = calin@google.com, chriswailes@google.com, maco@google.com, narayan@google.com, ngeoffray@google.com diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 2c653bbb0f63..069d19f6138f 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -816,9 +816,9 @@ public final class Zygote { throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-app"); } else if (args.mStartChildZygote) { throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--start-child-zygote"); - } else if (args.mApiBlacklistExemptions != null) { + } else if (args.mApiDenylistExemptions != null) { throw new IllegalArgumentException( - USAP_ERROR_PREFIX + "--set-api-blacklist-exemptions"); + USAP_ERROR_PREFIX + "--set-api-denylist-exemptions"); } else if (args.mHiddenApiAccessLogSampleRate != -1) { throw new IllegalArgumentException( USAP_ERROR_PREFIX + "--hidden-api-log-sampling-rate="); diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index 94c1f71a26db..22082d02d9f6 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -192,10 +192,10 @@ class ZygoteArguments { boolean mBootCompleted; /** - * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, or - * when they change, via --set-api-blacklist-exemptions. + * Exemptions from API deny-listing. These are sent to the pre-forked zygote at boot time, or + * when they change, via --set-api-denylist-exemptions. */ - String[] mApiBlacklistExemptions; + String[] mApiDenylistExemptions; /** * Sampling rate for logging hidden API accesses to the event log. This is sent to the @@ -416,10 +416,10 @@ class ZygoteArguments { expectRuntimeArgs = false; } else if (arg.equals("--start-child-zygote")) { mStartChildZygote = true; - } else if (arg.equals("--set-api-blacklist-exemptions")) { + } else if (arg.equals("--set-api-denylist-exemptions")) { // consume all remaining args; this is a stand-alone command, never included // with the regular fork command. - mApiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length); + mApiDenylistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length); curArg = args.length; expectRuntimeArgs = false; } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) { diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index e6a3029c5b2b..6573e4217a7f 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -185,8 +185,8 @@ class ZygoteConnection { return null; } - if (parsedArgs.mApiBlacklistExemptions != null) { - return handleApiBlacklistExemptions(zygoteServer, parsedArgs.mApiBlacklistExemptions); + if (parsedArgs.mApiDenylistExemptions != null) { + return handleApiDenylistExemptions(zygoteServer, parsedArgs.mApiDenylistExemptions); } if (parsedArgs.mHiddenApiAccessLogSampleRate != -1 @@ -367,11 +367,11 @@ class ZygoteConnection { } /** - * Makes the necessary changes to implement a new API blacklist exemption policy, and then + * Makes the necessary changes to implement a new API deny list exemption policy, and then * responds to the system server, letting it know that the task has been completed. * * This necessitates a change to the internal state of the Zygote. As such, if the USAP - * pool is enabled all existing USAPs have an incorrect API blacklist exemption list. To + * pool is enabled all existing USAPs have an incorrect API deny list exemption list. To * properly handle this request the pool must be emptied and refilled. This process can return * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked. * @@ -380,9 +380,9 @@ class ZygoteConnection { * @return A Runnable object representing a new app in any USAPs spawned from here; the * zygote process will always receive a null value from this function. */ - private Runnable handleApiBlacklistExemptions(ZygoteServer zygoteServer, String[] exemptions) { + private Runnable handleApiDenylistExemptions(ZygoteServer zygoteServer, String[] exemptions) { return stateChangeWithUsapPoolReset(zygoteServer, - () -> ZygoteInit.setApiBlacklistExemptions(exemptions)); + () -> ZygoteInit.setApiDenylistExemptions(exemptions)); } private Runnable handleUsapPoolStatusChange(ZygoteServer zygoteServer, boolean newStatus) { diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index a70955ce5d4c..f4789f5eb5b2 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -589,7 +589,10 @@ public class ZygoteInit { VMRuntime.registerAppInfo(profilePath, codePaths); } - public static void setApiBlacklistExemptions(String[] exemptions) { + /** + * Sets the list of classes/methods for the hidden API + */ + public static void setApiDenylistExemptions(String[] exemptions) { VMRuntime.getRuntime().setHiddenApiExemptions(exemptions); } diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index 8d281b7ce9a0..8c81984064d0 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -451,7 +451,7 @@ class ZygoteServer { * For reasons of correctness the USAP pool pipe and event FDs * must be processed before the session and server sockets. This * is to ensure that the USAP pool accounting information is - * accurate when handling other requests like API blacklist + * accurate when handling other requests like API deny list * exemptions. */ diff --git a/core/jni/OWNERS b/core/jni/OWNERS index d7d8621a3640..7d80993afc6e 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -17,4 +17,4 @@ per-file android_view_*MotionEvent.* = michaelwr@google.com, svv@google.com per-file android_view_PointerIcon.* = michaelwr@google.com, svv@google.com # Zygote -per-file com_android_internal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com +per-file com_android_internal_os_Zygote.*,fd_utils.* = calin@google.com, chriswailes@google.com, maco@google.com, narayan@google.com, ngeoffray@google.com diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 22bb2102a6e1..1f625443c96e 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -974,7 +974,7 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, if (jHandle == NULL) { return (jint)AUDIO_JAVA_ERROR; } - // create dummy port and port config objects with just the correct handle + // create placeholder port and port config objects with just the correct handle // and configuration data. The actual AudioPortConfig objects will be // constructed by java code with correct class type (device, mix etc...) // and reference to AudioPort instance in this client diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index e56809f66dc7..8d4c4e5311f8 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -93,13 +93,13 @@ static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) { - int dummy = 0; + int optval_ignored = 0; int fd = jniGetFDFromFileDescriptor(env, javaFd); - if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &dummy, sizeof(dummy)) != 0) { + if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) != + 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_DETACH_FILTER): %s", strerror(errno)); } - } static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId) diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index d6e8531fa6ca..ef0eeec2c7af 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -920,7 +920,7 @@ int register_android_os_Debug(JNIEnv *env) { jclass clazz = env->FindClass("android/os/Debug$MemoryInfo"); - // Sanity check the number of other statistics expected in Java matches here. + // Check the number of other statistics expected in Java matches here. jfieldID numOtherStats_field = env->GetStaticFieldID(clazz, "NUM_OTHER_STATS", "I"); jint numOtherStats = env->GetStaticIntField(clazz, numOtherStats_field); jfieldID numDvkStats_field = env->GetStaticFieldID(clazz, "NUM_DVK_STATS", "I"); diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index 6ec5bce224fc..9ae3d160be98 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -349,7 +349,7 @@ static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, j if (parcel != NULL) { int32_t len = parcel->readInt32(); - // sanity check the stored length against the true data size + // Validate the stored length against the true data size if (len >= 0 && len <= (int32_t)parcel->dataAvail()) { ret = env->NewByteArray(len); diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 4c9d7abeb5a1..9643b64c68f8 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -961,7 +961,7 @@ static jlong android_os_Binder_clearCallingIdentity() static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token) { - // XXX temporary sanity check to debug crashes. + // XXX temporary validation check to debug crashes. int uid = (int)(token>>32); if (uid > 0 && uid < 999) { // In Android currently there are no uids in this range. diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp index 24c3ff80badd..70a9be740810 100644 --- a/core/jni/android_view_InputQueue.cpp +++ b/core/jni/android_view_InputQueue.cpp @@ -176,8 +176,8 @@ void InputQueue::enqueueEvent(InputEvent* event) { Mutex::Autolock _l(mLock); mPendingEvents.push(event); if (mPendingEvents.size() == 1) { - char dummy = 0; - int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &dummy, sizeof(dummy))); + char payload = '\0'; + int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &payload, sizeof(payload))); if (res < 0 && errno != EAGAIN) { ALOGW("Failed writing to dispatch fd: %s", strerror(errno)); } diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index 21b79604192f..a80bb4013854 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -449,7 +449,7 @@ message GlobalSettingsProto { // Game Driver - List of Apps that are allowed to use Game Driver optional SettingProto game_driver_allowlist = 12; // ANGLE - List of Apps that can check ANGLE rules - optional SettingProto angle_whitelist = 13; + optional SettingProto angle_allowlist = 13; // Game Driver - List of denylists, each denylist is a denylist for // a specific Game Driver version optional SettingProto game_driver_denylists = 14; diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index fd12bb057ae0..2570ae520f4d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -976,7 +976,7 @@ with the modem or some other restricted hardware, add "/dev/bus/usb/001/" to this list. If this is empty, no parts of the host USB bus will be excluded. --> - <string-array name="config_usbHostBlacklist" translatable="false"> + <string-array name="config_usbHostDenylist" translatable="false"> </string-array> <!-- List of paths to serial ports that are available to the serial manager. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 686dd78b635c..5951ef16c1c1 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1854,7 +1854,7 @@ <java-symbol type="array" name="config_tether_upstream_types" /> <java-symbol type="array" name="config_tether_usb_regexs" /> <java-symbol type="array" name="config_tether_wifi_regexs" /> - <java-symbol type="array" name="config_usbHostBlacklist" /> + <java-symbol type="array" name="config_usbHostDenylist" /> <java-symbol type="array" name="config_serialPorts" /> <java-symbol type="array" name="radioAttributes" /> <java-symbol type="array" name="config_oemUsbModeOverride" /> diff --git a/core/tests/coretests/apks/install/res/values/strings.xml b/core/tests/coretests/apks/install/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install/res/values/strings.xml +++ b/core/tests/coretests/apks/install/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_bad_dex/res/values/strings.xml b/core/tests/coretests/apks/install_bad_dex/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_bad_dex/res/values/strings.xml +++ b/core/tests/coretests/apks/install_bad_dex/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_decl_perm/res/values/strings.xml b/core/tests/coretests/apks/install_decl_perm/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_decl_perm/res/values/strings.xml +++ b/core/tests/coretests/apks/install_decl_perm/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_loc_auto/res/values/strings.xml b/core/tests/coretests/apks/install_loc_auto/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_loc_auto/res/values/strings.xml +++ b/core/tests/coretests/apks/install_loc_auto/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_loc_internal/res/values/strings.xml b/core/tests/coretests/apks/install_loc_internal/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_loc_internal/res/values/strings.xml +++ b/core/tests/coretests/apks/install_loc_internal/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_loc_sdcard/res/values/strings.xml b/core/tests/coretests/apks/install_loc_sdcard/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_loc_sdcard/res/values/strings.xml +++ b/core/tests/coretests/apks/install_loc_sdcard/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_loc_unspecified/res/values/strings.xml b/core/tests/coretests/apks/install_loc_unspecified/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_loc_unspecified/res/values/strings.xml +++ b/core/tests/coretests/apks/install_loc_unspecified/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_use_perm_good/res/values/strings.xml b/core/tests/coretests/apks/install_use_perm_good/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_use_perm_good/res/values/strings.xml +++ b/core/tests/coretests/apks/install_use_perm_good/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_uses_feature/res/values/strings.xml b/core/tests/coretests/apks/install_uses_feature/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_uses_feature/res/values/strings.xml +++ b/core/tests/coretests/apks/install_uses_feature/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml b/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml +++ b/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml b/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml +++ b/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/keyset/res/values/strings.xml b/core/tests/coretests/apks/keyset/res/values/strings.xml index ff99ffa77b2a..d811ec29ef19 100644 --- a/core/tests/coretests/apks/keyset/res/values/strings.xml +++ b/core/tests/coretests/apks/keyset/res/values/strings.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> <string name="keyset_perm_desc">keyset_perm_description</string> <string name="keyset_perm_label">keyset_perm_label</string> </resources> diff --git a/core/tests/coretests/apks/version/res/values/strings.xml b/core/tests/coretests/apks/version/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/version/res/values/strings.xml +++ b/core/tests/coretests/apks/version/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/core/tests/coretests/apks/version_nosys/res/values/strings.xml b/core/tests/coretests/apks/version_nosys/res/values/strings.xml index 3b8b3b1af9b5..984152fb5fa7 100644 --- a/core/tests/coretests/apks/version_nosys/res/values/strings.xml +++ b/core/tests/coretests/apks/version_nosys/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Just need this dummy file to have something to build. --> +<!-- Just need this placeholder file to have something to build. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="dummy">dummy</string> + <string name="placeholder">placeholder</string> </resources> diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 4cdfbb8ce27f..bd2d4af54c83 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -345,10 +345,10 @@ key 377 TV # key 395 "KEY_LIST" # key 396 "KEY_MEMO" key 397 CALENDAR -# key 398 "KEY_RED" -# key 399 "KEY_GREEN" -# key 400 "KEY_YELLOW" -# key 401 "KEY_BLUE" +key 398 PROG_RED +key 399 PROG_GREEN +key 400 PROG_YELLOW +key 401 PROG_BLUE key 402 CHANNEL_UP key 403 CHANNEL_DOWN # key 404 "KEY_FIRST" @@ -415,6 +415,7 @@ key 583 ASSIST key usage 0x0c0067 WINDOW key usage 0x0c006F BRIGHTNESS_UP key usage 0x0c0070 BRIGHTNESS_DOWN +key usage 0x0c0173 MEDIA_AUDIO_TRACK # Joystick and game controller axes. # Axes that are not mapped will be assigned generic axis numbers by the input subsystem. diff --git a/data/keyboards/OWNERS b/data/keyboards/OWNERS index 031a6c1c7a89..c4f6df824a39 100644 --- a/data/keyboards/OWNERS +++ b/data/keyboards/OWNERS @@ -2,3 +2,4 @@ set noparent michaelwr@google.com svv@google.com +lzye@google.com diff --git a/data/keyboards/Vendor_27f8_Product_0bbf.kl b/data/keyboards/Vendor_27f8_Product_0bbf.kl new file mode 100644 index 000000000000..a59f5663842c --- /dev/null +++ b/data/keyboards/Vendor_27f8_Product_0bbf.kl @@ -0,0 +1,54 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Kishi Mobile Controller +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 0x133 BUTTON_X +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x134 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +axis 0x00 X +axis 0x01 Y + +axis 0x02 Z +axis 0x05 RZ + +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER + +key 0x13d BUTTON_THUMBL +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Left Triangle Button +key 0x13a BUTTON_SELECT +# Right Triangle Button +key 0x13b BUTTON_START +# Home Button +key 0x13c BUTTON_MODE diff --git a/data/keyboards/Vendor_2e95_Product_7725.kl b/data/keyboards/Vendor_2e95_Product_7725.kl new file mode 100644 index 000000000000..7672e22f8adc --- /dev/null +++ b/data/keyboards/Vendor_2e95_Product_7725.kl @@ -0,0 +1,64 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Scuf Vantage Controller +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# L2 Trigger axis +axis 0x03 LTRIGGER +# R2 Trigger axis +axis 0x04 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE +# Touchpad press +key 0x13d BUTTON_1 diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt index d8af726ffa72..70dedb8179b0 100644 --- a/framework-jarjar-rules.txt +++ b/framework-jarjar-rules.txt @@ -1,2 +1,6 @@ rule android.hidl.** android.internal.hidl.@1 rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1 + +# Hide media mainline module implementation classes to avoid collisions with +# app-bundled ExoPlayer classes. +rule com.google.android.exoplayer2.** android.media.internal.exo.@1 diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 5252cd041199..dca35012cbdd 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -16,6 +16,9 @@ cc_library_shared { name: "libinputservice", srcs: [ "PointerController.cpp", + "PointerControllerContext.cpp", + "MouseCursorController.cpp", + "TouchSpotController.cpp", "SpriteController.cpp", "SpriteIcon.cpp", ], diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp new file mode 100644 index 000000000000..80b555be97dd --- /dev/null +++ b/libs/input/MouseCursorController.cpp @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MouseCursorController" +//#define LOG_NDEBUG 0 + +// Log debug messages about pointer updates +#define DEBUG_MOUSE_CURSOR_UPDATES 0 + +#include "MouseCursorController.h" + +#include <log/log.h> + +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> + +namespace { +// Time to spend fading out the pointer completely. +const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms +} // namespace + +namespace android { + +// --- MouseCursorController --- + +MouseCursorController::MouseCursorController(PointerControllerContext& context) + : mContext(context) { + std::scoped_lock lock(mLock); + + mLocked.animationFrameIndex = 0; + mLocked.lastFrameUpdatedTime = 0; + + mLocked.pointerFadeDirection = 0; + mLocked.pointerX = 0; + mLocked.pointerY = 0; + mLocked.pointerAlpha = 0.0f; // pointer is initially faded + mLocked.pointerSprite = mContext.getSpriteController()->createSprite(); + mLocked.updatePointerIcon = false; + mLocked.requestedPointerType = mContext.getPolicy()->getDefaultPointerIconId(); + + mLocked.resourcesLoaded = false; + + mLocked.buttonState = 0; +} + +MouseCursorController::~MouseCursorController() { + std::scoped_lock lock(mLock); + + mLocked.pointerSprite.clear(); +} + +bool MouseCursorController::getBounds(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const { + std::scoped_lock lock(mLock); + + return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY); +} + +bool MouseCursorController::getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return false; + } + + *outMinX = mLocked.viewport.logicalLeft; + *outMinY = mLocked.viewport.logicalTop; + *outMaxX = mLocked.viewport.logicalRight - 1; + *outMaxY = mLocked.viewport.logicalBottom - 1; + return true; +} + +void MouseCursorController::move(float deltaX, float deltaY) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); +#endif + if (deltaX == 0.0f && deltaY == 0.0f) { + return; + } + + std::scoped_lock lock(mLock); + + setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); +} + +void MouseCursorController::setButtonState(int32_t buttonState) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Set button state 0x%08x", buttonState); +#endif + std::scoped_lock lock(mLock); + + if (mLocked.buttonState != buttonState) { + mLocked.buttonState = buttonState; + } +} + +int32_t MouseCursorController::getButtonState() const { + std::scoped_lock lock(mLock); + return mLocked.buttonState; +} + +void MouseCursorController::setPosition(float x, float y) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); +#endif + std::scoped_lock lock(mLock); + setPositionLocked(x, y); +} + +void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + if (x <= minX) { + mLocked.pointerX = minX; + } else if (x >= maxX) { + mLocked.pointerX = maxX; + } else { + mLocked.pointerX = x; + } + if (y <= minY) { + mLocked.pointerY = minY; + } else if (y >= maxY) { + mLocked.pointerY = maxY; + } else { + mLocked.pointerY = y; + } + updatePointerLocked(); + } +} + +void MouseCursorController::getPosition(float* outX, float* outY) const { + std::scoped_lock lock(mLock); + + *outX = mLocked.pointerX; + *outY = mLocked.pointerY; +} + +int32_t MouseCursorController::getDisplayId() const { + std::scoped_lock lock(mLock); + return mLocked.viewport.displayId; +} + +void MouseCursorController::fade(PointerControllerInterface::Transition transition) { + std::scoped_lock lock(mLock); + + // Remove the inactivity timeout, since we are fading now. + mContext.removeInactivityTimeout(); + + // Start fading. + if (transition == PointerControllerInterface::Transition::IMMEDIATE) { + mLocked.pointerFadeDirection = 0; + mLocked.pointerAlpha = 0.0f; + updatePointerLocked(); + } else { + mLocked.pointerFadeDirection = -1; + mContext.startAnimation(); + } +} + +void MouseCursorController::unfade(PointerControllerInterface::Transition transition) { + std::scoped_lock lock(mLock); + + // Always reset the inactivity timer. + mContext.resetInactivityTimeout(); + + // Start unfading. + if (transition == PointerControllerInterface::Transition::IMMEDIATE) { + mLocked.pointerFadeDirection = 0; + mLocked.pointerAlpha = 1.0f; + updatePointerLocked(); + } else { + mLocked.pointerFadeDirection = 1; + mContext.startAnimation(); + } +} + +void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) { + std::scoped_lock lock(mLock); + + loadResourcesLocked(getAdditionalMouseResources); + updatePointerLocked(); +} + +/** + * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation, + * so here we are getting the dimensions in the original, unrotated orientation (orientation 0). + */ +static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) { + width = viewport.deviceWidth; + height = viewport.deviceHeight; + + if (viewport.orientation == DISPLAY_ORIENTATION_90 || + viewport.orientation == DISPLAY_ORIENTATION_270) { + std::swap(width, height); + } +} + +void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport, + bool getAdditionalMouseResources) { + std::scoped_lock lock(mLock); + + if (viewport == mLocked.viewport) { + return; + } + + const DisplayViewport oldViewport = mLocked.viewport; + mLocked.viewport = viewport; + + int32_t oldDisplayWidth, oldDisplayHeight; + getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight); + int32_t newDisplayWidth, newDisplayHeight; + getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight); + + // Reset cursor position to center if size or display changed. + if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth || + oldDisplayHeight != newDisplayHeight) { + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + mLocked.pointerX = (minX + maxX) * 0.5f; + mLocked.pointerY = (minY + maxY) * 0.5f; + // Reload icon resources for density may be changed. + loadResourcesLocked(getAdditionalMouseResources); + } else { + mLocked.pointerX = 0; + mLocked.pointerY = 0; + } + } else if (oldViewport.orientation != viewport.orientation) { + // Apply offsets to convert from the pixel top-left corner position to the pixel center. + // This creates an invariant frame of reference that we can easily rotate when + // taking into account that the pointer may be located at fractional pixel offsets. + float x = mLocked.pointerX + 0.5f; + float y = mLocked.pointerY + 0.5f; + float temp; + + // Undo the previous rotation. + switch (oldViewport.orientation) { + case DISPLAY_ORIENTATION_90: + temp = x; + x = oldViewport.deviceHeight - y; + y = temp; + break; + case DISPLAY_ORIENTATION_180: + x = oldViewport.deviceWidth - x; + y = oldViewport.deviceHeight - y; + break; + case DISPLAY_ORIENTATION_270: + temp = x; + x = y; + y = oldViewport.deviceWidth - temp; + break; + } + + // Perform the new rotation. + switch (viewport.orientation) { + case DISPLAY_ORIENTATION_90: + temp = x; + x = y; + y = viewport.deviceHeight - temp; + break; + case DISPLAY_ORIENTATION_180: + x = viewport.deviceWidth - x; + y = viewport.deviceHeight - y; + break; + case DISPLAY_ORIENTATION_270: + temp = x; + x = viewport.deviceWidth - y; + y = temp; + break; + } + + // Apply offsets to convert from the pixel center to the pixel top-left corner position + // and save the results. + mLocked.pointerX = x - 0.5f; + mLocked.pointerY = y - 0.5f; + } + + updatePointerLocked(); +} + +void MouseCursorController::updatePointerIcon(int32_t iconId) { + std::scoped_lock lock(mLock); + + if (mLocked.requestedPointerType != iconId) { + mLocked.requestedPointerType = iconId; + mLocked.updatePointerIcon = true; + updatePointerLocked(); + } +} + +void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) { + std::scoped_lock lock(mLock); + + const int32_t iconId = mContext.getPolicy()->getCustomPointerIconId(); + mLocked.additionalMouseResources[iconId] = icon; + mLocked.requestedPointerType = iconId; + mLocked.updatePointerIcon = true; + updatePointerLocked(); +} + +bool MouseCursorController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) { + nsecs_t frameDelay = timestamp - mContext.getAnimationTime(); + + std::scoped_lock lock(mLock); + + // Animate pointer fade. + if (mLocked.pointerFadeDirection < 0) { + mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; + if (mLocked.pointerAlpha <= 0.0f) { + mLocked.pointerAlpha = 0.0f; + mLocked.pointerFadeDirection = 0; + } else { + keepAnimating = true; + } + updatePointerLocked(); + } else if (mLocked.pointerFadeDirection > 0) { + mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION; + if (mLocked.pointerAlpha >= 1.0f) { + mLocked.pointerAlpha = 1.0f; + mLocked.pointerFadeDirection = 0; + } else { + keepAnimating = true; + } + updatePointerLocked(); + } + + return keepAnimating; +} + +bool MouseCursorController::doBitmapAnimation(nsecs_t timestamp) { + std::scoped_lock lock(mLock); + + std::map<int32_t, PointerAnimation>::const_iterator iter = + mLocked.animationResources.find(mLocked.requestedPointerType); + if (iter == mLocked.animationResources.end()) { + return false; + } + + if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) { + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame; + mLocked.animationFrameIndex += incr; + mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr; + while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) { + mLocked.animationFrameIndex -= iter->second.animationFrames.size(); + } + mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]); + + spriteController->closeTransaction(); + } + + // Keep animating. + return true; +} + +void MouseCursorController::updatePointerLocked() REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); + mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); + mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId); + + if (mLocked.pointerAlpha > 0) { + mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); + mLocked.pointerSprite->setVisible(true); + } else { + mLocked.pointerSprite->setVisible(false); + } + + if (mLocked.updatePointerIcon) { + if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) { + mLocked.pointerSprite->setIcon(mLocked.pointerIcon); + } else { + std::map<int32_t, SpriteIcon>::const_iterator iter = + mLocked.additionalMouseResources.find(mLocked.requestedPointerType); + if (iter != mLocked.additionalMouseResources.end()) { + std::map<int32_t, PointerAnimation>::const_iterator anim_iter = + mLocked.animationResources.find(mLocked.requestedPointerType); + if (anim_iter != mLocked.animationResources.end()) { + mLocked.animationFrameIndex = 0; + mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC); + mContext.startAnimation(); + } + mLocked.pointerSprite->setIcon(iter->second); + } else { + ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType); + mLocked.pointerSprite->setIcon(mLocked.pointerIcon); + } + } + mLocked.updatePointerIcon = false; + } + + spriteController->closeTransaction(); +} + +void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + + if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true; + + sp<PointerControllerPolicyInterface> policy = mContext.getPolicy(); + policy->loadPointerResources(&mResources, mLocked.viewport.displayId); + policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); + + mLocked.additionalMouseResources.clear(); + mLocked.animationResources.clear(); + if (getAdditionalMouseResources) { + policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources, + mLocked.viewport.displayId); + } + + mLocked.updatePointerIcon = true; +} + +bool MouseCursorController::isViewportValid() { + std::scoped_lock lock(mLock); + return mLocked.viewport.isValid(); +} + +void MouseCursorController::getAdditionalMouseResources() { + std::scoped_lock lock(mLock); + + if (mLocked.additionalMouseResources.empty()) { + mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources, + mLocked.viewport.displayId); + } + mLocked.updatePointerIcon = true; + updatePointerLocked(); +} + +bool MouseCursorController::resourcesLoaded() { + std::scoped_lock lock(mLock); + return mLocked.resourcesLoaded; +} + +} // namespace android diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h new file mode 100644 index 000000000000..448165b5ac46 --- /dev/null +++ b/libs/input/MouseCursorController.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_MOUSE_CURSOR_CONTROLLER_H +#define _UI_MOUSE_CURSOR_CONTROLLER_H + +#include <gui/DisplayEventReceiver.h> +#include <input/DisplayViewport.h> +#include <input/Input.h> +#include <ui/DisplayInfo.h> +#include <utils/BitSet.h> +#include <utils/Looper.h> +#include <utils/RefBase.h> + +#include <map> +#include <memory> +#include <vector> + +#include "PointerControllerContext.h" +#include "SpriteController.h" + +namespace android { + +/* + * Helper class for PointerController that specifically handles + * mouse cursor resources and actions. + */ +class MouseCursorController { +public: + MouseCursorController(PointerControllerContext& context); + ~MouseCursorController(); + + bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; + void move(float deltaX, float deltaY); + void setButtonState(int32_t buttonState); + int32_t getButtonState() const; + void setPosition(float x, float y); + void getPosition(float* outX, float* outY) const; + int32_t getDisplayId() const; + void fade(PointerControllerInterface::Transition transition); + void unfade(PointerControllerInterface::Transition transition); + void setDisplayViewport(const DisplayViewport& viewport, bool getAdditionalMouseResources); + + void updatePointerIcon(int32_t iconId); + void setCustomPointerIcon(const SpriteIcon& icon); + void reloadPointerResources(bool getAdditionalMouseResources); + + void getAdditionalMouseResources(); + bool isViewportValid(); + + bool doBitmapAnimation(nsecs_t timestamp); + bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating); + + bool resourcesLoaded(); + +private: + mutable std::mutex mLock; + + PointerResources mResources; + + PointerControllerContext& mContext; + + struct Locked { + DisplayViewport viewport; + + size_t animationFrameIndex; + nsecs_t lastFrameUpdatedTime; + + int32_t pointerFadeDirection; + float pointerX; + float pointerY; + float pointerAlpha; + sp<Sprite> pointerSprite; + SpriteIcon pointerIcon; + bool updatePointerIcon; + + bool resourcesLoaded; + + std::map<int32_t, SpriteIcon> additionalMouseResources; + std::map<int32_t, PointerAnimation> animationResources; + + int32_t requestedPointerType; + + int32_t buttonState; + + } mLocked GUARDED_BY(mLock); + + bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; + void setPositionLocked(float x, float y); + + void updatePointerLocked(); + + void loadResourcesLocked(bool getAdditionalMouseResources); +}; + +} // namespace android + +#endif // _UI_MOUSE_CURSOR_CONTROLLER_H diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 5e480a66c355..14c96cefd462 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -21,31 +21,26 @@ #define DEBUG_POINTER_UPDATES 0 #include "PointerController.h" +#include "MouseCursorController.h" +#include "PointerControllerContext.h" +#include "TouchSpotController.h" #include <log/log.h> -#include <memory> +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> namespace android { // --- PointerController --- -// Time to wait before starting the fade when the pointer is inactive. -static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds -static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds - -// Time to spend fading out the spot completely. -static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms - -// Time to spend fading out the pointer completely. -static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms - -// The number of events to be read at once for DisplayEventReceiver. -static const int EVENT_BUFFER_SIZE = 100; - std::shared_ptr<PointerController> PointerController::create( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, const sp<SpriteController>& spriteController) { + // using 'new' to access non-public constructor std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>( new PointerController(policy, looper, spriteController)); @@ -60,758 +55,175 @@ std::shared_ptr<PointerController> PointerController::create( * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr. */ - controller->mHandler->pointerController = controller; - controller->mCallback->pointerController = controller; - if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) { - controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, - Looper::EVENT_INPUT, controller->mCallback, nullptr); - } else { - ALOGE("Failed to initialize DisplayEventReceiver."); - } + controller->mContext.setHandlerController(controller); + controller->mContext.setCallbackController(controller); + controller->mContext.initializeDisplayEventReceiver(); return controller; } PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, const sp<SpriteController>& spriteController) - : mPolicy(policy), - mLooper(looper), - mSpriteController(spriteController), - mHandler(new MessageHandler()), - mCallback(new LooperCallback()) { - AutoMutex _l(mLock); - - mLocked.animationPending = false; - - mLocked.presentation = Presentation::POINTER; - mLocked.presentationChanged = false; - - mLocked.inactivityTimeout = InactivityTimeout::NORMAL; - - mLocked.pointerFadeDirection = 0; - mLocked.pointerX = 0; - mLocked.pointerY = 0; - mLocked.pointerAlpha = 0.0f; // pointer is initially faded - mLocked.pointerSprite = mSpriteController->createSprite(); - mLocked.pointerIconChanged = false; - mLocked.requestedPointerType = mPolicy->getDefaultPointerIconId(); - - mLocked.animationFrameIndex = 0; - mLocked.lastFrameUpdatedTime = 0; - - mLocked.buttonState = 0; + : mContext(policy, looper, spriteController, *this), mCursorController(mContext) { + std::scoped_lock lock(mLock); + mLocked.presentation = Presentation::SPOT; } -PointerController::~PointerController() { - mLooper->removeMessages(mHandler); - - AutoMutex _l(mLock); - - mLocked.pointerSprite.clear(); - - for (auto& it : mLocked.spotsByDisplay) { - const std::vector<Spot*>& spots = it.second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots; i++) { - delete spots[i]; - } - } - mLocked.spotsByDisplay.clear(); - mLocked.recycledSprites.clear(); -} - -bool PointerController::getBounds(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const { - AutoMutex _l(mLock); - - return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY); -} - -bool PointerController::getBoundsLocked(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const { - - if (!mLocked.viewport.isValid()) { - return false; - } - - *outMinX = mLocked.viewport.logicalLeft; - *outMinY = mLocked.viewport.logicalTop; - *outMaxX = mLocked.viewport.logicalRight - 1; - *outMaxY = mLocked.viewport.logicalBottom - 1; - return true; +bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const { + return mCursorController.getBounds(outMinX, outMinY, outMaxX, outMaxY); } void PointerController::move(float deltaX, float deltaY) { -#if DEBUG_POINTER_UPDATES - ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); -#endif - if (deltaX == 0.0f && deltaY == 0.0f) { - return; - } - - AutoMutex _l(mLock); - - setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); + mCursorController.move(deltaX, deltaY); } void PointerController::setButtonState(int32_t buttonState) { -#if DEBUG_POINTER_UPDATES - ALOGD("Set button state 0x%08x", buttonState); -#endif - AutoMutex _l(mLock); - - if (mLocked.buttonState != buttonState) { - mLocked.buttonState = buttonState; - } + mCursorController.setButtonState(buttonState); } int32_t PointerController::getButtonState() const { - AutoMutex _l(mLock); - - return mLocked.buttonState; + return mCursorController.getButtonState(); } void PointerController::setPosition(float x, float y) { -#if DEBUG_POINTER_UPDATES - ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); -#endif - AutoMutex _l(mLock); - - setPositionLocked(x, y); -} - -void PointerController::setPositionLocked(float x, float y) { - float minX, minY, maxX, maxY; - if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { - if (x <= minX) { - mLocked.pointerX = minX; - } else if (x >= maxX) { - mLocked.pointerX = maxX; - } else { - mLocked.pointerX = x; - } - if (y <= minY) { - mLocked.pointerY = minY; - } else if (y >= maxY) { - mLocked.pointerY = maxY; - } else { - mLocked.pointerY = y; - } - updatePointerLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.setPosition(x, y); } void PointerController::getPosition(float* outX, float* outY) const { - AutoMutex _l(mLock); - - *outX = mLocked.pointerX; - *outY = mLocked.pointerY; + mCursorController.getPosition(outX, outY); } int32_t PointerController::getDisplayId() const { - AutoMutex _l(mLock); - - return mLocked.viewport.displayId; + return mCursorController.getDisplayId(); } void PointerController::fade(Transition transition) { - AutoMutex _l(mLock); - - // Remove the inactivity timeout, since we are fading now. - removeInactivityTimeoutLocked(); - - // Start fading. - if (transition == Transition::IMMEDIATE) { - mLocked.pointerFadeDirection = 0; - mLocked.pointerAlpha = 0.0f; - updatePointerLocked(); - } else { - mLocked.pointerFadeDirection = -1; - startAnimationLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.fade(transition); } void PointerController::unfade(Transition transition) { - AutoMutex _l(mLock); - - // Always reset the inactivity timer. - resetInactivityTimeoutLocked(); - - // Start unfading. - if (transition == Transition::IMMEDIATE) { - mLocked.pointerFadeDirection = 0; - mLocked.pointerAlpha = 1.0f; - updatePointerLocked(); - } else { - mLocked.pointerFadeDirection = 1; - startAnimationLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.unfade(transition); } void PointerController::setPresentation(Presentation presentation) { - AutoMutex _l(mLock); + std::scoped_lock lock(mLock); if (mLocked.presentation == presentation) { return; } mLocked.presentation = presentation; - mLocked.presentationChanged = true; - if (!mLocked.viewport.isValid()) { + if (!mCursorController.isViewportValid()) { return; } if (presentation == Presentation::POINTER) { - if (mLocked.additionalMouseResources.empty()) { - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources, - mLocked.viewport.displayId); - } - fadeOutAndReleaseAllSpotsLocked(); - updatePointerLocked(); + mCursorController.getAdditionalMouseResources(); + clearSpotsLocked(); } } -void PointerController::setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) { -#if DEBUG_POINTER_UPDATES - ALOGD("setSpots: idBits=%08x", spotIdBits.value); - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, - c.getAxisValue(AMOTION_EVENT_AXIS_X), - c.getAxisValue(AMOTION_EVENT_AXIS_Y), - c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - displayId); +void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId) { + std::scoped_lock lock(mLock); + auto it = mLocked.spotControllers.find(displayId); + if (it == mLocked.spotControllers.end()) { + mLocked.spotControllers.try_emplace(displayId, displayId, mContext); } -#endif - - AutoMutex _l(mLock); - if (!mLocked.viewport.isValid()) { - return; - } - - std::vector<Spot*> newSpots; - std::map<int32_t, std::vector<Spot*>>::const_iterator iter = - mLocked.spotsByDisplay.find(displayId); - if (iter != mLocked.spotsByDisplay.end()) { - newSpots = iter->second; - } - - mSpriteController->openTransaction(); - - // Add or move spots for fingers that are down. - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 - ? mResources.spotTouch : mResources.spotHover; - float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); - float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); - - Spot* spot = getSpot(id, newSpots); - if (!spot) { - spot = createAndAddSpotLocked(id, newSpots); - } - - spot->updateSprite(&icon, x, y, displayId); - } - - // Remove spots for fingers that went up. - for (size_t i = 0; i < newSpots.size(); i++) { - Spot* spot = newSpots[i]; - if (spot->id != Spot::INVALID_ID - && !spotIdBits.hasBit(spot->id)) { - fadeOutAndReleaseSpotLocked(spot); - } - } - - mSpriteController->closeTransaction(); - mLocked.spotsByDisplay[displayId] = newSpots; + mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits); } void PointerController::clearSpots() { -#if DEBUG_POINTER_UPDATES - ALOGD("clearSpots"); -#endif + std::scoped_lock lock(mLock); + clearSpotsLocked(); +} - AutoMutex _l(mLock); - if (!mLocked.viewport.isValid()) { - return; +void PointerController::clearSpotsLocked() REQUIRES(mLock) { + for (auto& [displayID, spotController] : mLocked.spotControllers) { + spotController.clearSpots(); } - - fadeOutAndReleaseAllSpotsLocked(); } void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) { - AutoMutex _l(mLock); - - if (mLocked.inactivityTimeout != inactivityTimeout) { - mLocked.inactivityTimeout = inactivityTimeout; - resetInactivityTimeoutLocked(); - } + mContext.setInactivityTimeout(inactivityTimeout); } void PointerController::reloadPointerResources() { - AutoMutex _l(mLock); + std::scoped_lock lock(mLock); - loadResourcesLocked(); - updatePointerLocked(); -} - -/** - * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation, - * so here we are getting the dimensions in the original, unrotated orientation (orientation 0). - */ -static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) { - width = viewport.deviceWidth; - height = viewport.deviceHeight; + for (auto& [displayID, spotController] : mLocked.spotControllers) { + spotController.reloadSpotResources(); + } - if (viewport.orientation == DISPLAY_ORIENTATION_90 - || viewport.orientation == DISPLAY_ORIENTATION_270) { - std::swap(width, height); + if (mCursorController.resourcesLoaded()) { + bool getAdditionalMouseResources = false; + if (mLocked.presentation == PointerController::Presentation::POINTER) { + getAdditionalMouseResources = true; + } + mCursorController.reloadPointerResources(getAdditionalMouseResources); } } void PointerController::setDisplayViewport(const DisplayViewport& viewport) { - AutoMutex _l(mLock); - if (viewport == mLocked.viewport) { - return; - } - - const DisplayViewport oldViewport = mLocked.viewport; - mLocked.viewport = viewport; - - int32_t oldDisplayWidth, oldDisplayHeight; - getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight); - int32_t newDisplayWidth, newDisplayHeight; - getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight); - - // Reset cursor position to center if size or display changed. - if (oldViewport.displayId != viewport.displayId - || oldDisplayWidth != newDisplayWidth - || oldDisplayHeight != newDisplayHeight) { - - float minX, minY, maxX, maxY; - if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { - mLocked.pointerX = (minX + maxX) * 0.5f; - mLocked.pointerY = (minY + maxY) * 0.5f; - // Reload icon resources for density may be changed. - loadResourcesLocked(); - } else { - mLocked.pointerX = 0; - mLocked.pointerY = 0; - } + std::scoped_lock lock(mLock); - fadeOutAndReleaseAllSpotsLocked(); - } else if (oldViewport.orientation != viewport.orientation) { - // Apply offsets to convert from the pixel top-left corner position to the pixel center. - // This creates an invariant frame of reference that we can easily rotate when - // taking into account that the pointer may be located at fractional pixel offsets. - float x = mLocked.pointerX + 0.5f; - float y = mLocked.pointerY + 0.5f; - float temp; - - // Undo the previous rotation. - switch (oldViewport.orientation) { - case DISPLAY_ORIENTATION_90: - temp = x; - x = oldViewport.deviceHeight - y; - y = temp; - break; - case DISPLAY_ORIENTATION_180: - x = oldViewport.deviceWidth - x; - y = oldViewport.deviceHeight - y; - break; - case DISPLAY_ORIENTATION_270: - temp = x; - x = y; - y = oldViewport.deviceWidth - temp; - break; - } - - // Perform the new rotation. - switch (viewport.orientation) { - case DISPLAY_ORIENTATION_90: - temp = x; - x = y; - y = viewport.deviceHeight - temp; - break; - case DISPLAY_ORIENTATION_180: - x = viewport.deviceWidth - x; - y = viewport.deviceHeight - y; - break; - case DISPLAY_ORIENTATION_270: - temp = x; - x = viewport.deviceWidth - y; - y = temp; - break; - } - - // Apply offsets to convert from the pixel center to the pixel top-left corner position - // and save the results. - mLocked.pointerX = x - 0.5f; - mLocked.pointerY = y - 0.5f; + bool getAdditionalMouseResources = false; + if (mLocked.presentation == PointerController::Presentation::POINTER) { + getAdditionalMouseResources = true; } - - updatePointerLocked(); + mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources); } void PointerController::updatePointerIcon(int32_t iconId) { - AutoMutex _l(mLock); - if (mLocked.requestedPointerType != iconId) { - mLocked.requestedPointerType = iconId; - mLocked.presentationChanged = true; - updatePointerLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.updatePointerIcon(iconId); } void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { - AutoMutex _l(mLock); - - const int32_t iconId = mPolicy->getCustomPointerIconId(); - mLocked.additionalMouseResources[iconId] = icon; - mLocked.requestedPointerType = iconId; - mLocked.presentationChanged = true; - - updatePointerLocked(); -} - -void PointerController::MessageHandler::handleMessage(const Message& message) { - std::shared_ptr<PointerController> controller = pointerController.lock(); - - if (controller == nullptr) { - ALOGE("PointerController instance was released before processing message: what=%d", - message.what); - return; - } - switch (message.what) { - case MSG_INACTIVITY_TIMEOUT: - controller->doInactivityTimeout(); - break; - } -} - -int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) { - std::shared_ptr<PointerController> controller = pointerController.lock(); - if (controller == nullptr) { - ALOGW("PointerController instance was released with pending callbacks. events=0x%x", - events); - return 0; // Remove the callback, the PointerController is gone anyways - } - if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events); - return 0; // remove the callback - } - - if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); - return 1; // keep the callback - } - - bool gotVsync = false; - ssize_t n; - nsecs_t timestamp; - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - for (size_t i = 0; i < static_cast<size_t>(n); ++i) { - if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - timestamp = buf[i].header.timestamp; - gotVsync = true; - } - } - } - if (gotVsync) { - controller->doAnimate(timestamp); - } - return 1; // keep the callback + std::scoped_lock lock(mLock); + mCursorController.setCustomPointerIcon(icon); } void PointerController::doAnimate(nsecs_t timestamp) { - AutoMutex _l(mLock); - - mLocked.animationPending = false; + std::scoped_lock lock(mLock); - bool keepFading = doFadingAnimationLocked(timestamp); - bool keepBitmapFlipping = doBitmapAnimationLocked(timestamp); - if (keepFading || keepBitmapFlipping) { - startAnimationLocked(); - } -} + mContext.setAnimationPending(false); -bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) { - bool keepAnimating = false; - nsecs_t frameDelay = timestamp - mLocked.animationTime; + bool keepFading = false; + keepFading = mCursorController.doFadingAnimation(timestamp, keepFading); - // Animate pointer fade. - if (mLocked.pointerFadeDirection < 0) { - mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; - if (mLocked.pointerAlpha <= 0.0f) { - mLocked.pointerAlpha = 0.0f; - mLocked.pointerFadeDirection = 0; - } else { - keepAnimating = true; - } - updatePointerLocked(); - } else if (mLocked.pointerFadeDirection > 0) { - mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION; - if (mLocked.pointerAlpha >= 1.0f) { - mLocked.pointerAlpha = 1.0f; - mLocked.pointerFadeDirection = 0; - } else { - keepAnimating = true; - } - updatePointerLocked(); + for (auto& [displayID, spotController] : mLocked.spotControllers) { + keepFading = spotController.doFadingAnimation(timestamp, keepFading); } - // Animate spots that are fading out and being removed. - for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) { - std::vector<Spot*>& spots = it->second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots;) { - Spot* spot = spots[i]; - if (spot->id == Spot::INVALID_ID) { - spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; - if (spot->alpha <= 0) { - spots.erase(spots.begin() + i); - releaseSpotLocked(spot); - numSpots--; - continue; - } else { - spot->sprite->setAlpha(spot->alpha); - keepAnimating = true; - } - } - ++i; - } - - if (spots.size() == 0) { - it = mLocked.spotsByDisplay.erase(it); - } else { - ++it; - } - } - - return keepAnimating; -} - -bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) { - std::map<int32_t, PointerAnimation>::const_iterator iter = mLocked.animationResources.find( - mLocked.requestedPointerType); - if (iter == mLocked.animationResources.end()) { - return false; - } - - if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) { - mSpriteController->openTransaction(); - - int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame; - mLocked.animationFrameIndex += incr; - mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr; - while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) { - mLocked.animationFrameIndex -= iter->second.animationFrames.size(); - } - mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]); - - mSpriteController->closeTransaction(); + bool keepBitmapFlipping = mCursorController.doBitmapAnimation(timestamp); + if (keepFading || keepBitmapFlipping) { + mContext.startAnimation(); } - - // Keep animating. - return true; } void PointerController::doInactivityTimeout() { fade(Transition::GRADUAL); } -void PointerController::startAnimationLocked() { - if (!mLocked.animationPending) { - mLocked.animationPending = true; - mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); - mDisplayEventReceiver.requestNextVsync(); - } -} - -void PointerController::resetInactivityTimeoutLocked() { - mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); - - nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT - ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT - : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; - mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); -} - -void PointerController::removeInactivityTimeoutLocked() { - mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); -} - -void PointerController::updatePointerLocked() REQUIRES(mLock) { - if (!mLocked.viewport.isValid()) { - return; - } - - mSpriteController->openTransaction(); - - mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); - mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); - mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId); - - if (mLocked.pointerAlpha > 0) { - mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); - mLocked.pointerSprite->setVisible(true); - } else { - mLocked.pointerSprite->setVisible(false); - } - - if (mLocked.pointerIconChanged || mLocked.presentationChanged) { - if (mLocked.presentation == Presentation::POINTER) { - if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) { - mLocked.pointerSprite->setIcon(mLocked.pointerIcon); - } else { - std::map<int32_t, SpriteIcon>::const_iterator iter = - mLocked.additionalMouseResources.find(mLocked.requestedPointerType); - if (iter != mLocked.additionalMouseResources.end()) { - std::map<int32_t, PointerAnimation>::const_iterator anim_iter = - mLocked.animationResources.find(mLocked.requestedPointerType); - if (anim_iter != mLocked.animationResources.end()) { - mLocked.animationFrameIndex = 0; - mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC); - startAnimationLocked(); - } - mLocked.pointerSprite->setIcon(iter->second); - } else { - ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType); - mLocked.pointerSprite->setIcon(mLocked.pointerIcon); - } - } - } else { - mLocked.pointerSprite->setIcon(mResources.spotAnchor); - } - mLocked.pointerIconChanged = false; - mLocked.presentationChanged = false; - } - - mSpriteController->closeTransaction(); -} - -PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) { - for (size_t i = 0; i < spots.size(); i++) { - Spot* spot = spots[i]; - if (spot->id == id) { - return spot; - } +void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) { + std::unordered_set<int32_t> displayIdSet; + for (DisplayViewport viewport : viewports) { + displayIdSet.insert(viewport.displayId); } - return nullptr; -} - -PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id, - std::vector<Spot*>& spots) { - // Remove spots until we have fewer than MAX_SPOTS remaining. - while (spots.size() >= MAX_SPOTS) { - Spot* spot = removeFirstFadingSpotLocked(spots); - if (!spot) { - spot = spots[0]; - spots.erase(spots.begin()); - } - releaseSpotLocked(spot); - } - - // Obtain a sprite from the recycled pool. - sp<Sprite> sprite; - if (! mLocked.recycledSprites.empty()) { - sprite = mLocked.recycledSprites.back(); - mLocked.recycledSprites.pop_back(); - } else { - sprite = mSpriteController->createSprite(); - } - - // Return the new spot. - Spot* spot = new Spot(id, sprite); - spots.push_back(spot); - return spot; -} - -PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) { - for (size_t i = 0; i < spots.size(); i++) { - Spot* spot = spots[i]; - if (spot->id == Spot::INVALID_ID) { - spots.erase(spots.begin() + i); - return spot; - } - } - return nullptr; -} - -void PointerController::releaseSpotLocked(Spot* spot) { - spot->sprite->clearIcon(); - - if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { - mLocked.recycledSprites.push_back(spot->sprite); - } - - delete spot; -} - -void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { - if (spot->id != Spot::INVALID_ID) { - spot->id = Spot::INVALID_ID; - startAnimationLocked(); - } -} - -void PointerController::fadeOutAndReleaseAllSpotsLocked() { - for (auto& it : mLocked.spotsByDisplay) { - const std::vector<Spot*>& spots = it.second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots; i++) { - Spot* spot = spots[i]; - fadeOutAndReleaseSpotLocked(spot); - } - } -} - -void PointerController::loadResourcesLocked() REQUIRES(mLock) { - if (!mLocked.viewport.isValid()) { - return; - } - - mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId); - mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); - - mLocked.additionalMouseResources.clear(); - mLocked.animationResources.clear(); - if (mLocked.presentation == Presentation::POINTER) { - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources, mLocked.viewport.displayId); - } - - mLocked.pointerIconChanged = true; -} - - -// --- PointerController::Spot --- - -void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, - int32_t displayId) { - sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); - sprite->setAlpha(alpha); - sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); - sprite->setPosition(x, y); - sprite->setDisplayId(displayId); - this->x = x; - this->y = y; - - if (icon != lastIcon) { - lastIcon = icon; - if (icon) { - sprite->setIcon(*icon); - sprite->setVisible(true); + std::scoped_lock lock(mLock); + for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) { + int32_t displayID = it->first; + if (!displayIdSet.count(displayID)) { + it = mLocked.spotControllers.erase(it); } else { - sprite->setVisible(false); + ++it; } } } diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 14c0679654c6..1f561da333b1 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -30,48 +30,14 @@ #include <memory> #include <vector> +#include "MouseCursorController.h" +#include "PointerControllerContext.h" #include "SpriteController.h" +#include "TouchSpotController.h" namespace android { /* - * Pointer resources. - */ -struct PointerResources { - SpriteIcon spotHover; - SpriteIcon spotTouch; - SpriteIcon spotAnchor; -}; - -struct PointerAnimation { - std::vector<SpriteIcon> animationFrames; - nsecs_t durationPerFrame; -}; - -/* - * Pointer controller policy interface. - * - * The pointer controller policy is used by the pointer controller to interact with - * the Window Manager and other system components. - * - * The actual implementation is partially supported by callbacks into the DVM - * via JNI. This interface is also mocked in the unit tests. - */ -class PointerControllerPolicyInterface : public virtual RefBase { -protected: - PointerControllerPolicyInterface() { } - virtual ~PointerControllerPolicyInterface() { } - -public: - virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0; - virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0; - virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources, - std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0; - virtual int32_t getDefaultPointerIconId() = 0; - virtual int32_t getCustomPointerIconId() = 0; -}; - -/* * Tracks pointer movements and draws the pointer sprite to a surface. * * Handles pointer acceleration and animation. @@ -81,15 +47,10 @@ public: static std::shared_ptr<PointerController> create( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, const sp<SpriteController>& spriteController); - enum class InactivityTimeout { - NORMAL = 0, - SHORT = 1, - }; - virtual ~PointerController(); + virtual ~PointerController() = default; - virtual bool getBounds(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const; + virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; virtual void move(float deltaX, float deltaY); virtual void setButtonState(int32_t buttonState); virtual int32_t getButtonState() const; @@ -101,129 +62,37 @@ public: virtual void setDisplayViewport(const DisplayViewport& viewport); virtual void setPresentation(Presentation presentation); - virtual void setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId); + virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId); virtual void clearSpots(); void updatePointerIcon(int32_t iconId); void setCustomPointerIcon(const SpriteIcon& icon); void setInactivityTimeout(InactivityTimeout inactivityTimeout); + void doInactivityTimeout(); + void doAnimate(nsecs_t timestamp); void reloadPointerResources(); + void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports); private: - static constexpr size_t MAX_RECYCLED_SPRITES = 12; - static constexpr size_t MAX_SPOTS = 12; - - enum { - MSG_INACTIVITY_TIMEOUT, - }; - - struct Spot { - static const uint32_t INVALID_ID = 0xffffffff; - - uint32_t id; - sp<Sprite> sprite; - float alpha; - float scale; - float x, y; - - inline Spot(uint32_t id, const sp<Sprite>& sprite) - : id(id), - sprite(sprite), - alpha(1.0f), - scale(1.0f), - x(0.0f), - y(0.0f), - lastIcon(nullptr) {} - - void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); + friend PointerControllerContext::LooperCallback; + friend PointerControllerContext::MessageHandler; - private: - const SpriteIcon* lastIcon; - }; + mutable std::mutex mLock; - class MessageHandler : public virtual android::MessageHandler { - public: - void handleMessage(const Message& message) override; - std::weak_ptr<PointerController> pointerController; - }; + PointerControllerContext mContext; - class LooperCallback : public virtual android::LooperCallback { - public: - int handleEvent(int fd, int events, void* data) override; - std::weak_ptr<PointerController> pointerController; - }; - - mutable Mutex mLock; - - sp<PointerControllerPolicyInterface> mPolicy; - sp<Looper> mLooper; - sp<SpriteController> mSpriteController; - sp<MessageHandler> mHandler; - sp<LooperCallback> mCallback; - - DisplayEventReceiver mDisplayEventReceiver; - - PointerResources mResources; + MouseCursorController mCursorController; struct Locked { - bool animationPending; - nsecs_t animationTime; - - size_t animationFrameIndex; - nsecs_t lastFrameUpdatedTime; - - DisplayViewport viewport; - - InactivityTimeout inactivityTimeout; - Presentation presentation; - bool presentationChanged; - - int32_t pointerFadeDirection; - float pointerX; - float pointerY; - float pointerAlpha; - sp<Sprite> pointerSprite; - SpriteIcon pointerIcon; - bool pointerIconChanged; - - std::map<int32_t, SpriteIcon> additionalMouseResources; - std::map<int32_t, PointerAnimation> animationResources; - int32_t requestedPointerType; - - int32_t buttonState; - - std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay; - std::vector<sp<Sprite>> recycledSprites; + std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers; } mLocked GUARDED_BY(mLock); PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, const sp<SpriteController>& spriteController); - - bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; - void setPositionLocked(float x, float y); - - void doAnimate(nsecs_t timestamp); - bool doFadingAnimationLocked(nsecs_t timestamp); - bool doBitmapAnimationLocked(nsecs_t timestamp); - void doInactivityTimeout(); - - void startAnimationLocked(); - - void resetInactivityTimeoutLocked(); - void removeInactivityTimeoutLocked(); - void updatePointerLocked(); - - Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); - Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots); - Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots); - void releaseSpotLocked(Spot* spot); - void fadeOutAndReleaseSpotLocked(Spot* spot); - void fadeOutAndReleaseAllSpotsLocked(); - - void loadResourcesLocked(); + void clearSpotsLocked(); }; } // namespace android diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp new file mode 100644 index 000000000000..2d7e22b01112 --- /dev/null +++ b/libs/input/PointerControllerContext.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PointerControllerContext.h" +#include "PointerController.h" + +namespace { +// Time to wait before starting the fade when the pointer is inactive. +const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds +const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds + +// The number of events to be read at once for DisplayEventReceiver. +const int EVENT_BUFFER_SIZE = 100; +} // namespace + +namespace android { + +// --- PointerControllerContext --- + +PointerControllerContext::PointerControllerContext( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController, PointerController& controller) + : mPolicy(policy), + mLooper(looper), + mSpriteController(spriteController), + mHandler(new MessageHandler()), + mCallback(new LooperCallback()), + mController(controller) { + std::scoped_lock lock(mLock); + mLocked.inactivityTimeout = InactivityTimeout::NORMAL; + mLocked.animationPending = false; +} + +PointerControllerContext::~PointerControllerContext() { + mLooper->removeMessages(mHandler); +} + +void PointerControllerContext::setInactivityTimeout(InactivityTimeout inactivityTimeout) { + std::scoped_lock lock(mLock); + + if (mLocked.inactivityTimeout != inactivityTimeout) { + mLocked.inactivityTimeout = inactivityTimeout; + resetInactivityTimeoutLocked(); + } +} + +void PointerControllerContext::startAnimation() { + std::scoped_lock lock(mLock); + if (!mLocked.animationPending) { + mLocked.animationPending = true; + mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); + mDisplayEventReceiver.requestNextVsync(); + } +} + +void PointerControllerContext::resetInactivityTimeout() { + std::scoped_lock lock(mLock); + resetInactivityTimeoutLocked(); +} + +void PointerControllerContext::resetInactivityTimeoutLocked() REQUIRES(mLock) { + mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); + + nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT + ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT + : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; + mLooper->sendMessageDelayed(timeout, mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); +} + +void PointerControllerContext::removeInactivityTimeout() { + std::scoped_lock lock(mLock); + mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); +} + +void PointerControllerContext::setAnimationPending(bool animationPending) { + std::scoped_lock lock(mLock); + mLocked.animationPending = animationPending; +} + +nsecs_t PointerControllerContext::getAnimationTime() { + std::scoped_lock lock(mLock); + return mLocked.animationTime; +} + +void PointerControllerContext::setHandlerController(std::shared_ptr<PointerController> controller) { + mHandler->pointerController = controller; +} + +void PointerControllerContext::setCallbackController( + std::shared_ptr<PointerController> controller) { + mCallback->pointerController = controller; +} + +sp<PointerControllerPolicyInterface> PointerControllerContext::getPolicy() { + return mPolicy; +} + +sp<SpriteController> PointerControllerContext::getSpriteController() { + return mSpriteController; +} + +void PointerControllerContext::initializeDisplayEventReceiver() { + if (mDisplayEventReceiver.initCheck() == NO_ERROR) { + mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, Looper::EVENT_INPUT, + mCallback, nullptr); + } else { + ALOGE("Failed to initialize DisplayEventReceiver."); + } +} + +void PointerControllerContext::handleDisplayEvents() { + bool gotVsync = false; + ssize_t n; + nsecs_t timestamp; + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + for (size_t i = 0; i < static_cast<size_t>(n); ++i) { + if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { + timestamp = buf[i].header.timestamp; + gotVsync = true; + } + } + } + if (gotVsync) { + mController.doAnimate(timestamp); + } +} + +void PointerControllerContext::MessageHandler::handleMessage(const Message& message) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + + if (controller == nullptr) { + ALOGE("PointerController instance was released before processing message: what=%d", + message.what); + return; + } + switch (message.what) { + case MSG_INACTIVITY_TIMEOUT: + controller->doInactivityTimeout(); + break; + } +} + +int PointerControllerContext::LooperCallback::handleEvent(int /* fd */, int events, + void* /* data */) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + if (controller == nullptr) { + ALOGW("PointerController instance was released with pending callbacks. events=0x%x", + events); + return 0; // Remove the callback, the PointerController is gone anyways + } + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events); + return 0; // remove the callback + } + + if (!(events & Looper::EVENT_INPUT)) { + ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); + return 1; // keep the callback + } + + controller->mContext.handleDisplayEvents(); + return 1; // keep the callback +} + +} // namespace android diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h new file mode 100644 index 000000000000..92e1bda25f56 --- /dev/null +++ b/libs/input/PointerControllerContext.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_POINTER_CONTROLLER_CONTEXT_H +#define _UI_POINTER_CONTROLLER_CONTEXT_H + +#include <PointerControllerInterface.h> +#include <gui/DisplayEventReceiver.h> +#include <input/DisplayViewport.h> +#include <input/Input.h> +#include <ui/DisplayInfo.h> +#include <utils/BitSet.h> +#include <utils/Looper.h> +#include <utils/RefBase.h> + +#include <map> +#include <memory> +#include <vector> + +#include "SpriteController.h" + +namespace android { + +class PointerController; + +/* + * Pointer resources. + */ +struct PointerResources { + SpriteIcon spotHover; + SpriteIcon spotTouch; + SpriteIcon spotAnchor; +}; + +struct PointerAnimation { + std::vector<SpriteIcon> animationFrames; + nsecs_t durationPerFrame; +}; + +enum class InactivityTimeout { + NORMAL = 0, + SHORT = 1, +}; + +/* + * Pointer controller policy interface. + * + * The pointer controller policy is used by the pointer controller to interact with + * the Window Manager and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class PointerControllerPolicyInterface : public virtual RefBase { +protected: + PointerControllerPolicyInterface() {} + virtual ~PointerControllerPolicyInterface() {} + +public: + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0; + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0; + virtual void loadAdditionalMouseResources( + std::map<int32_t, SpriteIcon>* outResources, + std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0; + virtual int32_t getDefaultPointerIconId() = 0; + virtual int32_t getCustomPointerIconId() = 0; +}; + +/* + * Contains logic and resources shared among PointerController, + * MouseCursorController, and TouchSpotController. + */ + +class PointerControllerContext { +public: + PointerControllerContext(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, const sp<SpriteController>& spriteController, + PointerController& controller); + ~PointerControllerContext(); + + void removeInactivityTimeout(); + void resetInactivityTimeout(); + void startAnimation(); + void setInactivityTimeout(InactivityTimeout inactivityTimeout); + + void setAnimationPending(bool animationPending); + nsecs_t getAnimationTime(); + + void clearSpotsByDisplay(int32_t displayId); + + void setHandlerController(std::shared_ptr<PointerController> controller); + void setCallbackController(std::shared_ptr<PointerController> controller); + + sp<PointerControllerPolicyInterface> getPolicy(); + sp<SpriteController> getSpriteController(); + + void initializeDisplayEventReceiver(); + void handleDisplayEvents(); + + class MessageHandler : public virtual android::MessageHandler { + public: + enum { + MSG_INACTIVITY_TIMEOUT, + }; + + void handleMessage(const Message& message) override; + std::weak_ptr<PointerController> pointerController; + }; + + class LooperCallback : public virtual android::LooperCallback { + public: + int handleEvent(int fd, int events, void* data) override; + std::weak_ptr<PointerController> pointerController; + }; + +private: + sp<PointerControllerPolicyInterface> mPolicy; + sp<Looper> mLooper; + sp<SpriteController> mSpriteController; + sp<MessageHandler> mHandler; + sp<LooperCallback> mCallback; + + DisplayEventReceiver mDisplayEventReceiver; + + PointerController& mController; + + mutable std::mutex mLock; + + struct Locked { + bool animationPending; + nsecs_t animationTime; + + InactivityTimeout inactivityTimeout; + } mLocked GUARDED_BY(mLock); + + void resetInactivityTimeoutLocked(); +}; + +} // namespace android + +#endif // _UI_POINTER_CONTROLLER_CONTEXT_H diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp new file mode 100644 index 000000000000..c7430ceead41 --- /dev/null +++ b/libs/input/TouchSpotController.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "TouchSpotController" + +// Log debug messages about pointer updates +#define DEBUG_SPOT_UPDATES 0 + +#include "TouchSpotController.h" + +#include <log/log.h> + +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> + +namespace { +// Time to spend fading out the spot completely. +const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms +} // namespace + +namespace android { + +// --- Spot --- + +void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, + int32_t displayId) { + sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); + sprite->setAlpha(alpha); + sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); + sprite->setPosition(x, y); + sprite->setDisplayId(displayId); + this->x = x; + this->y = y; + + if (icon != mLastIcon) { + mLastIcon = icon; + if (icon) { + sprite->setIcon(*icon); + sprite->setVisible(true); + } else { + sprite->setVisible(false); + } + } +} + +// --- TouchSpotController --- + +TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context) + : mDisplayId(displayId), mContext(context) { + mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); +} + +TouchSpotController::~TouchSpotController() { + std::scoped_lock lock(mLock); + + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots; i++) { + delete mLocked.displaySpots[i]; + } + mLocked.displaySpots.clear(); +} + +void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits) { +#if DEBUG_SPOT_UPDATES + ALOGD("setSpots: idBits=%08x", spotIdBits.value); + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + const PointerCoords& c = spotCoords[spotIdToIndex[id]]; + ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, + c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y), + c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId); + } +#endif + + std::scoped_lock lock(mLock); + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + // Add or move spots for fingers that are down. + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const PointerCoords& c = spotCoords[spotIdToIndex[id]]; + const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 + ? mResources.spotTouch + : mResources.spotHover; + float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); + float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); + + Spot* spot = getSpot(id, mLocked.displaySpots); + if (!spot) { + spot = createAndAddSpotLocked(id, mLocked.displaySpots); + } + + spot->updateSprite(&icon, x, y, mDisplayId); + } + + for (Spot* spot : mLocked.displaySpots) { + if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) { + fadeOutAndReleaseSpotLocked(spot); + } + } + + spriteController->closeTransaction(); +} + +void TouchSpotController::clearSpots() { +#if DEBUG_SPOT_UPDATES + ALOGD("clearSpots"); +#endif + + std::scoped_lock lock(mLock); + fadeOutAndReleaseAllSpotsLocked(); +} + +TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id, + const std::vector<Spot*>& spots) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; + if (spot->id == id) { + return spot; + } + } + return nullptr; +} + +TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id, + std::vector<Spot*>& spots) { + // Remove spots until we have fewer than MAX_SPOTS remaining. + while (spots.size() >= MAX_SPOTS) { + Spot* spot = removeFirstFadingSpotLocked(spots); + if (!spot) { + spot = spots[0]; + spots.erase(spots.begin()); + } + releaseSpotLocked(spot); + } + + // Obtain a sprite from the recycled pool. + sp<Sprite> sprite; + if (!mLocked.recycledSprites.empty()) { + sprite = mLocked.recycledSprites.back(); + mLocked.recycledSprites.pop_back(); + } else { + sprite = mContext.getSpriteController()->createSprite(); + } + + // Return the new spot. + Spot* spot = new Spot(id, sprite); + spots.push_back(spot); + return spot; +} + +TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked( + std::vector<Spot*>& spots) REQUIRES(mLock) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; + if (spot->id == Spot::INVALID_ID) { + spots.erase(spots.begin() + i); + return spot; + } + } + return NULL; +} + +void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) { + spot->sprite->clearIcon(); + + if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { + mLocked.recycledSprites.push_back(spot->sprite); + } + + delete spot; +} + +void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) { + if (spot->id != Spot::INVALID_ID) { + spot->id = Spot::INVALID_ID; + mContext.startAnimation(); + } +} + +void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) { + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots; i++) { + Spot* spot = mLocked.displaySpots[i]; + fadeOutAndReleaseSpotLocked(spot); + } +} + +void TouchSpotController::reloadSpotResources() { + mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); +} + +bool TouchSpotController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) { + std::scoped_lock lock(mLock); + nsecs_t animationTime = mContext.getAnimationTime(); + nsecs_t frameDelay = timestamp - animationTime; + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots;) { + Spot* spot = mLocked.displaySpots[i]; + if (spot->id == Spot::INVALID_ID) { + spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; + if (spot->alpha <= 0) { + mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i); + releaseSpotLocked(spot); + numSpots--; + continue; + } else { + spot->sprite->setAlpha(spot->alpha); + keepAnimating = true; + } + } + ++i; + } + return keepAnimating; +} + +} // namespace android diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h new file mode 100644 index 000000000000..f3b355010bee --- /dev/null +++ b/libs/input/TouchSpotController.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_TOUCH_SPOT_CONTROLLER_H +#define _UI_TOUCH_SPOT_CONTROLLER_H + +#include "PointerControllerContext.h" + +namespace android { + +/* + * Helper class for PointerController that specifically handles + * touch spot resources and actions for a single display. + */ +class TouchSpotController { +public: + TouchSpotController(int32_t displayId, PointerControllerContext& context); + ~TouchSpotController(); + void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits); + void clearSpots(); + + void reloadSpotResources(); + bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating); + +private: + struct Spot { + static const uint32_t INVALID_ID = 0xffffffff; + + uint32_t id; + sp<Sprite> sprite; + float alpha; + float scale; + float x, y; + + inline Spot(uint32_t id, const sp<Sprite>& sprite) + : id(id), + sprite(sprite), + alpha(1.0f), + scale(1.0f), + x(0.0f), + y(0.0f), + mLastIcon(nullptr) {} + + void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); + + private: + const SpriteIcon* mLastIcon; + }; + + int32_t mDisplayId; + + mutable std::mutex mLock; + + PointerResources mResources; + + PointerControllerContext& mContext; + + static constexpr size_t MAX_RECYCLED_SPRITES = 12; + static constexpr size_t MAX_SPOTS = 12; + + struct Locked { + std::vector<Spot*> displaySpots; + std::vector<sp<Sprite>> recycledSprites; + + } mLocked GUARDED_BY(mLock); + + Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); + Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots); + Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots); + void releaseSpotLocked(Spot* spot); + void fadeOutAndReleaseSpotLocked(Spot* spot); + void fadeOutAndReleaseAllSpotsLocked(); +}; + +} // namespace android + +#endif // _UI_TOUCH_SPOT_CONTROLLER_H diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index 6e129a064385..b67088a389b6 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -178,9 +178,6 @@ void PointerControllerTest::ensureDisplayViewportIsSet() { viewport.deviceWidth = 400; viewport.deviceHeight = 300; mPointerController->setDisplayViewport(viewport); - - // The first call to setDisplayViewport should trigger the loading of the necessary resources. - EXPECT_TRUE(mPolicy->allResourcesAreLoaded()); } void PointerControllerTest::loopThread() { @@ -208,6 +205,7 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { TEST_F(PointerControllerTest, updatePointerIcon) { ensureDisplayViewportIsSet(); + mPointerController->setPresentation(PointerController::Presentation::POINTER); mPointerController->unfade(PointerController::Transition::IMMEDIATE); int32_t type = CURSOR_TYPE_ADDITIONAL; @@ -247,8 +245,6 @@ TEST_F(PointerControllerTest, setCustomPointerIcon) { TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) { mPointerController->setPresentation(PointerController::Presentation::POINTER); - mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1); - mPointerController->clearSpots(); mPointerController->setPosition(1.0f, 1.0f); mPointerController->move(1.0f, 1.0f); mPointerController->unfade(PointerController::Transition::IMMEDIATE); diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index e910add86d3c..82b746f6a9e5 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -590,7 +590,7 @@ android_media_MediaPlayer_getSyncParams(JNIEnv *env, jobject thiz) ALOGV("getSyncSettings: %d %d %f %f", scp.sync.mSource, scp.sync.mAudioAdjustMode, scp.sync.mTolerance, scp.frameRate); - // sanity check params + // check params if (scp.sync.mSource >= AVSYNC_SOURCE_MAX || scp.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX || scp.sync.mTolerance < 0.f diff --git a/media/jni/android_media_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp index f75200868a0d..d1ce30a3e827 100644 --- a/media/jni/android_media_MediaSync.cpp +++ b/media/jni/android_media_MediaSync.cpp @@ -451,7 +451,7 @@ static jobject android_media_MediaSync_getSyncParams(JNIEnv *env, jobject thiz) ALOGV("getSyncParams: %d %d %f %f", scs.sync.mSource, scs.sync.mAudioAdjustMode, scs.sync.mTolerance, scs.frameRate); - // sanity check params + // check params if (scs.sync.mSource >= AVSYNC_SOURCE_MAX || scs.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX || scs.sync.mTolerance < 0.f diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp index f4d65d0a397f..a74ae5307a36 100644 --- a/media/jni/audioeffect/Visualizer.cpp +++ b/media/jni/audioeffect/Visualizer.cpp @@ -34,21 +34,9 @@ namespace android { // --------------------------------------------------------------------------- -Visualizer::Visualizer (const String16& opPackageName, - int32_t priority, - effect_callback_t cbf, - void* user, - audio_session_t sessionId) - : AudioEffect(SL_IID_VISUALIZATION, opPackageName, NULL, priority, cbf, user, sessionId), - mCaptureRate(CAPTURE_RATE_DEF), - mCaptureSize(CAPTURE_SIZE_DEF), - mSampleRate(44100000), - mScalingMode(VISUALIZER_SCALING_MODE_NORMALIZED), - mMeasurementMode(MEASUREMENT_MODE_NONE), - mCaptureCallBack(NULL), - mCaptureCbkUser(NULL) +Visualizer::Visualizer (const String16& opPackageName) + : AudioEffect(opPackageName) { - initCaptureSize(); } Visualizer::~Visualizer() @@ -58,6 +46,23 @@ Visualizer::~Visualizer() setCaptureCallBack(NULL, NULL, 0, 0); } +status_t Visualizer::set(int32_t priority, + effect_callback_t cbf, + void* user, + audio_session_t sessionId, + audio_io_handle_t io, + const AudioDeviceTypeAddr& device, + bool probe) +{ + status_t status = AudioEffect::set( + SL_IID_VISUALIZATION, nullptr, priority, cbf, user, sessionId, io, device, probe); + if (status == NO_ERROR || status == ALREADY_EXISTS) { + initCaptureSize(); + } + return status; +} + + void Visualizer::release() { ALOGV("Visualizer::release()"); diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h index d4672a95c6d8..8b6a62f25638 100644 --- a/media/jni/audioeffect/Visualizer.h +++ b/media/jni/audioeffect/Visualizer.h @@ -65,14 +65,22 @@ public: /* Constructor. * See AudioEffect constructor for details on parameters. */ - Visualizer(const String16& opPackageName, - int32_t priority = 0, - effect_callback_t cbf = NULL, - void* user = NULL, - audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX); + explicit Visualizer(const String16& opPackageName); ~Visualizer(); + /** + * Initialize an uninitialized Visualizer. + * See AudioEffect 'set' function for details on parameters. + */ + status_t set(int32_t priority = 0, + effect_callback_t cbf = NULL, + void* user = NULL, + audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX, + audio_io_handle_t io = AUDIO_IO_HANDLE_NONE, + const AudioDeviceTypeAddr& device = {}, + bool probe = false); + // Declared 'final' because we call this in ~Visualizer(). status_t setEnabled(bool enabled) final; @@ -163,15 +171,15 @@ private: uint32_t initCaptureSize(); Mutex mCaptureLock; - uint32_t mCaptureRate; - uint32_t mCaptureSize; - uint32_t mSampleRate; - uint32_t mScalingMode; - uint32_t mMeasurementMode; - capture_cbk_t mCaptureCallBack; - void *mCaptureCbkUser; + uint32_t mCaptureRate = CAPTURE_RATE_DEF; + uint32_t mCaptureSize = CAPTURE_SIZE_DEF; + uint32_t mSampleRate = 44100000; + uint32_t mScalingMode = VISUALIZER_SCALING_MODE_NORMALIZED; + uint32_t mMeasurementMode = MEASUREMENT_MODE_NONE; + capture_cbk_t mCaptureCallBack = nullptr; + void *mCaptureCbkUser = nullptr; sp<CaptureThread> mCaptureThread; - uint32_t mCaptureFlags; + uint32_t mCaptureFlags = 0; }; diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index dbe7b4b619c9..96961ac21a2d 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -337,22 +337,21 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t } // create the native AudioEffect object - lpAudioEffect = new AudioEffect(typeStr, - String16(opPackageNameStr.c_str()), - uuidStr, - priority, - effectCallback, - &lpJniStorage->mCallbackData, - (audio_session_t) sessionId, - AUDIO_IO_HANDLE_NONE, - device, - probe); + lpAudioEffect = new AudioEffect(String16(opPackageNameStr.c_str())); if (lpAudioEffect == 0) { ALOGE("Error creating AudioEffect"); goto setup_failure; } - + lpAudioEffect->set(typeStr, + uuidStr, + priority, + effectCallback, + &lpJniStorage->mCallbackData, + (audio_session_t) sessionId, + AUDIO_IO_HANDLE_NONE, + device, + probe); lStatus = AudioEffectJni::translateNativeErrorToJava(lpAudioEffect->initCheck()); if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) { ALOGE("AudioEffect initCheck failed %d", lStatus); diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp index f9a77f474c50..4c5970a30a05 100644 --- a/media/jni/audioeffect/android_media_Visualizer.cpp +++ b/media/jni/audioeffect/android_media_Visualizer.cpp @@ -382,15 +382,15 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th } // create the native Visualizer object - lpVisualizer = new Visualizer(String16(opPackageNameStr.c_str()), - 0, - android_media_visualizer_effect_callback, - lpJniStorage, - (audio_session_t) sessionId); + lpVisualizer = new Visualizer(String16(opPackageNameStr.c_str())); if (lpVisualizer == 0) { ALOGE("Error creating Visualizer"); goto setup_failure; } + lpVisualizer->set(0, + android_media_visualizer_effect_callback, + lpJniStorage, + (audio_session_t) sessionId); lStatus = translateError(lpVisualizer->initCheck()); if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) { diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterFunction.java b/media/mca/filterfw/java/android/filterfw/core/FilterFunction.java index ce81a18eeaa8..ab9ae8ab2ac8 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FilterFunction.java +++ b/media/mca/filterfw/java/android/filterfw/core/FilterFunction.java @@ -43,7 +43,7 @@ public class FilterFunction { public Frame execute(KeyValueMap inputMap) { int filterOutCount = mFilter.getNumberOfOutputs(); - // Sanity checks + // Validation checks if (filterOutCount > 1) { throw new RuntimeException("Calling execute on filter " + mFilter + " with multiple " + "outputs! Use executeMulti() instead!"); diff --git a/media/mca/filterfw/jni/jni_native_program.cpp b/media/mca/filterfw/jni/jni_native_program.cpp index 14246078225e..cd4f7187c250 100644 --- a/media/mca/filterfw/jni/jni_native_program.cpp +++ b/media/mca/filterfw/jni/jni_native_program.cpp @@ -134,7 +134,7 @@ jboolean Java_android_filterfw_core_NativeProgram_callNativeProcess(JNIEnv* env, jobject output) { NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz); - // Sanity checks + // Validation checks if (!program || !inputs) { return JNI_FALSE; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java index 0ae640dd7910..e74bda8a6b35 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java @@ -16,16 +16,6 @@ package com.android.mediaframeworktest.helpers; -import com.android.ex.camera2.blocking.BlockingCameraManager; -import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException; -import com.android.ex.camera2.blocking.BlockingSessionCallback; -import com.android.ex.camera2.blocking.BlockingStateCallback; -import com.android.ex.camera2.exceptions.TimeoutRuntimeException; - -import junit.framework.Assert; - -import org.mockito.Mockito; - import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageFormat; @@ -64,6 +54,16 @@ import android.view.WindowManager; import androidx.test.InstrumentationRegistry; +import com.android.ex.camera2.blocking.BlockingCameraManager; +import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException; +import com.android.ex.camera2.blocking.BlockingSessionCallback; +import com.android.ex.camera2.blocking.BlockingStateCallback; +import com.android.ex.camera2.exceptions.TimeoutRuntimeException; + +import junit.framework.Assert; + +import org.mockito.Mockito; + import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Array; @@ -77,8 +77,8 @@ import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @@ -225,7 +225,7 @@ public class CameraTestUtils extends Assert { } /** - * Dummy listener that release the image immediately once it is available. + * Mock listener that release the image immediately once it is available. * * <p> * It can be used for the case where we don't care the image data at all. diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/Preconditions.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/Preconditions.java index 96b0424595e3..a77b2898c5e0 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/Preconditions.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/Preconditions.java @@ -22,7 +22,7 @@ import java.util.Objects; /** * Helper set of methods to perform precondition checks before starting method execution. * - * <p>Typically used to sanity check arguments or the current object state.</p> + * <p>Typically used to check arguments or the current object state.</p> */ /** * (non-Javadoc) diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java index b3f443b30a70..9a64b58a080d 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java @@ -16,7 +16,7 @@ package com.android.mediaframeworktest.helpers; -import junit.framework.Assert; +import static com.android.mediaframeworktest.helpers.AssertHelpers.assertArrayContainsAnyOf; import android.graphics.ImageFormat; import android.graphics.Rect; @@ -31,6 +31,8 @@ import android.util.Range; import android.util.Rational; import android.util.Size; +import junit.framework.Assert; + import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; @@ -40,8 +42,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import static com.android.mediaframeworktest.helpers.AssertHelpers.assertArrayContainsAnyOf; - /** * Helpers to get common static info out of the camera. * @@ -435,7 +435,7 @@ public class StaticMetadata { } /** - * Get max AE regions and do sanity check. + * Get max AE regions and do validation check. * * @return AE max regions supported by the camera device */ @@ -448,7 +448,7 @@ public class StaticMetadata { } /** - * Get max AWB regions and do sanity check. + * Get max AWB regions and do validation check. * * @return AWB max regions supported by the camera device */ @@ -461,7 +461,7 @@ public class StaticMetadata { } /** - * Get max AF regions and do sanity check. + * Get max AF regions and do validation check. * * @return AF max regions supported by the camera device */ @@ -545,7 +545,7 @@ public class StaticMetadata { } /** - * Get available thumbnail sizes and do the sanity check. + * Get available thumbnail sizes and do the validation check. * * @return The array of available thumbnail sizes */ @@ -573,7 +573,7 @@ public class StaticMetadata { } /** - * Get available focal lengths and do the sanity check. + * Get available focal lengths and do the validation check. * * @return The array of available focal lengths */ @@ -594,7 +594,7 @@ public class StaticMetadata { } /** - * Get available apertures and do the sanity check. + * Get available apertures and do the validation check. * * @return The non-null array of available apertures */ @@ -909,7 +909,7 @@ public class StaticMetadata { } /** - * Get hyperfocalDistance and do the sanity check. + * Get hyperfocalDistance and do the validation check. * <p> * Note that, this tag is optional, will return -1 if this tag is not * available. @@ -1068,7 +1068,7 @@ public class StaticMetadata { } /** - * get android.control.availableModes and do the sanity check. + * get android.control.availableModes and do the validation check. * * @return available control modes. */ @@ -1124,7 +1124,7 @@ public class StaticMetadata { } /** - * Get aeAvailableModes and do the sanity check. + * Get aeAvailableModes and do the validation check. * * <p>Depending on the check level this class has, for WAR or COLLECT levels, * If the aeMode list is invalid, return an empty mode array. The the caller doesn't @@ -1196,7 +1196,7 @@ public class StaticMetadata { } /** - * Get available AWB modes and do the sanity check. + * Get available AWB modes and do the validation check. * * @return array that contains available AWB modes, empty array if awbAvailableModes is * unavailable. @@ -1222,7 +1222,7 @@ public class StaticMetadata { } /** - * Get available AF modes and do the sanity check. + * Get available AF modes and do the validation check. * * @return array that contains available AF modes, empty array if afAvailableModes is * unavailable. @@ -1580,7 +1580,7 @@ public class StaticMetadata { } /** - * Get value of key android.control.aeCompensationStep and do the sanity check. + * Get value of key android.control.aeCompensationStep and do the validation check. * * @return default value if the value is null. */ @@ -1605,7 +1605,7 @@ public class StaticMetadata { } /** - * Get value of key android.control.aeCompensationRange and do the sanity check. + * Get value of key android.control.aeCompensationRange and do the validation check. * * @return default value if the value is null or malformed. */ @@ -1635,7 +1635,7 @@ public class StaticMetadata { } /** - * Get availableVideoStabilizationModes and do the sanity check. + * Get availableVideoStabilizationModes and do the validation check. * * @return available video stabilization modes, empty array if it is unavailable. */ @@ -1666,7 +1666,7 @@ public class StaticMetadata { } /** - * Get availableOpticalStabilization and do the sanity check. + * Get availableOpticalStabilization and do the validation check. * * @return available optical stabilization modes, empty array if it is unavailable. */ @@ -1780,7 +1780,7 @@ public class StaticMetadata { } /** - * Get max pipeline depth and do the sanity check. + * Get max pipeline depth and do the validation check. * * @return max pipeline depth, default value if it is not available. */ @@ -1846,7 +1846,7 @@ public class StaticMetadata { /** - * Get available capabilities and do the sanity check. + * Get available capabilities and do the validation check. * * @return reported available capabilities list, empty list if the value is unavailable. */ @@ -2070,7 +2070,7 @@ public class StaticMetadata { } /** - * Get max number of output raw streams and do the basic sanity check. + * Get max number of output raw streams and do the basic validation check. * * @return reported max number of raw output stream */ @@ -2083,7 +2083,7 @@ public class StaticMetadata { } /** - * Get max number of output processed streams and do the basic sanity check. + * Get max number of output processed streams and do the basic validation check. * * @return reported max number of processed output stream */ @@ -2096,7 +2096,7 @@ public class StaticMetadata { } /** - * Get max number of output stalling processed streams and do the basic sanity check. + * Get max number of output stalling processed streams and do the basic validation check. * * @return reported max number of stalling processed output stream */ @@ -2109,7 +2109,7 @@ public class StaticMetadata { } /** - * Get lens facing and do the sanity check + * Get lens facing and do the validation check * @return lens facing, return default value (BACK) if value is unavailable. */ public int getLensFacingChecked() { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2CaptureRequestTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2CaptureRequestTest.java index 31b79677f9ff..47caf0a0bc9b 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2CaptureRequestTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2CaptureRequestTest.java @@ -309,7 +309,7 @@ public class Camera2CaptureRequestTest extends Camera2SurfaceViewTestCase { private void changeExposure(CaptureRequest.Builder requestBuilder, long expTime, int sensitivity) { // Check if the max analog sensitivity is available and no larger than max sensitivity. - // The max analog sensitivity is not actually used here. This is only an extra sanity check. + // The max analog sensitivity is not actually used here. This is only an extra check. mStaticInfo.getMaxAnalogSensitivityChecked(); expTime = mStaticInfo.getExposureClampToRange(expTime); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java index 6a4db5791d5c..dc8da4868df3 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java @@ -985,7 +985,7 @@ public class Camera2RecordingTest extends Camera2SurfaceViewTestCase { } /** - * Validate video snapshot capture image object sanity and test. + * Validate video snapshot capture image object soundness and test. * * <p> Check for size, format and jpeg decoding</p> * diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java index f7373f7b68db..cbdcc36eea3a 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java @@ -570,7 +570,7 @@ public class Camera2StillCaptureTest extends Camera2SurfaceViewTestCase { } /** - * Validate JPEG capture image object sanity and test. + * Validate JPEG capture image object soundness and test. * <p> * In addition to image object sanity, this function also does the decoding * test, which is slower. diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index 7aa997e39307..a62c287ece41 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -259,18 +259,19 @@ public class DeviceDiscoveryService extends Service { private void onDeviceFound(@Nullable DeviceFilterPair device) { if (device == null) return; - if (mDevicesFound.contains(device)) { - return; - } - - if (DEBUG) Log.i(LOG_TAG, "Found device " + device); - Handler.getMain().sendMessage(obtainMessage( DeviceDiscoveryService::onDeviceFoundMainThread, this, device)); } @MainThread void onDeviceFoundMainThread(@NonNull DeviceFilterPair device) { + if (mDevicesFound.contains(device)) { + Log.i(LOG_TAG, "Skipping device " + device + " - already among found devices"); + return; + } + + Log.i(LOG_TAG, "Found device " + device); + if (mDevicesFound.isEmpty()) { onReadyToShowUI(); } @@ -432,10 +433,10 @@ public class DeviceDiscoveryService extends Service { @Override public String toString() { - return "DeviceFilterPair{" + - "device=" + device + - ", filter=" + filter + - '}'; + return "DeviceFilterPair{" + + "device=" + device + " " + getDisplayName() + + ", filter=" + filter + + '}'; } } diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeView.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeView.java index 276d55ee9a5e..9fe7ab655e46 100644 --- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeView.java +++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeView.java @@ -26,7 +26,7 @@ import android.os.Message; import android.view.View; /** - * Dummy view to emulate stuff an OEM may want to do. + * Fake view to emulate stuff an OEM may want to do. */ public class FakeView extends View { static final long TICK_DELAY = 30*1000; // 30 seconds diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index 1e4c7cac4404..52d2b3c919d9 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -376,8 +376,12 @@ public abstract class Tile implements Parcelable { * Check whether tile only has primary profile. */ public boolean isPrimaryProfileOnly() { - String profile = mMetaData != null - ? mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL; + return isPrimaryProfileOnly(mMetaData); + } + + static boolean isPrimaryProfileOnly(Bundle metaData) { + String profile = metaData != null + ? metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL; profile = (profile != null ? profile : PROFILE_ALL); return TextUtils.equals(profile, PROFILE_PRIMARY); } diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java index ace50f30663d..49f6bd8c3334 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java @@ -339,6 +339,16 @@ public class TileUtils { private static void loadTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData, ComponentInfo componentInfo) { + // Skip loading tile if the component is tagged primary_profile_only but not running on + // the current user. + if (user.getIdentifier() != ActivityManager.getCurrentUser() + && Tile.isPrimaryProfileOnly(componentInfo.metaData)) { + Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent " + + intent + " is primary profile only, skip loading tile for uid " + + user.getIdentifier()); + return; + } + String categoryKey = defaultCategory; // Load category if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY)) diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 3015397ff1a3..bf5ab1c9951a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -256,7 +256,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } /** - * Sanity warning: this wipes out mScoreCache, so use with extreme caution + * Validity warning: this wipes out mScoreCache, so use with extreme caution * @param workThread substitute Handler thread, for testing purposes only */ @VisibleForTesting diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index 9b4b97e7f55d..176905305506 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -17,11 +17,14 @@ package com.android.settingslib.drawer; import static com.android.settingslib.drawer.TileUtils.IA_SETTINGS_ACTION; +import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI; +import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL; +import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY; import static com.google.common.truth.Truth.assertThat; @@ -189,7 +192,7 @@ public class TileUtilsTest { List<Tile> outTiles = new ArrayList<>(); List<ResolveInfo> info = new ArrayList<>(); ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, "my title", 0); + URI_GET_SUMMARY, "my title", 0, PROFILE_ALL); info.add(resolveInfo); when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) @@ -211,7 +214,7 @@ public class TileUtilsTest { List<Tile> outTiles = new ArrayList<>(); List<ResolveInfo> info = new ArrayList<>(); ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, null, 123); + URI_GET_SUMMARY, null, 123, PROFILE_ALL); info.add(resolveInfo); when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) @@ -235,7 +238,7 @@ public class TileUtilsTest { List<Tile> outTiles = new ArrayList<>(); List<ResolveInfo> info = new ArrayList<>(); ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, null, 123); + URI_GET_SUMMARY, null, 123, PROFILE_ALL); resolveInfo.activityInfo.packageName = "com.android.settings"; resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings"; info.add(resolveInfo); @@ -258,7 +261,7 @@ public class TileUtilsTest { final List<Tile> outTiles = new ArrayList<>(); final List<ResolveInfo> info = new ArrayList<>(); final ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, null, 123); + URI_GET_SUMMARY, null, 123, PROFILE_ALL); resolveInfo.activityInfo.packageName = "com.android.settings"; resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings"; info.add(resolveInfo); @@ -290,7 +293,7 @@ public class TileUtilsTest { List<Tile> outTiles = new ArrayList<>(); List<ResolveInfo> info = new ArrayList<>(); ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, null, 123); + URI_GET_SUMMARY, null, 123, PROFILE_ALL); resolveInfo.activityInfo.metaData .putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, true); info.add(resolveInfo); @@ -327,6 +330,26 @@ public class TileUtilsTest { assertThat(outTiles).hasSize(2); } + @Test + public void loadTilesForAction_isPrimaryProfileOnly_shouldSkipNonPrimaryUserTiles() { + Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); + List<Tile> outTiles = new ArrayList<>(); + List<ResolveInfo> info = new ArrayList<>(); + ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, + URI_GET_SUMMARY, null, 123, PROFILE_PRIMARY); + info.add(resolveInfo); + + when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) + .thenReturn(info); + when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(), + anyInt())).thenReturn(info); + + TileUtils.loadTilesForAction(mContext, new UserHandle(10), IA_SETTINGS_ACTION, + addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */); + + assertThat(outTiles).isEmpty(); + } + public static ResolveInfo newInfo(boolean systemApp, String category) { return newInfo(systemApp, category, null); } @@ -337,14 +360,14 @@ public class TileUtilsTest { private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint, String iconUri, String summaryUri) { - return newInfo(systemApp, category, keyHint, iconUri, summaryUri, null, 0); + return newInfo(systemApp, category, keyHint, iconUri, summaryUri, null, 0, PROFILE_ALL); } private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint, - String iconUri, String summaryUri, String title, int titleResId) { + String iconUri, String summaryUri, String title, int titleResId, String profile) { final Bundle metaData = newMetaData(category, keyHint, iconUri, summaryUri, title, - titleResId); + titleResId, profile); final ResolveInfo info = new ResolveInfo(); info.system = systemApp; @@ -358,6 +381,7 @@ public class TileUtilsTest { info.providerInfo.packageName = "abc"; info.providerInfo.name = "456"; info.providerInfo.authority = "auth"; + info.providerInfo.metaData = metaData; ShadowTileUtils.setMetaData(metaData); info.providerInfo.applicationInfo = new ApplicationInfo(); @@ -369,7 +393,7 @@ public class TileUtilsTest { } private static Bundle newMetaData(String category, String keyHint, String iconUri, - String summaryUri, String title, int titleResId) { + String summaryUri, String title, int titleResId, String profile) { final Bundle metaData = new Bundle(); metaData.putString("com.android.settings.category", category); metaData.putInt(META_DATA_PREFERENCE_ICON, 314159); @@ -388,6 +412,9 @@ public class TileUtilsTest { } else if (title != null) { metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE, title); } + if (profile != null) { + metaData.putString(META_DATA_KEY_PROFILE, profile); + } return metaData; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 6fd6f711927d..7c198c88d5b6 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -761,8 +761,8 @@ class SettingsProtoDumpUtil { Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES); dumpSetting(s, p, - Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, - GlobalSettingsProto.Gpu.ANGLE_WHITELIST); + Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, + GlobalSettingsProto.Gpu.ANGLE_ALLOWLIST); dumpSetting(s, p, Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, GlobalSettingsProto.Gpu.SHOW_ANGLE_IN_USE_DIALOG); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 3af8f044f180..bc1c3f908097 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -502,7 +502,7 @@ public class SettingsBackupTest { Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, - Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, + Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, Settings.Global.GAME_DRIVER_ALL_APPS, Settings.Global.GAME_DRIVER_OPT_IN_APPS, Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS, diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 72f623e5fcab..01abc77aea38 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -531,6 +531,8 @@ <!-- Defines the blacklist for system icons. That is to say, the icons in the status bar that are part of the blacklist are never displayed. Each item in the blacklist must be a string defined in core/res/res/config.xml to properly blacklist the icon. + + TODO: See if we can rename this config variable. --> <string-array name="config_statusBarIconBlackList" translatable="false"> <item>@*android:string/status_bar_rotate</item> diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index a46ab3a9e35b..5235a451d021 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -234,7 +234,7 @@ public class BatteryMeterView extends LinearLayout implements } Dependency.get(TunerService.class) - .addTunable(this, StatusBarIconController.ICON_BLACKLIST); + .addTunable(this, StatusBarIconController.ICON_HIDE_LIST); mIsSubscribedForTunerUpdates = true; } @@ -287,8 +287,8 @@ public class BatteryMeterView extends LinearLayout implements @Override public void onTuningChanged(String key, String newValue) { - if (StatusBarIconController.ICON_BLACKLIST.equals(key)) { - ArraySet<String> icons = StatusBarIconController.getIconBlacklist( + if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) { + ArraySet<String> icons = StatusBarIconController.getIconHideList( getContext(), newValue); setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java index fbcd6ba0ff47..42dde4064a97 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java @@ -205,7 +205,8 @@ public class ScreenshotNotificationsController { mPublicNotificationBuilder .setContentTitle(mResources.getString(R.string.screenshot_saved_title)) .setContentText(mResources.getString(R.string.screenshot_saved_text)) - .setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0)) + .setContentIntent(PendingIntent + .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE)) .setWhen(now) .setAutoCancel(true) .setColor(mContext.getColor( @@ -213,7 +214,8 @@ public class ScreenshotNotificationsController { mNotificationBuilder .setContentTitle(mResources.getString(R.string.screenshot_saved_title)) .setContentText(mResources.getString(R.string.screenshot_saved_text)) - .setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0)) + .setContentIntent(PendingIntent + .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE)) .setWhen(now) .setAutoCancel(true) .setColor(mContext.getColor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 9abc66056452..d04389d6cbe8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -36,6 +36,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; +import com.android.systemui.bubbles.BubbleController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationListener; @@ -189,6 +190,8 @@ public class NotificationEntryManager implements } } + private final Lazy<BubbleController> mBubbleControllerLazy; + /** * Injected constructor. See {@link NotificationsModule}. */ @@ -201,6 +204,7 @@ public class NotificationEntryManager implements Lazy<NotificationRowBinder> notificationRowBinderLazy, Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, LeakDetector leakDetector, + Lazy<BubbleController> bubbleController, ForegroundServiceDismissalFeatureController fgsFeatureController) { mLogger = logger; mGroupManager = groupManager; @@ -211,6 +215,7 @@ public class NotificationEntryManager implements mRemoteInputManagerLazy = notificationRemoteInputManagerLazy; mLeakDetector = leakDetector; mFgsFeatureController = fgsFeatureController; + mBubbleControllerLazy = bubbleController; } /** Once called, the NEM will start processing notification events from system server. */ @@ -920,8 +925,20 @@ public class NotificationEntryManager implements /** * @return {@code true} if there is at least one notification that should be visible right now */ - public boolean hasActiveNotifications() { - return mReadOnlyNotifications.size() != 0; + public boolean hasVisibleNotifications() { + if (mReadOnlyNotifications.size() == 0) { + return false; + } + + // Filter out suppressed notifications, which are active notifications backing a bubble + // but are not present in the shade + for (NotificationEntry e : mSortedAndFiltered) { + if (!mBubbleControllerLazy.get().isBubbleNotificationSuppressedFromShade(e)) { + return true; + } + } + + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index df1de63b65a0..c37e93d4fcc5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -87,6 +87,7 @@ public interface NotificationsModule { Lazy<NotificationRowBinder> notificationRowBinderLazy, Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, LeakDetector leakDetector, + Lazy<BubbleController> bubbleController, ForegroundServiceDismissalFeatureController fgsFeatureController) { return new NotificationEntryManager( logger, @@ -97,6 +98,7 @@ public interface NotificationsModule { notificationRowBinderLazy, notificationRemoteInputManagerLazy, leakDetector, + bubbleController, fgsFeatureController); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index b9d31a93f408..a4a58194a46b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -6580,7 +6580,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { return !mNotifPipeline.getShadeList().isEmpty(); } else { - return mEntryManager.hasActiveNotifications(); + return mEntryManager.hasVisibleNotifications(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java index 8e192c5bf17d..c758670fc457 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java @@ -97,7 +97,7 @@ public class LightsOutNotifController { } private boolean hasActiveNotifications() { - return mEntryManager.hasActiveNotifications(); + return mEntryManager.hasVisibleNotifications(); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 375af6b099c2..64202d221b2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -3003,7 +3003,7 @@ public class NotificationPanelViewController extends PanelViewController { private void updateShowEmptyShadeView() { boolean showEmptyShadeView = - mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications(); + mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasVisibleNotifications(); showEmptyShadeView(showEmptyShadeView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 93df14f18fda..0364186a83a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -83,15 +83,16 @@ public interface StatusBarIconController { public void removeIcon(String slot, int tag); public void removeAllIconsForSlot(String slot); - public static final String ICON_BLACKLIST = "icon_blacklist"; + // TODO: See if we can rename this tunable name. + String ICON_HIDE_LIST = "icon_blacklist"; - /** Reads the default blacklist from config value unless blacklistStr is provided. */ - static ArraySet<String> getIconBlacklist(Context context, String blackListStr) { + /** Reads the default hide list from config value unless hideListStr is provided. */ + static ArraySet<String> getIconHideList(Context context, String hideListStr) { ArraySet<String> ret = new ArraySet<>(); - String[] blacklist = blackListStr == null + String[] hideList = hideListStr == null ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList) - : blackListStr.split(","); - for (String slot : blacklist) { + : hideListStr.split(","); + for (String slot : hideList) { if (!TextUtils.isEmpty(slot)) { ret.add(slot); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index d0e806769f14..21e1d319cffa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -59,7 +59,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu private static final String TAG = "StatusBarIconController"; private final ArrayList<IconManager> mIconGroups = new ArrayList<>(); - private final ArraySet<String> mIconBlacklist = new ArraySet<>(); + private final ArraySet<String> mIconHideList = new ArraySet<>(); // Points to light or dark context depending on the... context? private Context mContext; @@ -79,7 +79,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu loadDimens(); commandQueue.addCallback(this); - Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST); + Dependency.get(TunerService.class).addTunable(this, ICON_HIDE_LIST); } @Override @@ -89,12 +89,12 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu for (int i = 0; i < allSlots.size(); i++) { Slot slot = allSlots.get(i); List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder(); - boolean blocked = mIconBlacklist.contains(slot.getName()); + boolean hidden = mIconHideList.contains(slot.getName()); for (StatusBarIconHolder holder : holders) { int tag = holder.getTag(); int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag()); - group.onIconAdded(viewIndex, slot.getName(), blocked, holder); + group.onIconAdded(viewIndex, slot.getName(), hidden, holder); } } } @@ -107,11 +107,11 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu @Override public void onTuningChanged(String key, String newValue) { - if (!ICON_BLACKLIST.equals(key)) { + if (!ICON_HIDE_LIST.equals(key)) { return; } - mIconBlacklist.clear(); - mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(mContext, newValue)); + mIconHideList.clear(); + mIconHideList.addAll(StatusBarIconController.getIconHideList(mContext, newValue)); ArrayList<Slot> currentSlots = getSlots(); ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>(); @@ -142,9 +142,9 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu private void addSystemIcon(int index, StatusBarIconHolder holder) { String slot = getSlotName(index); int viewIndex = getViewIndex(index, holder.getTag()); - boolean blocked = mIconBlacklist.contains(slot); + boolean hidden = mIconHideList.contains(slot); - mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder)); + mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 45f0c49a4fd4..8e933a281b3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -330,7 +330,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } public boolean hasActiveNotifications() { - return mEntryManager.hasActiveNotifications(); + return mEntryManager.hasVisibleNotifications(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index 690d57345db6..7eefaf28517e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -52,12 +52,12 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba private final SecurityController mSecurityController; private final Handler mHandler = Handler.getMain(); - private boolean mBlockAirplane; - private boolean mBlockMobile; - private boolean mBlockWifi; - private boolean mBlockEthernet; + private boolean mHideAirplane; + private boolean mHideMobile; + private boolean mHideWifi; + private boolean mHideEthernet; private boolean mActivityEnabled; - private boolean mForceBlockWifi; + private boolean mForceHideWifi; // Track as little state as possible, and only for padding purposes private boolean mIsAirplaneMode = false; @@ -80,7 +80,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba mNetworkController = Dependency.get(NetworkController.class); mSecurityController = Dependency.get(SecurityController.class); - Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST); + Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST); mNetworkController.addCallback(this); mSecurityController.addCallback(this); } @@ -114,21 +114,21 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba @Override public void onTuningChanged(String key, String newValue) { - if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { + if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) { return; } - ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(mContext, newValue); - boolean blockAirplane = blockList.contains(mSlotAirplane); - boolean blockMobile = blockList.contains(mSlotMobile); - boolean blockWifi = blockList.contains(mSlotWifi); - boolean blockEthernet = blockList.contains(mSlotEthernet); - - if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile - || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) { - mBlockAirplane = blockAirplane; - mBlockMobile = blockMobile; - mBlockEthernet = blockEthernet; - mBlockWifi = blockWifi || mForceBlockWifi; + ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue); + boolean hideAirplane = hideList.contains(mSlotAirplane); + boolean hideMobile = hideList.contains(mSlotMobile); + boolean hideWifi = hideList.contains(mSlotWifi); + boolean hideEthernet = hideList.contains(mSlotEthernet); + + if (hideAirplane != mHideAirplane || hideMobile != mHideMobile + || hideEthernet != mHideEthernet || hideWifi != mHideWifi) { + mHideAirplane = hideAirplane; + mHideMobile = hideMobile; + mHideEthernet = hideEthernet; + mHideWifi = hideWifi || mForceHideWifi; // Re-register to get new callbacks. mNetworkController.removeCallback(this); mNetworkController.addCallback(this); @@ -140,7 +140,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba boolean activityIn, boolean activityOut, String description, boolean isTransient, String statusLabel) { - boolean visible = statusIcon.visible && !mBlockWifi; + boolean visible = statusIcon.visible && !mHideWifi; boolean in = activityIn && mActivityEnabled && visible; boolean out = activityOut && mActivityEnabled && visible; @@ -189,7 +189,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba // Visibility of the data type indicator changed boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0); - state.visible = statusIcon.visible && !mBlockMobile; + state.visible = statusIcon.visible && !mHideMobile; state.strengthId = statusIcon.icon; state.typeId = statusType; state.contentDescription = statusIcon.contentDescription; @@ -270,7 +270,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba @Override public void setEthernetIndicators(IconState state) { - boolean visible = state.visible && !mBlockEthernet; + boolean visible = state.visible && !mHideEthernet; int resId = state.icon; String description = state.contentDescription; @@ -284,7 +284,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba @Override public void setIsAirplaneMode(IconState icon) { - mIsAirplaneMode = icon.visible && !mBlockAirplane; + mIsAirplaneMode = icon.visible && !mHideAirplane; int resId = icon.icon; String description = icon.contentDescription; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index c4c0d3fdff42..d43dd232e7c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -190,7 +190,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, Dependency.get(Dependency.TIME_TICK_HANDLER), UserHandle.ALL); Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS, - StatusBarIconController.ICON_BLACKLIST); + StatusBarIconController.ICON_HIDE_LIST); mCommandQueue.addCallback(this); if (mShowDark) { Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this); @@ -296,8 +296,8 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C if (CLOCK_SECONDS.equals(key)) { mShowSeconds = TunerService.parseIntegerSwitch(newValue, false); updateShowSeconds(); - } else { - setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(getContext(), newValue) + } else if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) { + setClockVisibleByUser(!StatusBarIconController.getIconHideList(getContext(), newValue) .contains("clock")); updateClockVisibility(); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java index 66372c311325..b71aafdf6b96 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java @@ -38,7 +38,7 @@ public class BatteryPreference extends DropDownPreference implements TunerServic private final String mBattery; private boolean mBatteryEnabled; private boolean mHasPercentage; - private ArraySet<String> mBlacklist; + private ArraySet<String> mHideList; private boolean mHasSetValue; public BatteryPreference(Context context, AttributeSet attrs) { @@ -52,7 +52,7 @@ public class BatteryPreference extends DropDownPreference implements TunerServic super.onAttached(); mHasPercentage = Settings.System.getInt(getContext().getContentResolver(), SHOW_BATTERY_PERCENT, 0) != 0; - Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST); + Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST); } @Override @@ -63,9 +63,9 @@ public class BatteryPreference extends DropDownPreference implements TunerServic @Override public void onTuningChanged(String key, String newValue) { - if (StatusBarIconController.ICON_BLACKLIST.equals(key)) { - mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue); - mBatteryEnabled = !mBlacklist.contains(mBattery); + if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) { + mHideList = StatusBarIconController.getIconHideList(getContext(), newValue); + mBatteryEnabled = !mHideList.contains(mBattery); } if (!mHasSetValue) { // Because of the complicated tri-state it can end up looping and setting state back to @@ -88,12 +88,12 @@ public class BatteryPreference extends DropDownPreference implements TunerServic MetricsLogger.action(getContext(), MetricsEvent.TUNER_BATTERY_PERCENTAGE, v); Settings.System.putInt(getContext().getContentResolver(), SHOW_BATTERY_PERCENT, v ? 1 : 0); if (DISABLED.equals(value)) { - mBlacklist.add(mBattery); + mHideList.add(mBattery); } else { - mBlacklist.remove(mBattery); + mHideList.remove(mBattery); } - Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_BLACKLIST, - TextUtils.join(",", mBlacklist)); + Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_HIDE_LIST, + TextUtils.join(",", mHideList)); return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java index f7d0c9fb9d86..c92d7bbfe0b7 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java @@ -33,7 +33,7 @@ public class ClockPreference extends DropDownPreference implements TunerService. private final String mClock; private boolean mClockEnabled; private boolean mHasSeconds; - private ArraySet<String> mBlacklist; + private ArraySet<String> mHideList; private boolean mHasSetValue; private boolean mReceivedSeconds; private boolean mReceivedClock; @@ -47,7 +47,7 @@ public class ClockPreference extends DropDownPreference implements TunerService. @Override public void onAttached() { super.onAttached(); - Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST, + Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST, Clock.CLOCK_SECONDS); } @@ -59,10 +59,10 @@ public class ClockPreference extends DropDownPreference implements TunerService. @Override public void onTuningChanged(String key, String newValue) { - if (StatusBarIconController.ICON_BLACKLIST.equals(key)) { + if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) { mReceivedClock = true; - mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue); - mClockEnabled = !mBlacklist.contains(mClock); + mHideList = StatusBarIconController.getIconHideList(getContext(), newValue); + mClockEnabled = !mHideList.contains(mClock); } else if (Clock.CLOCK_SECONDS.equals(key)) { mReceivedSeconds = true; mHasSeconds = newValue != null && Integer.parseInt(newValue) != 0; @@ -87,12 +87,12 @@ public class ClockPreference extends DropDownPreference implements TunerService. Dependency.get(TunerService.class).setValue(Clock.CLOCK_SECONDS, SECONDS.equals(value) ? 1 : 0); if (DISABLED.equals(value)) { - mBlacklist.add(mClock); + mHideList.add(mClock); } else { - mBlacklist.remove(mClock); + mHideList.remove(mClock); } - Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_BLACKLIST, - TextUtils.join(",", mBlacklist)); + Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_HIDE_LIST, + TextUtils.join(",", mHideList)); return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java index de8ccfa848e3..cc0050b64d60 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java @@ -34,7 +34,7 @@ import java.util.Set; public class StatusBarSwitch extends SwitchPreference implements Tunable { - private Set<String> mBlacklist; + private Set<String> mHideList; public StatusBarSwitch(Context context, AttributeSet attrs) { super(context, attrs); @@ -43,7 +43,7 @@ public class StatusBarSwitch extends SwitchPreference implements Tunable { @Override public void onAttached() { super.onAttached(); - Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST); + Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST); } @Override @@ -54,35 +54,35 @@ public class StatusBarSwitch extends SwitchPreference implements Tunable { @Override public void onTuningChanged(String key, String newValue) { - if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { + if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) { return; } - mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue); - setChecked(!mBlacklist.contains(getKey())); + mHideList = StatusBarIconController.getIconHideList(getContext(), newValue); + setChecked(!mHideList.contains(getKey())); } @Override protected boolean persistBoolean(boolean value) { if (!value) { - // If not enabled add to blacklist. - if (!mBlacklist.contains(getKey())) { + // If not enabled add to hideList. + if (!mHideList.contains(getKey())) { MetricsLogger.action(getContext(), MetricsEvent.TUNER_STATUS_BAR_DISABLE, getKey()); - mBlacklist.add(getKey()); - setList(mBlacklist); + mHideList.add(getKey()); + setList(mHideList); } } else { - if (mBlacklist.remove(getKey())) { + if (mHideList.remove(getKey())) { MetricsLogger.action(getContext(), MetricsEvent.TUNER_STATUS_BAR_ENABLE, getKey()); - setList(mBlacklist); + setList(mHideList); } } return true; } - private void setList(Set<String> blacklist) { + private void setList(Set<String> hideList) { ContentResolver contentResolver = getContext().getContentResolver(); - Settings.Secure.putStringForUser(contentResolver, StatusBarIconController.ICON_BLACKLIST, - TextUtils.join(",", blacklist), ActivityManager.getCurrentUser()); + Settings.Secure.putStringForUser(contentResolver, StatusBarIconController.ICON_HIDE_LIST, + TextUtils.join(",", hideList), ActivityManager.getCurrentUser()); } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 9ad2aa257aa0..644f7582f146 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -60,7 +60,7 @@ public class TunerServiceImpl extends TunerService { // Things that use the tunable infrastructure but are now real user settings and // shouldn't be reset with tuner settings. - private static final String[] RESET_BLACKLIST = new String[] { + private static final String[] RESET_EXCEPTION_LIST = new String[] { QSTileHost.TILES_SETTING, Settings.Secure.DOZE_ALWAYS_ON, Settings.Secure.MEDIA_CONTROLS_RESUME @@ -116,17 +116,17 @@ public class TunerServiceImpl extends TunerService { private void upgradeTuner(int oldVersion, int newVersion, Handler mainHandler) { if (oldVersion < 1) { - String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST); - if (blacklistStr != null) { - ArraySet<String> iconBlacklist = - StatusBarIconController.getIconBlacklist(mContext, blacklistStr); + String hideListStr = getValue(StatusBarIconController.ICON_HIDE_LIST); + if (hideListStr != null) { + ArraySet<String> iconHideList = + StatusBarIconController.getIconHideList(mContext, hideListStr); - iconBlacklist.add("rotate"); - iconBlacklist.add("headset"); + iconHideList.add("rotate"); + iconHideList.add("headset"); Settings.Secure.putStringForUser(mContentResolver, - StatusBarIconController.ICON_BLACKLIST, - TextUtils.join(",", iconBlacklist), mCurrentUser); + StatusBarIconController.ICON_HIDE_LIST, + TextUtils.join(",", iconHideList), mCurrentUser); } } if (oldVersion < 2) { @@ -251,7 +251,7 @@ public class TunerServiceImpl extends TunerService { mContext.sendBroadcast(intent); for (String key : mTunableLookup.keySet()) { - if (ArrayUtils.contains(RESET_BLACKLIST, key)) { + if (ArrayUtils.contains(RESET_EXCEPTION_LIST, key)) { continue; } Settings.Secure.putStringForUser(mContentResolver, key, null, user); diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java index b5f98ad47c09..89297fd83bb0 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java @@ -54,7 +54,7 @@ public class UsbAccessoryUriActivity extends AlertActivity String uriString = intent.getStringExtra("uri"); mUri = (uriString == null ? null : Uri.parse(uriString)); - // sanity check before displaying dialog + // Exception check before displaying dialog if (mUri == null) { Log.e(TAG, "could not parse Uri " + uriString); finish(); diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java index b25df5f9c07f..5e7280840bb9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/LeakReporter.java @@ -104,9 +104,13 @@ public class LeakReporter { .setContentText(String.format( "SystemUI has detected %d leaked objects. Tap to send", garbageCount)) .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) - .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, + .setContentIntent(PendingIntent.getActivityAsUser( + mContext, + 0, getIntent(hprofFile, dumpFile), - PendingIntent.FLAG_UPDATE_CURRENT, null, UserHandle.CURRENT)); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, + null, + UserHandle.CURRENT)); notiMan.notify(TAG, 0, builder.build()); } catch (IOException e) { Log.e(TAG, "Couldn't dump heap for leak", e); diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index 9fa03df4229a..06806d0e6ab6 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -338,7 +338,7 @@ public class ProximitySensor implements ThresholdSensor { @Override public void run() { unregister(); - mSensor.alertListeners(); + onProximityEvent(null); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java index 548da8e1f2aa..35620329467b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java @@ -384,7 +384,7 @@ public class PowerUITest extends SysuiTestCase { mPowerUI.mSevereWarningShownThisChargeCycle = false; BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); - // sanity check to make sure we can show for a valid config + // readiness check to make sure we can show for a valid config state.mBatteryLevel = 10; state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); @@ -449,7 +449,7 @@ public class PowerUITest extends SysuiTestCase { mPowerUI.mSevereWarningShownThisChargeCycle = false; BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); - // sanity check to make sure we can show for a valid config + // readiness check to make sure we can show for a valid config state.mBatteryLevel = 1; state.mTimeRemainingMillis = Duration.ofMinutes(1).toMillis(); boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); @@ -572,7 +572,7 @@ public class PowerUITest extends SysuiTestCase { state.mIsHybrid = false; BatteryStateSnapshot lastState = state.get(); - // sanity check to make sure we can show for a valid config + // readiness check to make sure we can show for a valid config state.mBatteryLevel = 10; state.mBucket = -1; boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index a5a5f81bdffe..90423c18216a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -61,6 +61,7 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bubbles.BubbleController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationMediaManager; @@ -98,6 +99,8 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import dagger.Lazy; + /** * Unit tests for {@link NotificationEntryManager}. This test will not test any interactions with * inflation. Instead, for functional inflation tests, see @@ -126,6 +129,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private LeakDetector mLeakDetector; @Mock private NotificationMediaManager mNotificationMediaManager; @Mock private NotificationRowBinder mNotificationRowBinder; + @Mock private Lazy<BubbleController> mBubbleControllerLazy; private int mId; private NotificationEntry mEntry; @@ -200,6 +204,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { () -> mNotificationRowBinder, () -> mRemoteInputManager, mLeakDetector, + mBubbleControllerLazy, mock(ForegroundServiceDismissalFeatureController.class) ); mEntryManager.setUpWithPresenter(mPresenter); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index bedbec6aadce..787b7b7e7b26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bubbles.BubbleController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; @@ -95,6 +96,8 @@ import org.mockito.stubbing.Answer; import java.util.concurrent.CountDownLatch; +import dagger.Lazy; + /** * Functional tests for notification inflation from {@link NotificationEntryManager}. */ @@ -136,6 +139,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock private NotificationRowComponent.Builder mNotificationRowComponentBuilder; @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; + @Mock private Lazy<BubbleController> mBubbleControllerLazy; + private StatusBarNotification mSbn; private NotificationListenerService.RankingMap mRankingMap; private NotificationEntryManager mEntryManager; @@ -183,6 +188,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { () -> mRowBinder, () -> mRemoteInputManager, mLeakDetector, + mBubbleControllerLazy, mock(ForegroundServiceDismissalFeatureController.class) ); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index b286f9486e13..2ae4caeca963 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -52,6 +52,7 @@ import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bubbles.BubbleController; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -109,6 +110,8 @@ import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.List; +import dagger.Lazy; + /** * Tests for {@link NotificationStackScrollLayout}. */ @@ -140,6 +143,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationSection mNotificationSection; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private FeatureFlags mFeatureFlags; + @Mock private Lazy<BubbleController> mBubbleControllerLazy; private UserChangedListener mUserChangedListener; private NotificationEntryManager mEntryManager; private int mOriginalInterruptionModelSetting; @@ -190,6 +194,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { () -> mock(NotificationRowBinder.class), () -> mRemoteInputManager, mock(LeakDetector.class), + mBubbleControllerLazy, mock(ForegroundServiceDismissalFeatureController.class) ); mEntryManager.setUpWithPresenter(mock(NotificationPresenter.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java index dbb451277535..9d81a90e7af1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java @@ -130,7 +130,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Test public void testLightsOut_withNotifs_onSystemBarAppearanceChanged() { // GIVEN active visible notifications - when(mEntryManager.hasActiveNotifications()).thenReturn(true); + when(mEntryManager.hasVisibleNotifications()).thenReturn(true); // WHEN lights out mCallbacks.onSystemBarAppearanceChanged( @@ -147,7 +147,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Test public void testLightsOut_withoutNotifs_onSystemBarAppearanceChanged() { // GIVEN no active visible notifications - when(mEntryManager.hasActiveNotifications()).thenReturn(false); + when(mEntryManager.hasVisibleNotifications()).thenReturn(false); // WHEN lights out mCallbacks.onSystemBarAppearanceChanged( @@ -164,7 +164,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Test public void testLightsOn_afterLightsOut_onSystemBarAppearanceChanged() { // GIVEN active visible notifications - when(mEntryManager.hasActiveNotifications()).thenReturn(true); + when(mEntryManager.hasVisibleNotifications()).thenReturn(true); // WHEN lights on mCallbacks.onSystemBarAppearanceChanged( @@ -181,13 +181,13 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Test public void testEntryAdded() { // GIVEN no visible notifications and lights out - when(mEntryManager.hasActiveNotifications()).thenReturn(false); + when(mEntryManager.hasVisibleNotifications()).thenReturn(false); mLightsOutNotifController.mAppearance = LIGHTS_OUT; mLightsOutNotifController.updateLightsOutView(); assertIsShowingDot(false); // WHEN an active notification is added - when(mEntryManager.hasActiveNotifications()).thenReturn(true); + when(mEntryManager.hasVisibleNotifications()).thenReturn(true); assertTrue(mLightsOutNotifController.shouldShowDot()); mEntryListener.onNotificationAdded(mock(NotificationEntry.class)); @@ -198,13 +198,13 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Test public void testEntryRemoved() { // GIVEN a visible notification and lights out - when(mEntryManager.hasActiveNotifications()).thenReturn(true); + when(mEntryManager.hasVisibleNotifications()).thenReturn(true); mLightsOutNotifController.mAppearance = LIGHTS_OUT; mLightsOutNotifController.updateLightsOutView(); assertIsShowingDot(true); // WHEN all active notifications are removed - when(mEntryManager.hasActiveNotifications()).thenReturn(false); + when(mEntryManager.hasVisibleNotifications()).thenReturn(false); assertFalse(mLightsOutNotifController.shouldShowDot()); mEntryListener.onEntryRemoved( mock(NotificationEntry.class), null, false, REASON_CANCEL_ALL); @@ -216,13 +216,13 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Test public void testEntryUpdated() { // GIVEN no visible notifications and lights out - when(mEntryManager.hasActiveNotifications()).thenReturn(false); + when(mEntryManager.hasVisibleNotifications()).thenReturn(false); mLightsOutNotifController.mAppearance = LIGHTS_OUT; mLightsOutNotifController.updateLightsOutView(); assertIsShowingDot(false); // WHEN an active notification is added - when(mEntryManager.hasActiveNotifications()).thenReturn(true); + when(mEntryManager.hasVisibleNotifications()).thenReturn(true); assertTrue(mLightsOutNotifController.shouldShowDot()); mEntryListener.onPostEntryUpdated(mock(NotificationEntry.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java index 8ba11dae2b5c..c5a197eef2d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java @@ -16,6 +16,7 @@ package com.android.systemui.util.sensors; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -80,6 +81,8 @@ public class ProximityCheckTest extends SysuiTestCase { mFakeExecutor.runAllReady(); assertFalse(mFakeProximitySensor.isRegistered()); + assertEquals(1, mTestableCallback.mNumCalls); + assertNull(mTestableCallback.mLastResult); } @Test @@ -110,9 +113,12 @@ public class ProximityCheckTest extends SysuiTestCase { private static class TestableCallback implements Consumer<Boolean> { Boolean mLastResult; + int mNumCalls = 0; + @Override public void accept(Boolean result) { mLastResult = result; + mNumCalls++; } } } diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index 9b9dcde910e7..5f8d2997197f 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -73,6 +73,9 @@ <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. --> <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool> + <!-- Use legacy wifi p2p dedicated address instead of randomize address. --> + <bool translatable="false" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip">false</bool> + <!-- Dhcp range (min, max) to use for tethering purposes --> <string-array translatable="false" name="config_tether_dhcp_range"> </string-array> diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml index 6a33d55cb0de..0ee7a992ee20 100644 --- a/packages/Tethering/res/values/overlayable.xml +++ b/packages/Tethering/res/values/overlayable.xml @@ -30,6 +30,7 @@ --> <item type="bool" name="config_tether_enable_bpf_offload"/> <item type="bool" name="config_tether_enable_legacy_dhcp_server"/> + <item type="bool" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip"/> <item type="integer" name="config_tether_offload_poll_interval"/> <item type="array" name="config_tether_upstream_types"/> <item type="bool" name="config_tether_upstream_automatic"/> diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java index aa58a4b6a320..fd9e36080c80 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java @@ -15,6 +15,8 @@ */ package com.android.networkstack.tethering; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; + import static java.util.Arrays.asList; import android.content.Context; @@ -58,6 +60,7 @@ public class PrivateAddressCoordinator { private static final int BYTE_MASK = 0xff; // reserved for bluetooth tethering. private static final int BLUETOOTH_RESERVED = 44; + private static final int WIFI_P2P_RESERVED = 49; private static final byte DEFAULT_ID = (byte) 42; // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream @@ -71,15 +74,18 @@ public class PrivateAddressCoordinator { // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers. private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16"; + private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24"; private final IpPrefix mTetheringPrefix; private final ConnectivityManager mConnectivityMgr; + private final TetheringConfiguration mConfig; - public PrivateAddressCoordinator(Context context) { + public PrivateAddressCoordinator(Context context, TetheringConfiguration config) { mDownstreams = new ArraySet<>(); mUpstreamPrefixMap = new ArrayMap<>(); mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX); mConnectivityMgr = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE); + mConfig = config; } /** @@ -141,12 +147,21 @@ public class PrivateAddressCoordinator { mUpstreamPrefixMap.removeAll(toBeRemoved); } + private boolean isReservedSubnet(final int subnet) { + return subnet == BLUETOOTH_RESERVED || subnet == WIFI_P2P_RESERVED; + } + /** * Pick a random available address and mark its prefix as in use for the provided IpServer, * returns null if there is no available address. */ @Nullable public LinkAddress requestDownstreamAddress(final IpServer ipServer) { + if (mConfig.shouldEnableWifiP2pDedicatedIp() + && ipServer.interfaceType() == TETHERING_WIFI_P2P) { + return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS); + } + // Address would be 192.168.[subAddress]/24. final byte[] bytes = mTetheringPrefix.getRawAddress(); final int subAddress = getRandomSubAddr(); @@ -154,7 +169,7 @@ public class PrivateAddressCoordinator { bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff); for (int i = 0; i < MAX_UBYTE; i++) { final int newSubNet = (subNet + i) & BYTE_MASK; - if (newSubNet == BLUETOOTH_RESERVED) continue; + if (isReservedSubnet(newSubNet)) continue; bytes[2] = (byte) newSubNet; final InetAddress addr; diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java index cfc657587332..7dd5290ee83b 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -320,10 +320,13 @@ public class Tethering { mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); mNetdCallback = new NetdCallback(); - mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext); // Load tethering configuration. updateConfiguration(); + // It is OK for the configuration to be passed to the PrivateAddressCoordinator at + // construction time because the only part of the configuration it uses is + // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that. + mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig); // Must be initialized after tethering configuration is loaded because BpfCoordinator // constructor needs to use the configuration. diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index e1771a561370..5783805861a3 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -84,6 +84,9 @@ public class TetheringConfiguration { public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = "tether_enable_legacy_dhcp_server"; + public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP = + "use_legacy_wifi_p2p_dedicated_ip"; + /** * Default value that used to periodic polls tether offload stats from tethering offload HAL * to make the data warnings work. @@ -113,6 +116,7 @@ public class TetheringConfiguration { private final int mOffloadPollInterval; // TODO: Add to TetheringConfigurationParcel if required. private final boolean mEnableBpfOffload; + private final boolean mEnableWifiP2pDedicatedIp; public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); @@ -156,6 +160,10 @@ public class TetheringConfiguration { R.integer.config_tether_offload_poll_interval, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); + mEnableWifiP2pDedicatedIp = getResourceBoolean(res, + R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip, + false /* defaultValue */); + configLog.log(toString()); } @@ -199,6 +207,11 @@ public class TetheringConfiguration { return !TextUtils.isEmpty(provisioningAppNoUi); } + /** Check whether dedicated wifi p2p address is enabled. */ + public boolean shouldEnableWifiP2pDedicatedIp() { + return mEnableWifiP2pDedicatedIp; + } + /** Does the dumping.*/ public void dump(PrintWriter pw) { pw.print("activeDataSubId: "); @@ -233,6 +246,9 @@ public class TetheringConfiguration { pw.print("enableLegacyDhcpServer: "); pw.println(enableLegacyDhcpServer); + + pw.print("enableWifiP2pDedicatedIp: "); + pw.println(mEnableWifiP2pDedicatedIp); } /** Returns the string representation of this object.*/ diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java index 2c0df6fc6327..8e93c2e447b3 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java @@ -15,6 +15,11 @@ */ package com.android.networkstack.tethering; +import static android.net.TetheringManager.TETHERING_ETHERNET; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.mockito.Mockito.never; @@ -54,22 +59,34 @@ public final class PrivateAddressCoordinatorTest { @Mock private IpServer mHotspotIpServer; @Mock private IpServer mUsbIpServer; @Mock private IpServer mEthernetIpServer; + @Mock private IpServer mWifiP2pIpServer; @Mock private Context mContext; @Mock private ConnectivityManager mConnectivityMgr; + @Mock private TetheringConfiguration mConfig; private PrivateAddressCoordinator mPrivateAddressCoordinator; private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); + private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24"); private final Network mWifiNetwork = new Network(1); private final Network mMobileNetwork = new Network(2); private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork}; + private void setUpIpServers() throws Exception { + when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB); + when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET); + when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI); + when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P); + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr); when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks); - mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext)); + when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false); + setUpIpServers(); + mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig)); } @Test @@ -256,4 +273,38 @@ public final class PrivateAddressCoordinatorTest { final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr); assertEquals(predefinedPrefix, ethPrefix); } + + private int getSubAddress(final byte... ipv4Address) { + assertEquals(4, ipv4Address.length); + + int subnet = Byte.toUnsignedInt(ipv4Address[2]); + return (subnet << 8) + ipv4Address[3]; + } + + private void assertReseveredWifiP2pPrefix() throws Exception { + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address); + final IpPrefix legacyWifiP2pPrefix = PrefixUtils.asIpPrefix(mLegacyWifiP2pAddress); + assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + } + + @Test + public void testEnableLegacyWifiP2PAddress() throws Exception { + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn( + getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress())); + // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix + // is resevered. + assertReseveredWifiP2pPrefix(); + + when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true); + assertReseveredWifiP2pPrefix(); + + // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address. + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mWifiP2pIpServer); + assertEquals(mLegacyWifiP2pAddress, address); + mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer); + } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index a9ac4e2851f3..dc0940cc0222 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -128,6 +128,8 @@ public class TetheringConfigurationTest { .thenReturn(new String[0]); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) + .thenReturn(false); initializeBpfOffloadConfiguration(true, null /* unset */); mHasTelephonyManager = true; @@ -413,4 +415,17 @@ public class TetheringConfigurationTest { R.string.config_mobile_hotspot_provision_response)).thenReturn( PROVISIONING_APP_RESPONSE); } + + @Test + public void testEnableLegacyWifiP2PAddress() throws Exception { + final TetheringConfiguration defaultCfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp()); + + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) + .thenReturn(true); + final TetheringConfiguration testCfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp()); + } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 02c08cc4cd39..ef62a991f323 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -220,6 +220,8 @@ import com.android.server.utils.PriorityDump; import com.google.android.collect.Lists; +import libcore.io.IoUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -7513,18 +7515,34 @@ public class ConnectivityService extends IConnectivityManager.Stub public void startNattKeepaliveWithFd(Network network, FileDescriptor fd, int resourceId, int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr, String dstAddr) { - mKeepaliveTracker.startNattKeepalive( - getNetworkAgentInfoForNetwork(network), fd, resourceId, - intervalSeconds, cb, - srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT); + try { + mKeepaliveTracker.startNattKeepalive( + getNetworkAgentInfoForNetwork(network), fd, resourceId, + intervalSeconds, cb, + srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT); + } finally { + // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks. + // startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately. + if (fd != null && Binder.getCallingPid() != Process.myPid()) { + IoUtils.closeQuietly(fd); + } + } } @Override public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds, ISocketKeepaliveCallback cb) { - enforceKeepalivePermission(); - mKeepaliveTracker.startTcpKeepalive( - getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb); + try { + enforceKeepalivePermission(); + mKeepaliveTracker.startTcpKeepalive( + getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb); + } finally { + // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks. + // startTcpKeepalive calls Os.dup(fd) before returning, so we can close immediately. + if (fd != null && Binder.getCallingPid() != Process.myPid()) { + IoUtils.closeQuietly(fd); + } + } } @Override diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 82abb988cb2c..354b29c367ec 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2480,7 +2480,7 @@ public class ActivityManagerService extends IActivityManager.Stub ? Collections.emptyList() : Arrays.asList(exemptions.split(",")); } - if (!ZYGOTE_PROCESS.setApiBlacklistExemptions(mExemptions)) { + if (!ZYGOTE_PROCESS.setApiDenylistExemptions(mExemptions)) { Slog.e(TAG, "Failed to set API blacklist exemptions!"); // leave mExemptionsStr as is, so we don't try to send the same list again. mExemptions = Collections.emptyList(); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index d0ca84fd38dc..2661dd62ce21 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -92,7 +92,7 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put( Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class); sGlobalSettingToTypeMap.put( - Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, String.class); + Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, String.class); sGlobalSettingToTypeMap.put( Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class); sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class); diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index f8774b1b0054..7202f0f401f9 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -171,8 +171,8 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse mAllApps.add(UserHandle.getAppId(uid)); final boolean isNetwork = hasPermission(CHANGE_NETWORK_STATE, uid); - final boolean hasRestrictedPermission = - hasRestrictedNetworkPermission(app.applicationInfo); + final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(uid) + || isCarryoverPackage(app.applicationInfo); if (isNetwork || hasRestrictedPermission) { Boolean permission = mApps.get(uid); @@ -200,7 +200,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse for (int i = 0; i < systemPermission.size(); i++) { ArraySet<String> perms = systemPermission.valueAt(i); int uid = systemPermission.keyAt(i); - int netdPermission = 0; + int netdPermission = PERMISSION_NONE; // Get the uids of native services that have UPDATE_DEVICE_STATS or INTERNET permission. if (perms != null) { netdPermission |= perms.contains(UPDATE_DEVICE_STATS) @@ -225,20 +225,21 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse } @VisibleForTesting - boolean hasRestrictedNetworkPermission(@Nullable final ApplicationInfo appInfo) { - if (appInfo == null) return false; - // TODO : remove this check in the future(b/162295056). All apps should just - // request the appropriate permission for their use case since android Q. - if ((appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo)) + // TODO : remove this check in the future(b/162295056). All apps should just request the + // appropriate permission for their use case since android Q. + boolean isCarryoverPackage(@Nullable final ApplicationInfo appInfo) { + if (appInfo == null) return false; + return (appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo)) // Backward compatibility for b/114245686, on devices that launched before Q daemons // and apps running as the system UID are exempted from this check. - || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q)) { - return true; - } + || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q); + } - return hasPermission(PERMISSION_MAINLINE_NETWORK_STACK, appInfo.uid) - || hasPermission(NETWORK_STACK, appInfo.uid) - || hasPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, appInfo.uid); + @VisibleForTesting + boolean hasRestrictedNetworkPermission(final int uid) { + return hasPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid) + || hasPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid) + || hasPermission(NETWORK_STACK, uid); } /** Returns whether the given uid has using background network permission. */ @@ -328,8 +329,8 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse try { final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS); final boolean isNetwork = hasPermission(CHANGE_NETWORK_STATE, uid); - final boolean hasRestrictedPermission = - hasRestrictedNetworkPermission(app.applicationInfo); + final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(uid) + || isCarryoverPackage(app.applicationInfo); if (isNetwork || hasRestrictedPermission) { currentPermission = hasRestrictedPermission; } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1f85d1046523..1c93d4eb599b 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -48,6 +48,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.ConnectivityManager; +import android.net.DnsResolver; import android.net.INetworkManagementEventObserver; import android.net.Ikev2VpnProfile; import android.net.IpPrefix; @@ -79,6 +80,7 @@ import android.net.ipsec.ike.IkeSessionParams; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.CancellationSignal; import android.os.FileUtils; import android.os.IBinder; import android.os.INetworkManagementService; @@ -123,6 +125,7 @@ import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.ArrayList; @@ -134,6 +137,8 @@ import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -190,6 +195,7 @@ public class Vpn { // automated reconnection private final Context mContext; + @VisibleForTesting final Dependencies mDeps; private final NetworkInfo mNetworkInfo; @VisibleForTesting protected String mPackage; private int mOwnerUID; @@ -252,17 +258,143 @@ public class Vpn { // Handle of the user initiating VPN. private final int mUserHandle; + interface RetryScheduler { + void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException; + } + + static class Dependencies { + public void startService(final String serviceName) { + SystemService.start(serviceName); + } + + public void stopService(final String serviceName) { + SystemService.stop(serviceName); + } + + public boolean isServiceRunning(final String serviceName) { + return SystemService.isRunning(serviceName); + } + + public boolean isServiceStopped(final String serviceName) { + return SystemService.isStopped(serviceName); + } + + public File getStateFile() { + return new File("/data/misc/vpn/state"); + } + + public void sendArgumentsToDaemon( + final String daemon, final LocalSocket socket, final String[] arguments, + final RetryScheduler retryScheduler) throws IOException, InterruptedException { + final LocalSocketAddress address = new LocalSocketAddress( + daemon, LocalSocketAddress.Namespace.RESERVED); + + // Wait for the socket to connect. + while (true) { + try { + socket.connect(address); + break; + } catch (Exception e) { + // ignore + } + retryScheduler.checkInterruptAndDelay(true /* sleepLonger */); + } + socket.setSoTimeout(500); + + final OutputStream out = socket.getOutputStream(); + for (String argument : arguments) { + byte[] bytes = argument.getBytes(StandardCharsets.UTF_8); + if (bytes.length >= 0xFFFF) { + throw new IllegalArgumentException("Argument is too large"); + } + out.write(bytes.length >> 8); + out.write(bytes.length); + out.write(bytes); + retryScheduler.checkInterruptAndDelay(false /* sleepLonger */); + } + out.write(0xFF); + out.write(0xFF); + + // Wait for End-of-File. + final InputStream in = socket.getInputStream(); + while (true) { + try { + if (in.read() == -1) { + break; + } + } catch (Exception e) { + // ignore + } + retryScheduler.checkInterruptAndDelay(true /* sleepLonger */); + } + } + + @NonNull + public InetAddress resolve(final String endpoint) + throws ExecutionException, InterruptedException { + try { + return InetAddress.parseNumericAddress(endpoint); + } catch (IllegalArgumentException e) { + // Endpoint is not numeric : fall through and resolve + } + + final CancellationSignal cancellationSignal = new CancellationSignal(); + try { + final DnsResolver resolver = DnsResolver.getInstance(); + final CompletableFuture<InetAddress> result = new CompletableFuture(); + final DnsResolver.Callback<List<InetAddress>> cb = + new DnsResolver.Callback<List<InetAddress>>() { + @Override + public void onAnswer(@NonNull final List<InetAddress> answer, + final int rcode) { + if (answer.size() > 0) { + result.complete(answer.get(0)); + } else { + result.completeExceptionally( + new UnknownHostException(endpoint)); + } + } + + @Override + public void onError(@Nullable final DnsResolver.DnsException error) { + // Unfortunately UnknownHostException doesn't accept a cause, so + // print a message here instead. Only show the summary, not the + // full stack trace. + Log.e(TAG, "Async dns resolver error : " + error); + result.completeExceptionally(new UnknownHostException(endpoint)); + } + }; + resolver.query(null /* network, null for default */, endpoint, + DnsResolver.FLAG_EMPTY, r -> r.run(), cancellationSignal, cb); + return result.get(); + } catch (final ExecutionException e) { + Log.e(TAG, "Cannot resolve VPN endpoint : " + endpoint + ".", e); + throw e; + } catch (final InterruptedException e) { + Log.e(TAG, "Legacy VPN was interrupted while resolving the endpoint", e); + cancellationSignal.cancel(); + throw e; + } + } + + public boolean checkInterfacePresent(final Vpn vpn, final String iface) { + return vpn.jniCheck(iface) == 0; + } + } + public Vpn(Looper looper, Context context, INetworkManagementService netService, @UserIdInt int userHandle, @NonNull KeyStore keyStore) { - this(looper, context, netService, userHandle, keyStore, + this(looper, context, new Dependencies(), netService, userHandle, keyStore, new SystemServices(context), new Ikev2SessionCreator()); } @VisibleForTesting - protected Vpn(Looper looper, Context context, INetworkManagementService netService, + protected Vpn(Looper looper, Context context, Dependencies deps, + INetworkManagementService netService, int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices, Ikev2SessionCreator ikev2SessionCreator) { mContext = context; + mDeps = deps; mNetd = netService; mUserHandle = userHandle; mLooper = looper; @@ -2129,7 +2261,8 @@ public class Vpn { } /** This class represents the common interface for all VPN runners. */ - private abstract class VpnRunner extends Thread { + @VisibleForTesting + abstract class VpnRunner extends Thread { protected VpnRunner(String name) { super(name); @@ -2638,7 +2771,7 @@ public class Vpn { } catch (InterruptedException e) { } for (String daemon : mDaemons) { - SystemService.stop(daemon); + mDeps.stopService(daemon); } } agentDisconnect(); @@ -2655,21 +2788,55 @@ public class Vpn { } } + private void checkAndFixupArguments(@NonNull final InetAddress endpointAddress) { + final String endpointAddressString = endpointAddress.getHostAddress(); + // Perform some safety checks before inserting the address in place. + // Position 0 in mDaemons and mArguments must be racoon, and position 1 must be mtpd. + if (!"racoon".equals(mDaemons[0]) || !"mtpd".equals(mDaemons[1])) { + throw new IllegalStateException("Unexpected daemons order"); + } + + // Respectively, the positions at which racoon and mtpd take the server address + // argument are 1 and 2. Not all types of VPN require both daemons however, and + // in that case the corresponding argument array is null. + if (mArguments[0] != null) { + if (!mProfile.server.equals(mArguments[0][1])) { + throw new IllegalStateException("Invalid server argument for racoon"); + } + mArguments[0][1] = endpointAddressString; + } + + if (mArguments[1] != null) { + if (!mProfile.server.equals(mArguments[1][2])) { + throw new IllegalStateException("Invalid server argument for mtpd"); + } + mArguments[1][2] = endpointAddressString; + } + } + private void bringup() { // Catch all exceptions so we can clean up a few things. try { + // resolve never returns null. If it does because of some bug, it will be + // caught by the catch() block below and cleanup gracefully. + final InetAddress endpointAddress = mDeps.resolve(mProfile.server); + + // Big hack : dynamically replace the address of the server in the arguments + // with the resolved address. + checkAndFixupArguments(endpointAddress); + // Initialize the timer. mBringupStartTime = SystemClock.elapsedRealtime(); // Wait for the daemons to stop. for (String daemon : mDaemons) { - while (!SystemService.isStopped(daemon)) { + while (!mDeps.isServiceStopped(daemon)) { checkInterruptAndDelay(true); } } // Clear the previous state. - File state = new File("/data/misc/vpn/state"); + final File state = mDeps.getStateFile(); state.delete(); if (state.exists()) { throw new IllegalStateException("Cannot delete the state"); @@ -2696,57 +2863,19 @@ public class Vpn { // Start the daemon. String daemon = mDaemons[i]; - SystemService.start(daemon); + mDeps.startService(daemon); // Wait for the daemon to start. - while (!SystemService.isRunning(daemon)) { + while (!mDeps.isServiceRunning(daemon)) { checkInterruptAndDelay(true); } // Create the control socket. mSockets[i] = new LocalSocket(); - LocalSocketAddress address = new LocalSocketAddress( - daemon, LocalSocketAddress.Namespace.RESERVED); - - // Wait for the socket to connect. - while (true) { - try { - mSockets[i].connect(address); - break; - } catch (Exception e) { - // ignore - } - checkInterruptAndDelay(true); - } - mSockets[i].setSoTimeout(500); - - // Send over the arguments. - OutputStream out = mSockets[i].getOutputStream(); - for (String argument : arguments) { - byte[] bytes = argument.getBytes(StandardCharsets.UTF_8); - if (bytes.length >= 0xFFFF) { - throw new IllegalArgumentException("Argument is too large"); - } - out.write(bytes.length >> 8); - out.write(bytes.length); - out.write(bytes); - checkInterruptAndDelay(false); - } - out.write(0xFF); - out.write(0xFF); - - // Wait for End-of-File. - InputStream in = mSockets[i].getInputStream(); - while (true) { - try { - if (in.read() == -1) { - break; - } - } catch (Exception e) { - // ignore - } - checkInterruptAndDelay(true); - } + + // Wait for the socket to connect and send over the arguments. + mDeps.sendArgumentsToDaemon(daemon, mSockets[i], arguments, + this::checkInterruptAndDelay); } // Wait for the daemons to create the new state. @@ -2754,7 +2883,7 @@ public class Vpn { // Check if a running daemon is dead. for (int i = 0; i < mDaemons.length; ++i) { String daemon = mDaemons[i]; - if (mArguments[i] != null && !SystemService.isRunning(daemon)) { + if (mArguments[i] != null && !mDeps.isServiceRunning(daemon)) { throw new IllegalStateException(daemon + " is dead"); } } @@ -2764,7 +2893,8 @@ public class Vpn { // Now we are connected. Read and parse the new state. String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1); if (parameters.length != 7) { - throw new IllegalStateException("Cannot parse the state"); + throw new IllegalStateException("Cannot parse the state: '" + + String.join("', '", parameters) + "'"); } // Set the interface and the addresses in the config. @@ -2793,20 +2923,15 @@ public class Vpn { } // Add a throw route for the VPN server endpoint, if one was specified. - String endpoint = parameters[5].isEmpty() ? mProfile.server : parameters[5]; - if (!endpoint.isEmpty()) { - try { - InetAddress addr = InetAddress.parseNumericAddress(endpoint); - if (addr instanceof Inet4Address) { - mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW)); - } else if (addr instanceof Inet6Address) { - mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW)); - } else { - Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint); - } - } catch (IllegalArgumentException e) { - Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e); - } + if (endpointAddress instanceof Inet4Address) { + mConfig.routes.add(new RouteInfo( + new IpPrefix(endpointAddress, 32), RTN_THROW)); + } else if (endpointAddress instanceof Inet6Address) { + mConfig.routes.add(new RouteInfo( + new IpPrefix(endpointAddress, 128), RTN_THROW)); + } else { + Log.e(TAG, "Unknown IP address family for VPN endpoint: " + + endpointAddress); } // Here is the last step and it must be done synchronously. @@ -2818,7 +2943,7 @@ public class Vpn { checkInterruptAndDelay(false); // Check if the interface is gone while we are waiting. - if (jniCheck(mConfig.interfaze) == 0) { + if (mDeps.checkInterfacePresent(Vpn.this, mConfig.interfaze)) { throw new IllegalStateException(mConfig.interfaze + " is gone"); } @@ -2849,7 +2974,7 @@ public class Vpn { while (true) { Thread.sleep(2000); for (int i = 0; i < mDaemons.length; i++) { - if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) { + if (mArguments[i] != null && mDeps.isServiceStopped(mDaemons[i])) { return; } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 1058000e0b68..24661d69a78e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2548,8 +2548,7 @@ public final class DisplayManagerService extends SystemService { public boolean requestPowerState(DisplayPowerRequest request, boolean waitForNegativeProximity) { synchronized (mSyncRoot) { - return mDisplayPowerController.requestPowerState(request, - waitForNegativeProximity); + return mDisplayPowerController.requestPowerState(request, waitForNegativeProximity); } } @@ -2677,6 +2676,10 @@ public final class DisplayManagerService extends SystemService { return getDisplayedContentSampleInternal(displayId, maxFrames, timestamp); } + @Override + public void ignoreProximitySensorUntilChanged() { + mDisplayPowerController.ignoreProximitySensorUntilChanged(); + } } class DesiredDisplayModeSpecsObserver diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9411c5629457..7c0f4197363b 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -117,6 +117,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_CONFIGURE_BRIGHTNESS = 5; private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6; private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7; + private static final int MSG_IGNORE_PROXIMITY = 8; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -263,6 +264,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // go to sleep by the user. While true, the screen remains off. private boolean mWaitingForNegativeProximity; + // True if the device should not take into account the proximity sensor + // until either the proximity sensor state changes, or there is no longer a + // request to listen to proximity sensor. + private boolean mIgnoreProximityUntilChanged; + // The actual proximity sensor threshold value. private float mProximityThreshold; @@ -760,8 +766,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mPowerRequest == null) { mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked); - mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked; - mPendingWaitForNegativeProximityLocked = false; + updatePendingProximityRequestsLocked(); mPendingRequestChangedLocked = false; mustInitialize = true; // Assume we're on and bright until told otherwise, since that's the state we turn @@ -770,8 +775,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else if (mPendingRequestChangedLocked) { previousPolicy = mPowerRequest.policy; mPowerRequest.copyFrom(mPendingRequestLocked); - mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; - mPendingWaitForNegativeProximityLocked = false; + updatePendingProximityRequestsLocked(); mPendingRequestChangedLocked = false; mDisplayReadyLocked = false; } else { @@ -822,9 +826,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Apply the proximity sensor. if (mProximitySensor != null) { if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) { + // At this point the policy says that the screen should be on, but we've been + // asked to listen to the prox sensor to adjust the display state, so lets make + // sure the sensor is on. setProximitySensorEnabled(true); if (!mScreenOffBecauseOfProximity - && mProximity == PROXIMITY_POSITIVE) { + && mProximity == PROXIMITY_POSITIVE + && !mIgnoreProximityUntilChanged) { + // Prox sensor already reporting "near" so we should turn off the screen. + // Also checked that we aren't currently set to ignore the proximity sensor + // temporarily. mScreenOffBecauseOfProximity = true; sendOnProximityPositiveWithWakelock(); } @@ -832,18 +843,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && mScreenOffBecauseOfProximity && mProximity == PROXIMITY_POSITIVE && state != Display.STATE_OFF) { + // The policy says that we should have the screen on, but it's off due to the prox + // and we've been asked to wait until the screen is far from the user to turn it + // back on. Let keep the prox sensor on so we can tell when it's far again. setProximitySensorEnabled(true); } else { + // We haven't been asked to use the prox sensor and we're not waiting on the screen + // to turn back on...so lets shut down the prox sensor. setProximitySensorEnabled(false); mWaitingForNegativeProximity = false; } + if (mScreenOffBecauseOfProximity - && mProximity != PROXIMITY_POSITIVE) { + && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) { + // The screen *was* off due to prox being near, but now it's "far" so lets turn + // the screen back on. Also turn it back on if we've been asked to ignore the + // prox sensor temporarily. mScreenOffBecauseOfProximity = false; sendOnProximityNegativeWithWakelock(); } } else { mWaitingForNegativeProximity = false; + mIgnoreProximityUntilChanged = false; } if (mScreenOffBecauseOfProximity) { state = Display.STATE_OFF; @@ -1181,6 +1202,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call sendUpdatePowerState(); } + /** + * Ignores the proximity sensor until the sensor state changes, but only if the sensor is + * currently enabled and forcing the screen to be dark. + */ + public void ignoreProximitySensorUntilChanged() { + mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY); + } + public void setBrightnessConfiguration(BrightnessConfiguration c) { Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c); msg.sendToTarget(); @@ -1529,6 +1558,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Register the listener. // Proximity sensor state already cleared initially. mProximitySensorEnabled = true; + mIgnoreProximityUntilChanged = false; mSensorManager.registerListener(mProximitySensorListener, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL, mHandler); } @@ -1538,6 +1568,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Clear the proximity sensor state for next time. mProximitySensorEnabled = false; mProximity = PROXIMITY_UNKNOWN; + mIgnoreProximityUntilChanged = false; mPendingProximity = PROXIMITY_UNKNOWN; mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); mSensorManager.unregisterListener(mProximitySensorListener); @@ -1580,6 +1611,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && mPendingProximityDebounceTime >= 0) { final long now = SystemClock.uptimeMillis(); if (mPendingProximityDebounceTime <= now) { + if (mProximity != mPendingProximity) { + // if the status of the sensor changed, stop ignoring. + mIgnoreProximityUntilChanged = false; + Slog.i(TAG, "No longer ignoring proximity [" + mPendingProximity + "]"); + } // Sensor reading accepted. Apply the change then release the wake lock. mProximity = mPendingProximity; updatePowerState(); @@ -1723,6 +1759,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private void updatePendingProximityRequestsLocked() { + mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; + mPendingWaitForNegativeProximityLocked = false; + + if (mIgnoreProximityUntilChanged) { + // Also, lets stop waiting for negative proximity if we're ignoring it. + mWaitingForNegativeProximity = false; + } + } + + private void ignoreProximitySensorUntilChangedInternal() { + if (!mIgnoreProximityUntilChanged + && mPowerRequest.useProximitySensor + && mProximity == PROXIMITY_POSITIVE) { + // Only ignore if it is still reporting positive (near) + mIgnoreProximityUntilChanged = true; + Slog.i(TAG, "Ignoring proximity"); + updatePowerState(); + } + } + private final Runnable mOnStateChangedRunnable = new Runnable() { @Override public void run() { @@ -1961,6 +2018,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1); updatePowerState(); break; + + case MSG_IGNORE_PROXIMITY: + ignoreProximitySensorUntilChangedInternal(); + break; } } } diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java index 3fb713bc01a5..cf1d1e53d026 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java @@ -86,7 +86,7 @@ class GnssNetworkConnectivityHandler { // Default time limit in milliseconds for the ConnectivityManager to find a suitable // network with SUPL connectivity or report an error. - private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000; + private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 20 * 1000; private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5; diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 5415967c3bdc..d48570fa3b0f 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -337,6 +337,7 @@ public class BackgroundDexOptService extends JobService { private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs, long lowStorageThreshold) { ArraySet<String> updatedPackages = new ArraySet<>(); + ArraySet<String> updatedPackagesDueToSecondaryDex = new ArraySet<>(); try { final boolean supportSecondaryDex = supportSecondaryDex(); @@ -391,11 +392,14 @@ public class BackgroundDexOptService extends JobService { } int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold, - /*isForPrimaryDex*/ false, updatedPackages); + /*isForPrimaryDex*/ false, updatedPackagesDueToSecondaryDex); return secondaryResult; } finally { // Always let the pinner service know about changes. notifyPinService(updatedPackages); + // Only notify IORap the primary dex opt, because we don't want to + // invalidate traces unnecessary due to b/161633001 and that it's + // better to have a trace than no trace at all. notifyPackagesUpdated(updatedPackages); } } diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index fe6aad70c31e..e48862e2e5e0 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -14,16 +14,16 @@ per-file ApexManager.java = dariofreni@google.com, ioffe@google.com, olilan@goog per-file StagingManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com # dex -per-file AbstractStatsBase.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file BackgroundDexOptService.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file CompilerStats.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file DynamicCodeLoggingService.java = alanstokes@google.com, agampe@google.com, calin@google.com, ngeoffray@google.com -per-file InstructionSets.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file OtaDexoptService.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file OtaDexoptShellCommand.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file PackageDexOptimizer.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file PackageManagerServiceCompilerMapping.java = agampe@google.com, calin@google.com, ngeoffray@google.com -per-file PackageUsage.java = agampe@google.com, calin@google.com, ngeoffray@google.com +per-file AbstractStatsBase.java = calin@google.com, ngeoffray@google.com +per-file BackgroundDexOptService.java = calin@google.com, ngeoffray@google.com +per-file CompilerStats.java = calin@google.com, ngeoffray@google.com +per-file DynamicCodeLoggingService.java = alanstokes@google.com, calin@google.com, ngeoffray@google.com +per-file InstructionSets.java = calin@google.com, ngeoffray@google.com +per-file OtaDexoptService.java = calin@google.com, ngeoffray@google.com +per-file OtaDexoptShellCommand.java = calin@google.com, ngeoffray@google.com +per-file PackageDexOptimizer.java = calin@google.com, ngeoffray@google.com +per-file PackageManagerServiceCompilerMapping.java = calin@google.com, ngeoffray@google.com +per-file PackageUsage.java = calin@google.com, ngeoffray@google.com # multi user / cross profile per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google.com diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 3de2dc2fdef3..a0feb94fd3fc 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -2709,7 +2709,7 @@ public final class Settings { private void writePackageListLPrInternal(int creatingUserId) { // Only derive GIDs for active users (not dying) - final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true); + final List<UserInfo> users = getActiveUsers(UserManagerService.getInstance(), true); int[] userIds = new int[users.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = users.get(i).id; @@ -4449,25 +4449,43 @@ public final class Settings { } /** - * Return all users on the device, including partial or dying users. + * Returns all users on the device, including pre-created and dying users. + * * @param userManager UserManagerService instance * @return the list of users */ private static List<UserInfo> getAllUsers(UserManagerService userManager) { - return getUsers(userManager, false); + return getUsers(userManager, /* excludeDying= */ false, /* excludePreCreated= */ false); + } + + /** + * Returns the list of users on the device, excluding pre-created ones. + * + * @param userManager UserManagerService instance + * @param excludeDying Indicates whether to exclude any users marked for deletion. + * + * @return the list of users + */ + private static List<UserInfo> getActiveUsers(UserManagerService userManager, + boolean excludeDying) { + return getUsers(userManager, excludeDying, /* excludePreCreated= */ true); } /** - * Return the list of users on the device. Clear the calling identity before calling into - * UserManagerService. + * Returns the list of users on the device. + * * @param userManager UserManagerService instance * @param excludeDying Indicates whether to exclude any users marked for deletion. + * @param excludePreCreated Indicates whether to exclude any pre-created users. + * * @return the list of users */ - private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) { + private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying, + boolean excludePreCreated) { long id = Binder.clearCallingIdentity(); try { - return userManager.getUsers(excludeDying); + return userManager.getUsers(/* excludePartial= */ true, excludeDying, + excludePreCreated); } catch (NullPointerException npe) { // packagemanager not yet initialized } finally { diff --git a/services/core/java/com/android/server/pm/dex/OWNERS b/services/core/java/com/android/server/pm/dex/OWNERS index fcc1f6c10eac..5a4431ee8c89 100644 --- a/services/core/java/com/android/server/pm/dex/OWNERS +++ b/services/core/java/com/android/server/pm/dex/OWNERS @@ -1,4 +1,2 @@ -agampe@google.com calin@google.com ngeoffray@google.com -sehr@google.com diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 0890b9212d2c..a4ed4e316ac3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -934,6 +934,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event); + GestureLauncherService gestureService = LocalServices.getService( GestureLauncherService.class); boolean gesturedServiceIntercepted = false; @@ -953,7 +955,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered - || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted; + || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted + || handledByPowerManager; if (!mPowerKeyHandled) { if (interactive) { // When interactive, we're already awake. diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 764ac969e188..f965e5815f1b 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -85,6 +85,7 @@ import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.Display; +import android.view.KeyEvent; import com.android.internal.BrightnessSynchronizer; import com.android.internal.annotations.VisibleForTesting; @@ -5425,6 +5426,29 @@ public final class PowerManagerService extends SystemService } } + /** + * If the user presses power while the proximity sensor is enabled and keeping + * the screen off, then turn the screen back on by telling display manager to + * ignore the proximity sensor. We don't turn off the proximity sensor because + * we still want it to be reenabled if it's state changes. + * + * @return True if the proximity sensor was successfully ignored and we should + * consume the key event. + */ + private boolean interceptPowerKeyDownInternal(KeyEvent event) { + synchronized (mLock) { + // DisplayPowerController only reports proximity positive (near) if it's + // positive and the proximity wasn't already being ignored. So it reliably + // also tells us that we're not already ignoring the proximity sensor. + if (mDisplayPowerRequest.useProximitySensor && mProximityPositive) { + mDisplayManagerInternal.ignoreProximitySensorUntilChanged(); + return true; + } + } + + return false; + } + @VisibleForTesting final class LocalService extends PowerManagerInternal { @Override @@ -5561,5 +5585,10 @@ public final class PowerManagerService extends SystemService public WakeData getLastWakeup() { return getLastWakeupInternal(); } + + @Override + public boolean interceptPowerKeyDown(KeyEvent event) { + return interceptPowerKeyDownInternal(event); + } } } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index bad6b805f042..ae86ea852f32 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -418,6 +418,10 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO AutoMutex _l(mLock); mLocked.viewports = viewports; mLocked.pointerDisplayId = pointerDisplayId; + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); + if (controller != nullptr) { + controller->onDisplayViewportsUpdated(mLocked.viewports); + } } // release lock mInputManager->getReader()->requestRefreshConfiguration( @@ -810,8 +814,8 @@ void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) { } bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; - controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT - : PointerController::InactivityTimeout::NORMAL); + controller->setInactivityTimeout(lightsOut ? InactivityTimeout::SHORT + : InactivityTimeout::NORMAL); } void NativeInputManager::setPointerSpeed(int32_t speed) { diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java index 6861ad1b2e2d..4986d1883307 100644 --- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java +++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java @@ -307,7 +307,7 @@ public class AppTimeLimitController { } } else { if (mActives > mObserved.length) { - // Try to get to a sane state and log the issue + // Try to get to a valid state and log the issue mActives = mObserved.length; final UserData user = mUserRef.get(); if (user == null) return; @@ -334,7 +334,7 @@ public class AppTimeLimitController { cancelCheckTimeoutLocked(this); } else { if (mActives < 0) { - // Try to get to a sane state and log the issue + // Try to get to a valid state and log the issue mActives = 0; final UserData user = mUserRef.get(); if (user == null) return; diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index 5239d976e66f..22629dd52608 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -64,7 +64,7 @@ public final class UsbAlsaManager { private UsbAlsaDevice mSelectedDevice; // - // Device Blacklist + // Device Denylist // // This exists due to problems with Sony game controllers which present as an audio device // even if no headset is connected and have no way to set the volume on the unit. @@ -73,31 +73,31 @@ public final class UsbAlsaManager { private static final int USB_PRODUCTID_PS4CONTROLLER_ZCT1 = 0x05C4; private static final int USB_PRODUCTID_PS4CONTROLLER_ZCT2 = 0x09CC; - private static final int USB_BLACKLIST_OUTPUT = 0x0001; - private static final int USB_BLACKLIST_INPUT = 0x0002; + private static final int USB_DENYLIST_OUTPUT = 0x0001; + private static final int USB_DENYLIST_INPUT = 0x0002; - private static class BlackListEntry { + private static class DenyListEntry { final int mVendorId; final int mProductId; final int mFlags; - BlackListEntry(int vendorId, int productId, int flags) { + DenyListEntry(int vendorId, int productId, int flags) { mVendorId = vendorId; mProductId = productId; mFlags = flags; } } - static final List<BlackListEntry> sDeviceBlacklist = Arrays.asList( - new BlackListEntry(USB_VENDORID_SONY, + static final List<DenyListEntry> sDeviceDenylist = Arrays.asList( + new DenyListEntry(USB_VENDORID_SONY, USB_PRODUCTID_PS4CONTROLLER_ZCT1, - USB_BLACKLIST_OUTPUT), - new BlackListEntry(USB_VENDORID_SONY, + USB_DENYLIST_OUTPUT), + new DenyListEntry(USB_VENDORID_SONY, USB_PRODUCTID_PS4CONTROLLER_ZCT2, - USB_BLACKLIST_OUTPUT)); + USB_DENYLIST_OUTPUT)); - private static boolean isDeviceBlacklisted(int vendorId, int productId, int flags) { - for (BlackListEntry entry : sDeviceBlacklist) { + private static boolean isDeviceDenylisted(int vendorId, int productId, int flags) { + for (DenyListEntry entry : sDeviceDenylist) { if (entry.mVendorId == vendorId && entry.mProductId == productId) { // see if the type flag is set return (entry.mFlags & flags) != 0; @@ -226,11 +226,11 @@ public final class UsbAlsaManager { // Add it to the devices list boolean hasInput = parser.hasInput() - && !isDeviceBlacklisted(usbDevice.getVendorId(), usbDevice.getProductId(), - USB_BLACKLIST_INPUT); + && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), + USB_DENYLIST_INPUT); boolean hasOutput = parser.hasOutput() - && !isDeviceBlacklisted(usbDevice.getVendorId(), usbDevice.getProductId(), - USB_BLACKLIST_OUTPUT); + && !isDeviceDenylisted(usbDevice.getVendorId(), usbDevice.getProductId(), + USB_DENYLIST_OUTPUT); if (DEBUG) { Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput); } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 7595e3f249ce..98b9dcd2cc2f 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -192,22 +192,22 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser private String[] mAccessoryStrings; private final UEventObserver mUEventObserver; - private static Set<Integer> sBlackListedInterfaces; + private static Set<Integer> sDenyInterfaces; private HashMap<Long, FileDescriptor> mControlFds; static { - sBlackListedInterfaces = new HashSet<>(); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_AUDIO); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_COMM); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_HID); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_PRINTER); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_MASS_STORAGE); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_HUB); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_CDC_DATA); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_CSCID); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_CONTENT_SEC); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_VIDEO); - sBlackListedInterfaces.add(UsbConstants.USB_CLASS_WIRELESS_CONTROLLER); + sDenyInterfaces = new HashSet<>(); + sDenyInterfaces.add(UsbConstants.USB_CLASS_AUDIO); + sDenyInterfaces.add(UsbConstants.USB_CLASS_COMM); + sDenyInterfaces.add(UsbConstants.USB_CLASS_HID); + sDenyInterfaces.add(UsbConstants.USB_CLASS_PRINTER); + sDenyInterfaces.add(UsbConstants.USB_CLASS_MASS_STORAGE); + sDenyInterfaces.add(UsbConstants.USB_CLASS_HUB); + sDenyInterfaces.add(UsbConstants.USB_CLASS_CDC_DATA); + sDenyInterfaces.add(UsbConstants.USB_CLASS_CSCID); + sDenyInterfaces.add(UsbConstants.USB_CLASS_CONTENT_SEC); + sDenyInterfaces.add(UsbConstants.USB_CLASS_VIDEO); + sDenyInterfaces.add(UsbConstants.USB_CLASS_WIRELESS_CONTROLLER); } /* @@ -886,7 +886,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser while (interfaceCount >= 0) { UsbInterface intrface = config.getInterface(interfaceCount); interfaceCount--; - if (sBlackListedInterfaces.contains(intrface.getInterfaceClass())) { + if (sDenyInterfaces.contains(intrface.getInterfaceClass())) { mHideUsbNotification = true; break; } diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 140a95d61100..f33001c9241e 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -62,7 +62,7 @@ public class UsbHostManager { private final Context mContext; // USB busses to exclude from USB host support - private final String[] mHostBlacklist; + private final String[] mHostDenyList; private final UsbAlsaManager mUsbAlsaManager; private final UsbPermissionManager mPermissionManager; @@ -235,8 +235,8 @@ public class UsbHostManager { UsbPermissionManager permissionManager) { mContext = context; - mHostBlacklist = context.getResources().getStringArray( - com.android.internal.R.array.config_usbHostBlacklist); + mHostDenyList = context.getResources().getStringArray( + com.android.internal.R.array.config_usbHostDenylist); mUsbAlsaManager = alsaManager; mPermissionManager = permissionManager; String deviceConnectionHandler = context.getResources().getString( @@ -271,10 +271,10 @@ public class UsbHostManager { } } - private boolean isBlackListed(String deviceAddress) { - int count = mHostBlacklist.length; + private boolean isDenyListed(String deviceAddress) { + int count = mHostDenyList.length; for (int i = 0; i < count; i++) { - if (deviceAddress.startsWith(mHostBlacklist[i])) { + if (deviceAddress.startsWith(mHostDenyList[i])) { return true; } } @@ -282,11 +282,11 @@ public class UsbHostManager { } /* returns true if the USB device should not be accessible by applications */ - private boolean isBlackListed(int clazz, int subClass) { - // blacklist hubs + private boolean isDenyListed(int clazz, int subClass) { + // deny hubs if (clazz == UsbConstants.USB_CLASS_HUB) return true; - // blacklist HID boot devices (mouse and keyboard) + // deny HID boot devices (mouse and keyboard) return clazz == UsbConstants.USB_CLASS_HID && subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT; @@ -355,23 +355,23 @@ public class UsbHostManager { Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start"); } - if (isBlackListed(deviceAddress)) { + if (isDenyListed(deviceAddress)) { if (DEBUG) { - Slog.d(TAG, "device address is black listed"); + Slog.d(TAG, "device address is Deny listed"); } return false; } - if (isBlackListed(deviceClass, deviceSubclass)) { + if (isDenyListed(deviceClass, deviceSubclass)) { if (DEBUG) { - Slog.d(TAG, "device class is black listed"); + Slog.d(TAG, "device class is deny listed"); } return false; } UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors); if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE - && !checkUsbInterfacesBlackListed(parser)) { + && !checkUsbInterfacesDenyListed(parser)) { return false; } @@ -491,12 +491,12 @@ public class UsbHostManager { public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserPermissionManager permissions, String packageName, int pid, int uid) { synchronized (mLock) { - if (isBlackListed(deviceAddress)) { + if (isDenyListed(deviceAddress)) { throw new SecurityException("USB device is on a restricted bus"); } UsbDevice device = mDevices.get(deviceAddress); if (device == null) { - // if it is not in mDevices, it either does not exist or is blacklisted + // if it is not in mDevices, it either does not exist or is denylisted throw new IllegalArgumentException( "device " + deviceAddress + " does not exist or is restricted"); } @@ -554,23 +554,23 @@ public class UsbHostManager { } } - private boolean checkUsbInterfacesBlackListed(UsbDescriptorParser parser) { + private boolean checkUsbInterfacesDenyListed(UsbDescriptorParser parser) { // Device class needs to be obtained through the device interface. Ignore device only - // if ALL interfaces are black-listed. + // if ALL interfaces are deny-listed. boolean shouldIgnoreDevice = false; for (UsbDescriptor descriptor: parser.getDescriptors()) { if (!(descriptor instanceof UsbInterfaceDescriptor)) { continue; } UsbInterfaceDescriptor iface = (UsbInterfaceDescriptor) descriptor; - shouldIgnoreDevice = isBlackListed(iface.getUsbClass(), iface.getUsbSubclass()); + shouldIgnoreDevice = isDenyListed(iface.getUsbClass(), iface.getUsbSubclass()); if (!shouldIgnoreDevice) { break; } } if (shouldIgnoreDevice) { if (DEBUG) { - Slog.d(TAG, "usb interface class is black listed"); + Slog.d(TAG, "usb interface class is deny listed"); } return false; } diff --git a/startop/OWNERS b/startop/OWNERS index 3394be9779e3..2d1eb38952ed 100644 --- a/startop/OWNERS +++ b/startop/OWNERS @@ -1,7 +1,7 @@ # mailing list: startop-eng@google.com +calin@google.com chriswailes@google.com eholk@google.com iam@google.com mathieuc@google.com -sehr@google.com yawanng@google.com diff --git a/test-runner/src/android/test/AndroidTestRunner.java b/test-runner/src/android/test/AndroidTestRunner.java index f898516a001b..b2fdc5084834 100644 --- a/test-runner/src/android/test/AndroidTestRunner.java +++ b/test-runner/src/android/test/AndroidTestRunner.java @@ -125,7 +125,7 @@ public class AndroidTestRunner extends BaseTestRunner { } catch (IllegalArgumentException e) { runFailed("Illegal argument passed to constructor. Class: " + testClass.getName()); } catch (InvocationTargetException e) { - runFailed("Constructor thew an exception. Class: " + testClass.getName()); + runFailed("Constructor threw an exception. Class: " + testClass.getName()); } return null; } diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS index 657b3f2add2e..7ee0d9a5e77e 100644 --- a/tests/BootImageProfileTest/OWNERS +++ b/tests/BootImageProfileTest/OWNERS @@ -1,4 +1,4 @@ -mathieuc@google.com calin@google.com +mathieuc@google.com +ngeoffray@google.com yawanng@google.com -sehr@google.com diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index eb0a867d8ec1..a384687e06f6 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -28,6 +28,7 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.os.Process.SYSTEM_UID; import static com.android.server.connectivity.PermissionMonitor.NETWORK; @@ -138,17 +139,10 @@ public class PermissionMonitorTest { verify(mMockPmi).getPackageList(mPermissionMonitor); } - /** - * Remove all permissions from the uid then build new package info and setup permissions to uid - * for checking restricted network permission. - */ - private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid, - String... permissions) { + private boolean wouldBeCarryoverPackage(String partition, int targetSdkVersion, int uid) { final PackageInfo packageInfo = buildPackageInfo(partition, uid, MOCK_USER1); packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; - removeAllPermissions(uid); - addPermissions(uid, permissions); - return mPermissionMonitor.hasRestrictedNetworkPermission(packageInfo.applicationInfo); + return mPermissionMonitor.isCarryoverPackage(packageInfo.applicationInfo); } private static PackageInfo packageInfoWithPartition(String partition) { @@ -228,61 +222,57 @@ public class PermissionMonitorTest { assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo)); } + /** + * Remove all permissions from the uid then setup permissions to uid for checking restricted + * network permission. + */ + private void assertRestrictedNetworkPermission(boolean hasPermission, int uid, + String... permissions) { + removeAllPermissions(uid); + addPermissions(uid, permissions); + assertEquals(hasPermission, mPermissionMonitor.hasRestrictedNetworkPermission(uid)); + } + @Test public void testHasRestrictedNetworkPermission() { - assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); - - assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL)); + assertRestrictedNetworkPermission(false, MOCK_UID1); + assertRestrictedNetworkPermission(false, MOCK_UID1, CHANGE_NETWORK_STATE); + assertRestrictedNetworkPermission(true, MOCK_UID1, NETWORK_STACK); + assertRestrictedNetworkPermission(false, MOCK_UID1, CONNECTIVITY_INTERNAL); + assertRestrictedNetworkPermission(true, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS); + assertRestrictedNetworkPermission(false, MOCK_UID1, CHANGE_WIFI_STATE); + assertRestrictedNetworkPermission(true, MOCK_UID1, PERMISSION_MAINLINE_NETWORK_STACK); + + assertFalse(mPermissionMonitor.hasRestrictedNetworkPermission(MOCK_UID2)); + assertFalse(mPermissionMonitor.hasRestrictedNetworkPermission(SYSTEM_UID)); } @Test - public void testHasRestrictedNetworkPermissionSystemUid() { + public void testIsCarryoverPackage() { doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt(); - assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); - assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - } - - @Test - public void testHasRestrictedNetworkPermissionVendorApp() { - assertTrue(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); - - assertFalse(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); + + assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1)); } private void assertBackgroundPermission(boolean hasPermission, String name, int uid, @@ -296,19 +286,23 @@ public class PermissionMonitorTest { @Test public void testHasUseBackgroundNetworksPermission() throws Exception { - doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); - assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID)); - assertBackgroundPermission(false, "system1", SYSTEM_UID); - assertBackgroundPermission(false, "system2", SYSTEM_UID, CONNECTIVITY_INTERNAL); - assertBackgroundPermission(true, "system3", SYSTEM_UID, CHANGE_NETWORK_STATE); - assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1)); assertBackgroundPermission(false, "mock1", MOCK_UID1); - assertBackgroundPermission(true, "mock2", MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS); + assertBackgroundPermission(false, "mock2", MOCK_UID1, CONNECTIVITY_INTERNAL); + assertBackgroundPermission(true, "mock3", MOCK_UID1, NETWORK_STACK); assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2)); - assertBackgroundPermission(false, "mock3", MOCK_UID2, CONNECTIVITY_INTERNAL); - assertBackgroundPermission(true, "mock4", MOCK_UID2, NETWORK_STACK); + assertBackgroundPermission(false, "mock4", MOCK_UID2); + assertBackgroundPermission(true, "mock5", MOCK_UID2, + CONNECTIVITY_USE_RESTRICTED_NETWORKS); + + doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); + assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID)); + assertBackgroundPermission(false, "system1", SYSTEM_UID); + assertBackgroundPermission(true, "system2", SYSTEM_UID, CHANGE_NETWORK_STATE); + doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt(); + removeAllPermissions(SYSTEM_UID); + assertBackgroundPermission(true, "system3", SYSTEM_UID); } private class NetdMonitor { diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 4ccf79a0cb37..de1c5759ee87 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -30,6 +30,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -49,6 +50,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.NotificationManager; @@ -65,6 +67,7 @@ import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecManager; import android.net.LinkProperties; +import android.net.LocalSocket; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo.DetailedState; @@ -74,6 +77,7 @@ import android.net.VpnManager; import android.net.VpnService; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.ConditionVariable; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Process; @@ -101,13 +105,20 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.net.Inet4Address; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; /** @@ -133,7 +144,8 @@ public class VpnTest { managedProfileA.profileGroupId = primaryUser.id; } - static final String TEST_VPN_PKG = "com.dummy.vpn"; + static final String EGRESS_IFACE = "wlan0"; + static final String TEST_VPN_PKG = "com.testvpn.vpn"; private static final String TEST_VPN_SERVER = "1.2.3.4"; private static final String TEST_VPN_IDENTITY = "identity"; private static final byte[] TEST_VPN_PSK = "psk".getBytes(); @@ -1012,31 +1024,190 @@ public class VpnTest { // a subsequent CL. } - @Test - public void testStartLegacyVpn() throws Exception { + public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception { final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); // Dummy egress interface - final String egressIface = "DUMMY0"; final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(egressIface); + lp.setInterfaceName(EGRESS_IFACE); final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - InetAddresses.parseNumericAddress("192.0.2.0"), egressIface); + InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE); lp.addRoute(defaultRoute); - vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp); + vpn.startLegacyVpn(vpnProfile, mKeyStore, lp); + return vpn; + } + @Test + public void testStartPlatformVpn() throws Exception { + startLegacyVpn(mVpnProfile); // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in - // a subsequent CL. + // a subsequent patch. + } + + @Test + public void testStartRacoonNumericAddress() throws Exception { + startRacoon("1.2.3.4", "1.2.3.4"); + } + + @Test + public void testStartRacoonHostname() throws Exception { + startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve + } + + public void startRacoon(final String serverAddr, final String expectedAddr) + throws Exception { + final ConditionVariable legacyRunnerReady = new ConditionVariable(); + final VpnProfile profile = new VpnProfile("testProfile" /* key */); + profile.type = VpnProfile.TYPE_L2TP_IPSEC_PSK; + profile.name = "testProfileName"; + profile.username = "userName"; + profile.password = "thePassword"; + profile.server = serverAddr; + profile.ipsecIdentifier = "id"; + profile.ipsecSecret = "secret"; + profile.l2tpSecret = "l2tpsecret"; + when(mConnectivityManager.getAllNetworks()) + .thenReturn(new Network[] { new Network(101) }); + when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), + anyInt(), any(), anyInt())).thenAnswer(invocation -> { + // The runner has registered an agent and is now ready. + legacyRunnerReady.open(); + return new Network(102); + }); + final Vpn vpn = startLegacyVpn(profile); + final TestDeps deps = (TestDeps) vpn.mDeps; + try { + // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK + assertArrayEquals( + new String[] { EGRESS_IFACE, expectedAddr, "udppsk", + profile.ipsecIdentifier, profile.ipsecSecret, "1701" }, + deps.racoonArgs.get(10, TimeUnit.SECONDS)); + // literal values are hardcoded in Vpn.java for mtpd args + assertArrayEquals( + new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret, + "name", profile.username, "password", profile.password, + "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns", + "idle", "1800", "mtu", "1400", "mru", "1400" }, + deps.mtpdArgs.get(10, TimeUnit.SECONDS)); + // Now wait for the runner to be ready before testing for the route. + legacyRunnerReady.block(10_000); + // In this test the expected address is always v4 so /32 + final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"), + RouteInfo.RTN_THROW); + assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : " + + vpn.mConfig.routes, + vpn.mConfig.routes.contains(expectedRoute)); + } finally { + // Now interrupt the thread, unblock the runner and clean up. + vpn.mVpnRunner.exitVpnRunner(); + deps.getStateFile().delete(); // set to delete on exit, but this deletes it earlier + vpn.mVpnRunner.join(10_000); // wait for up to 10s for the runner to die and cleanup + } + } + + private static final class TestDeps extends Vpn.Dependencies { + public final CompletableFuture<String[]> racoonArgs = new CompletableFuture(); + public final CompletableFuture<String[]> mtpdArgs = new CompletableFuture(); + public final File mStateFile; + + private final HashMap<String, Boolean> mRunningServices = new HashMap<>(); + + TestDeps() { + try { + mStateFile = File.createTempFile("vpnTest", ".tmp"); + mStateFile.deleteOnExit(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void startService(final String serviceName) { + mRunningServices.put(serviceName, true); + } + + @Override + public void stopService(final String serviceName) { + mRunningServices.put(serviceName, false); + } + + @Override + public boolean isServiceRunning(final String serviceName) { + return mRunningServices.getOrDefault(serviceName, false); + } + + @Override + public boolean isServiceStopped(final String serviceName) { + return !isServiceRunning(serviceName); + } + + @Override + public File getStateFile() { + return mStateFile; + } + + @Override + public void sendArgumentsToDaemon( + final String daemon, final LocalSocket socket, final String[] arguments, + final Vpn.RetryScheduler interruptChecker) throws IOException { + if ("racoon".equals(daemon)) { + racoonArgs.complete(arguments); + } else if ("mtpd".equals(daemon)) { + writeStateFile(arguments); + mtpdArgs.complete(arguments); + } else { + throw new UnsupportedOperationException("Unsupported daemon : " + daemon); + } + } + + private void writeStateFile(final String[] arguments) throws IOException { + mStateFile.delete(); + mStateFile.createNewFile(); + mStateFile.deleteOnExit(); + final BufferedWriter writer = new BufferedWriter( + new FileWriter(mStateFile, false /* append */)); + writer.write(EGRESS_IFACE); + writer.write("\n"); + // addresses + writer.write("10.0.0.1/24\n"); + // routes + writer.write("192.168.6.0/24\n"); + // dns servers + writer.write("192.168.6.1\n"); + // search domains + writer.write("vpn.searchdomains.com\n"); + // endpoint - intentionally empty + writer.write("\n"); + writer.flush(); + writer.close(); + } + + @Override + @NonNull + public InetAddress resolve(final String endpoint) { + try { + // If a numeric IP address, return it. + return InetAddress.parseNumericAddress(endpoint); + } catch (IllegalArgumentException e) { + // Otherwise, return some token IP to test for. + return InetAddress.parseNumericAddress("5.6.7.8"); + } + } + + @Override + public boolean checkInterfacePresent(final Vpn vpn, final String iface) { + return true; + } } /** * Mock some methods of vpn object. */ private Vpn createVpn(@UserIdInt int userId) { - return new Vpn(Looper.myLooper(), mContext, mNetService, + return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService, userId, mKeyStore, mSystemServices, mIkev2SessionCreator); } diff --git a/tests/utils/DummyIME/Android.bp b/tests/utils/StubIME/Android.bp index 4a44b3b27992..668c92c86c51 100644 --- a/tests/utils/DummyIME/Android.bp +++ b/tests/utils/StubIME/Android.bp @@ -15,7 +15,7 @@ // android_test { - name: "DummyIME", + name: "StubIME", srcs: ["src/**/*.java"], sdk_version: "current", } diff --git a/tests/utils/DummyIME/AndroidManifest.xml b/tests/utils/StubIME/AndroidManifest.xml index fd17a52cb7d9..04502d331d6a 100644 --- a/tests/utils/DummyIME/AndroidManifest.xml +++ b/tests/utils/StubIME/AndroidManifest.xml @@ -17,16 +17,16 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.testing.dummyime"> - <application android:label="Dummy IME"> - <service android:name="DummyIme" + package="com.android.testing.stubime"> + <application android:label="Stub IME"> + <service android:name="StubIme" android:permission="android.permission.BIND_INPUT_METHOD"> <intent-filter> <action android:name="android.view.InputMethod" /> </intent-filter> <meta-data android:name="android.view.im" android:resource="@xml/method" /> </service> - <activity android:name=".ImePreferences" android:label="Dummy IME Settings"> + <activity android:name=".ImePreferences" android:label="Stub IME Settings"> <intent-filter> <action android:name="android.intent.action.MAIN"/> </intent-filter> diff --git a/tests/utils/DummyIME/res/xml/method.xml b/tests/utils/StubIME/res/xml/method.xml index 43a330e2bc93..1bb4bcd3480b 100644 --- a/tests/utils/DummyIME/res/xml/method.xml +++ b/tests/utils/StubIME/res/xml/method.xml @@ -21,9 +21,9 @@ <!-- for the Search Manager. --> <input-method xmlns:android="http://schemas.android.com/apk/res/android" - android:settingsActivity="com.android.testing.dummyime.ImePreferences"> + android:settingsActivity="com.android.testing.stubime.ImePreferences"> <subtype android:label="Generic" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" /> -</input-method>
\ No newline at end of file +</input-method> diff --git a/tests/utils/DummyIME/src/com/android/testing/dummyime/ImePreferences.java b/tests/utils/StubIME/src/com/android/testing/stubime/ImePreferences.java index 41036ab86596..b77525ad0a43 100644 --- a/tests/utils/DummyIME/src/com/android/testing/dummyime/ImePreferences.java +++ b/tests/utils/StubIME/src/com/android/testing/stubime/ImePreferences.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.testing.dummyime; +package com.android.testing.stubime; import android.preference.PreferenceActivity; /** - * Dummy IME preference activity + * Stub IME preference activity */ public class ImePreferences extends PreferenceActivity { diff --git a/tests/utils/DummyIME/src/com/android/testing/dummyime/DummyIme.java b/tests/utils/StubIME/src/com/android/testing/stubime/StubIme.java index 7b7a39a702e5..8795202b3283 100644 --- a/tests/utils/DummyIME/src/com/android/testing/dummyime/DummyIme.java +++ b/tests/utils/StubIME/src/com/android/testing/stubime/StubIme.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.testing.dummyime; +package com.android.testing.stubime; import android.inputmethodservice.InputMethodService; /** - * Dummy IME implementation that basically does nothing + * Stub IME implementation that basically does nothing */ -public class DummyIme extends InputMethodService { +public class StubIme extends InputMethodService { @Override public boolean onEvaluateFullscreenMode() { |