Merge "Make pending intents of uninstall failure immutable"
diff --git a/api/system-current.txt b/api/system-current.txt
index 5aec633..6a044508 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3674,8 +3674,13 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+ field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START";
+ field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+ field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
field public static final long FUNCTION_ADB = 1L; // 0x1L
field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
diff --git a/api/test-current.txt b/api/test-current.txt
index 113d585..e11fcde 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -144,6 +144,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void stopSystemLockTaskMode();
method public static boolean supportsMultiWindow(android.content.Context);
method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
+ field public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440; // 0x1b8
field public static final int INVALID_STACK_ID = -1; // 0xffffffff
field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 0617eb6..124f815f 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -216,10 +216,6 @@
// address: true,
//},
},
- debuggable: {
- // Add a flag to enable stats log printing from statsd on debug builds.
- cflags: ["-DVERY_VERBOSE_PRINTING"],
- },
},
proto: {
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index e7b32c5..05e9ec3 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -409,11 +409,9 @@
onWatchdogRollbackOccurredLocked(event);
}
-#ifdef VERY_VERBOSE_PRINTING
if (mPrintAllLogs) {
ALOGI("%s", event->ToString().c_str());
}
-#endif
resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
// Hard-coded logic to update the isolated uid's in the uid-map.
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 23f2584..c0f54a0 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -139,10 +139,8 @@
int64_t getLastReportTimeNs(const ConfigKey& key);
inline void setPrintLogs(bool enabled) {
-#ifdef VERY_VERBOSE_PRINTING
std::lock_guard<std::mutex> lock(mMetricsMutex);
mPrintAllLogs = enabled;
-#endif
}
// Add a specific config key to the possible configs to dump ASAP.
@@ -276,9 +274,7 @@
//Last time we wrote metadata to disk.
int64_t mLastMetadataWriteNs = 0;
-#ifdef VERY_VERBOSE_PRINTING
bool mPrintAllLogs = false;
-#endif
FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 3226482..d5e3314 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -484,7 +484,8 @@
dprintf(out, " Clear cached puller data.\n");
dprintf(out, "\n");
dprintf(out, "usage: adb shell cmd stats print-logs\n");
- dprintf(out, " Only works on eng build\n");
+ dprintf(out, " Requires root privileges.\n");
+ dprintf(out, " Can be disabled by calling adb shell cmd stats print-logs 0\n");
}
status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
@@ -865,18 +866,19 @@
}
status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
- VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
- AIBinder_getCallingUid());
- if (checkPermission(kPermissionDump)) {
- bool enabled = true;
- if (args.size() >= 2) {
- enabled = atoi(args[1].c_str()) != 0;
- }
- mProcessor->setPrintLogs(enabled);
- return NO_ERROR;
- } else {
+ Status status = checkUid(AID_ROOT);
+ if (!status.isOk()) {
return PERMISSION_DENIED;
}
+
+ VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ bool enabled = true;
+ if (args.size() >= 2) {
+ enabled = atoi(args[1].c_str()) != 0;
+ }
+ mProcessor->setPrintLogs(enabled);
+ return NO_ERROR;
}
bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 0f31529..4283d7a 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -31,6 +31,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.DisplayMetrics;
import android.util.Singleton;
import java.util.List;
@@ -139,6 +140,8 @@
public static final String EXTRA_IGNORE_TARGET_SECURITY =
"android.app.extra.EXTRA_IGNORE_TARGET_SECURITY";
+ /** The minimal size of a display's long-edge needed to support split-screen multi-window. */
+ public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440;
private static int sMaxRecentTasks = -1;
@@ -282,8 +285,23 @@
com.android.internal.R.bool.config_supportsMultiWindow);
}
- /** Returns true if the system supports split screen multi-window. */
+ /**
+ * Returns {@code true} if the display the context is associated with supports split screen
+ * multi-window.
+ *
+ * @throws UnsupportedOperationException if the supplied {@link Context} is not associated with
+ * a display.
+ */
public static boolean supportsSplitScreenMultiWindow(Context context) {
+ DisplayMetrics dm = new DisplayMetrics();
+ context.getDisplay().getRealMetrics(dm);
+
+ int widthDp = (int) (dm.widthPixels / dm.density);
+ int heightDp = (int) (dm.heightPixels / dm.density);
+ if (Math.max(widthDp, heightDp) < DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP) {
+ return false;
+ }
+
return supportsMultiWindow(context)
&& Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 89e1f5a..a2947a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -86,6 +86,8 @@
import android.graphics.HardwareRenderer;
import android.hardware.display.DisplayManagerGlobal;
import android.inputmethodservice.InputMethodService;
+import android.media.MediaFrameworkInitializer;
+import android.media.MediaServiceManager;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.Proxy;
@@ -7665,6 +7667,7 @@
public static void initializeMainlineModules() {
TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager());
StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager());
+ MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
}
private void purgePendingResources() {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3410c92..f82ab7b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4801,7 +4801,6 @@
contentView.setViewVisibility(R.id.time, View.GONE);
contentView.setImageViewIcon(R.id.profile_badge, null);
contentView.setViewVisibility(R.id.profile_badge, View.GONE);
- contentView.setViewVisibility(R.id.alerted_icon, View.GONE);
mN.mUsesStandardHeader = false;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index c20c11f..59997cc 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -101,11 +101,11 @@
import android.location.ILocationManager;
import android.location.LocationManager;
import android.media.AudioManager;
+import android.media.MediaFrameworkInitializer;
import android.media.MediaRouter;
import android.media.midi.IMidiManager;
import android.media.midi.MidiManager;
import android.media.projection.MediaProjectionManager;
-import android.media.session.MediaSessionManager;
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
@@ -855,13 +855,6 @@
return new ConsumerIrManager(ctx);
}});
- registerService(Context.MEDIA_SESSION_SERVICE, MediaSessionManager.class,
- new CachedServiceFetcher<MediaSessionManager>() {
- @Override
- public MediaSessionManager createService(ContextImpl ctx) {
- return new MediaSessionManager(ctx);
- }});
-
registerService(Context.TRUST_SERVICE, TrustManager.class,
new StaticServiceFetcher<TrustManager>() {
@Override
@@ -1335,6 +1328,7 @@
WifiFrameworkInitializer.registerServiceWrappers();
StatsFrameworkInitializer.registerServiceWrappers();
RollbackManagerFrameworkInitializer.initialize();
+ MediaFrameworkInitializer.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 940bf26..f533760 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4673,8 +4673,7 @@
* Marks an application exempt from having its permissions be automatically revoked when
* the app is unused for an extended period of time.
*
- * Only the installer on record that installed the given package, or a holder of
- * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+ * Only the installer on record that installed the given package is allowed to call this.
*
* Packages start in whitelisted state, and it is the installer's responsibility to
* un-whitelist the packages it installs, unless auto-revoking permissions from that package
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 58cb1e1..c7fc2ad 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -610,7 +610,7 @@
serverCallback.sendResult(null /* data */);
}
}
- });
+ }, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index f4122fe..2f8c97f 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -90,7 +90,7 @@
void resetLockout(int userId, in byte [] hardwareAuthToken);
// Add a callback which gets notified when the face lockout period expired.
- void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
+ void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
void setFeature(IBinder token, int userId, int feature, boolean enabled,
in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, String opPackageName);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index abe63d6..9dacca7 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -768,7 +768,7 @@
serverCallback.sendResult(null /* data */);
}
}
- });
+ }, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 5adba75..ad58fea 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -94,7 +94,7 @@
void resetLockout(int userId, in byte [] hardwareAuthToken);
// Add a callback which gets notified when the fingerprint lockout period expired.
- void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
+ void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
// Check if a client request is currently being handled
boolean isClientActive();
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index a94266b..f4cfc74 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -24,7 +24,6 @@
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
-import java.util.Objects;
import java.util.Objects;
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index d16f070..ef305e2 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -160,6 +160,22 @@
"android.hardware.usb.action.USB_ACCESSORY_DETACHED";
/**
+ * Broadcast Action: A broadcast for USB accessory handshaking information delivery
+ *
+ * This intent is sent when a USB accessory connect attempt
+ *
+ * <p>For more information about communicating with USB accessory handshake, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ *
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public static final String ACTION_USB_ACCESSORY_HANDSHAKE =
+ "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
+
+ /**
* Boolean extra indicating whether USB is connected or disconnected.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*
@@ -303,6 +319,52 @@
public static final String EXTRA_ACCESSORY = "accessory";
/**
+ * A long extra indicating ms from boot to get get_protocol UEvent
+ * This is obtained with SystemClock.elapsedRealtime()
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_UEVENT_TIME =
+ "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
+
+ /**
+ * An integer extra indicating numbers of send string during handshake
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts
+ *
+ * <p>For more information about control request with identifying string information
+ * between communicating with USB accessory handshake, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_STRING_COUNT =
+ "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+
+ /**
+ * Boolean extra indicating whether got start accessory or not
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_START =
+ "android.hardware.usb.extra.ACCESSORY_START";
+
+ /**
+ * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE}
+ * This is obtained with SystemClock.elapsedRealtime()
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_HANDSHAKE_END =
+ "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+
+ /**
* Name of extra added to the {@link android.app.PendingIntent}
* passed into {@link #requestPermission(UsbDevice, PendingIntent)}
* or {@link #requestPermission(UsbAccessory, PendingIntent)}
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0e10c42..0eb3c1e 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -38,7 +38,9 @@
* Representation of a MAC address.
*
* This class only supports 48 bits long addresses and does not support 64 bits long addresses.
- * Instances of this class are immutable.
+ * Instances of this class are immutable. This class provides implementations of hashCode()
+ * and equals() that make it suitable for use as keys in standard implementations of
+ * {@link java.util.Map}.
*/
public final class MacAddress implements Parcelable {
@@ -122,12 +124,22 @@
}
/**
+ * Convert this MacAddress to a byte array.
+ *
+ * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6,
+ * the returned array is [1, 2, 3, 4, 5, 6].
+ *
* @return a byte array representation of this MacAddress.
*/
public @NonNull byte[] toByteArray() {
return byteAddrFromLongAddr(mAddr);
}
+ /**
+ * Returns a human-readable representation of this MacAddress.
+ * The exact format is implementation-dependent and should not be assumed to have any
+ * particular format.
+ */
@Override
public @NonNull String toString() {
return stringAddrFromLongAddr(mAddr);
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index d35ce5b..32c80e7 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -159,7 +159,7 @@
*
* @param fileSize - size of the signed file (APK)
*/
- public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
+ public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo,
SigningInfo signingInfo) {
final int size =
4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d44c7fd..feebb94 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7883,19 +7883,26 @@
public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
/**
+ * For user preference if swipe bottom to expand notification gesture enabled.
+ * @hide
+ */
+ public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
+ "swipe_bottom_to_notification_enabled";
+
+ /**
* For user preference if One-Handed Mode enabled.
* @hide
*/
public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled";
/**
- * For user perference if One-Handed Mode timeout.
+ * For user preference if One-Handed Mode timeout.
* @hide
*/
public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout";
/**
- * For user tapps app to exit One-Handed Mode.
+ * For user taps app to exit One-Handed Mode.
* @hide
*/
public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit";
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 2677087..5d34c47 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1938,6 +1938,17 @@
/**
* @hide
*/
+ public @NonNull Ranking withAudiblyAlertedInfo(@Nullable Ranking previous) {
+ if (previous != null && previous.mLastAudiblyAlertedMs > 0
+ && this.mLastAudiblyAlertedMs <= 0) {
+ this.mLastAudiblyAlertedMs = previous.mLastAudiblyAlertedMs;
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
public void populate(Ranking other) {
populate(other.mKey,
other.mRank,
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 346fe29..6e34666 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -24,7 +24,6 @@
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
-import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.util.ArrayMap;
@@ -213,11 +212,9 @@
verityDigest, apk.length(), signatureInfo);
}
- byte[] digest = pickBestDigestForV4(contentDigests);
-
return new VerifiedSigner(
signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
- verityRootHash, digest);
+ verityRootHash, contentDigests);
}
private static X509Certificate[] verifySigner(
@@ -339,8 +336,7 @@
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
}
- certificate = new VerbatimX509Certificate(
- certificate, encodedCert);
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
certs.add(certificate);
}
@@ -434,12 +430,15 @@
public final X509Certificate[][] certs;
public final byte[] verityRootHash;
- public final byte[] digest;
+ // Algorithm -> digest map of signed digests in the signature.
+ // All these are verified if requested.
+ public final Map<Integer, byte[]> contentDigests;
- public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
+ public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash,
+ Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.verityRootHash = verityRootHash;
- this.digest = digest;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 4ab541b..9357285 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -24,7 +24,6 @@
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
-import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.os.Build;
@@ -161,7 +160,7 @@
boolean doVerifyIntegrity) throws SecurityException, IOException {
int signerCount = 0;
Map<Integer, byte[]> contentDigests = new ArrayMap<>();
- VerifiedSigner result = null;
+ Pair<X509Certificate[], VerifiedProofOfRotation> result = null;
CertificateFactory certFactory;
try {
certFactory = CertificateFactory.getInstance("X.509");
@@ -206,18 +205,17 @@
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
+ byte[] verityRootHash = null;
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
- result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
+ verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
verityDigest, apk.length(), signatureInfo);
}
- result.digest = pickBestDigestForV4(contentDigests);
-
- return result;
+ return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests);
}
- private static VerifiedSigner verifySigner(
+ private static Pair<X509Certificate[], VerifiedProofOfRotation> verifySigner(
ByteBuffer signerBlock,
Map<Integer, byte[]> contentDigests,
CertificateFactory certFactory)
@@ -349,8 +347,7 @@
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
}
- certificate = new VerbatimX509Certificate(
- certificate, encodedCert);
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
certs.add(certificate);
}
@@ -382,8 +379,9 @@
private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c;
- private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs,
- List<X509Certificate> certs, CertificateFactory certFactory) throws IOException {
+ private static Pair<X509Certificate[], VerifiedProofOfRotation> verifyAdditionalAttributes(
+ ByteBuffer attrs, List<X509Certificate> certs, CertificateFactory certFactory)
+ throws IOException {
X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]);
VerifiedProofOfRotation por = null;
@@ -421,7 +419,7 @@
break;
}
}
- return new VerifiedSigner(certChain, por);
+ return Pair.create(certChain, por);
}
private static VerifiedProofOfRotation verifyProofOfRotationStruct(
@@ -570,12 +568,17 @@
public final X509Certificate[] certs;
public final VerifiedProofOfRotation por;
- public byte[] verityRootHash;
- public byte[] digest;
+ public final byte[] verityRootHash;
+ // Algorithm -> digest map of signed digests in the signature.
+ // All these are verified if requested.
+ public final Map<Integer, byte[]> contentDigests;
- public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
+ public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por,
+ byte[] verityRootHash, Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.por = por;
+ this.verityRootHash = verityRootHash;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index d40efce..844816c 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -16,12 +16,14 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import android.os.incremental.IncrementalManager;
import android.os.incremental.V4Signature;
+import android.util.ArrayMap;
import android.util.Pair;
import java.io.ByteArrayInputStream;
@@ -42,6 +44,7 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
+import java.util.Map;
/**
* APK Signature Scheme v4 verifier.
@@ -79,13 +82,20 @@
throw new SignatureNotFoundException("Failed to read V4 signature.", e);
}
- final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo,
+ // Verify signed data and extract certificates and apk digest.
+ final byte[] signedData = V4Signature.getSignedData(apk.length(), hashingInfo,
signingInfo);
+ final Pair<Certificate, byte[]> result = verifySigner(signingInfo, signedData);
- return verifySigner(signingInfo, signedData);
+ // Populate digests enforced by IncFS driver.
+ Map<Integer, byte[]> contentDigests = new ArrayMap<>();
+ contentDigests.put(convertToContentDigestType(hashingInfo.hashAlgorithm),
+ hashingInfo.rawRootHash);
+
+ return new VerifiedSigner(new Certificate[]{result.first}, result.second, contentDigests);
}
- private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo,
+ private static Pair<Certificate, byte[]> verifySigner(V4Signature.SigningInfo signingInfo,
final byte[] signedData) throws SecurityException {
if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) {
throw new SecurityException("No supported signatures found");
@@ -145,21 +155,34 @@
"Public key mismatch between certificate and signature record");
}
- return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
+ return Pair.create(certificate, signingInfo.apkDigest);
+ }
+
+ private static int convertToContentDigestType(int hashAlgorithm) throws SecurityException {
+ if (hashAlgorithm == V4Signature.HASHING_ALGORITHM_SHA256) {
+ return CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
+ }
+ throw new SecurityException("Unsupported hashAlgorithm: " + hashAlgorithm);
}
/**
- * Verified APK Signature Scheme v4 signer, including V3 digest.
+ * Verified APK Signature Scheme v4 signer, including V2/V3 digest.
*
* @hide for internal use only.
*/
public static class VerifiedSigner {
public final Certificate[] certs;
- public byte[] apkDigest;
+ public final byte[] apkDigest;
- public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
+ // Algorithm -> digest map of signed digests in the signature.
+ // These are continuously enforced by the IncFS driver.
+ public final Map<Integer, byte[]> contentDigests;
+
+ public VerifiedSigner(Certificate[] certs, byte[] apkDigest,
+ Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.apkDigest = apkDigest;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index ab8f80d3..e0258f7 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -45,6 +45,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;
@@ -184,21 +185,21 @@
Signature[] signerSigs = convertToSignatures(signerCerts);
if (verifyFull) {
- byte[] nonstreamingDigest = null;
- Certificate[][] nonstreamingCerts = null;
+ Map<Integer, byte[]> nonstreamingDigests;
+ Certificate[][] nonstreamingCerts;
try {
// v4 is an add-on and requires v2 or v3 signature to validate against its
// certificate and digest
ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
- nonstreamingDigest = v3Signer.digest;
+ nonstreamingDigests = v3Signer.contentDigests;
nonstreamingCerts = new Certificate[][]{v3Signer.certs};
} catch (SignatureNotFoundException e) {
try {
ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
ApkSignatureSchemeV2Verifier.verify(apkPath, false);
- nonstreamingDigest = v2Signer.digest;
+ nonstreamingDigests = v2Signer.contentDigests;
nonstreamingCerts = v2Signer.certs;
} catch (SignatureNotFoundException ee) {
throw new SecurityException(
@@ -220,8 +221,15 @@
}
}
- if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
- vSigner.apkDigest.length)) {
+ boolean found = false;
+ for (byte[] nonstreamingDigest : nonstreamingDigests.values()) {
+ if (ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
+ vSigner.apkDigest.length)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
throw new SecurityException("APK digest in V4 signature does not match V2/V3");
}
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 6efe95c..990092c 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -577,21 +577,6 @@
}
/**
- * Returns the best digest from the map of available digests.
- * similarly to compareContentDigestAlgorithm.
- *
- * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
- */
- static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
- for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
- if (contentDigests.containsKey(algo)) {
- return contentDigests.get(algo);
- }
- }
- return null;
- }
-
- /**
* Returns new byte buffer whose content is a shared subsequence of this buffer's content
* between the specified start (inclusive) and end (exclusive) positions. As opposed to
* {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index ff3ac073..9eb63087 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -174,12 +174,9 @@
int touchSlop = config.getScaledTouchSlop();
mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
if (mMovedEnoughForDrag) {
- // If the direction of the swipe motion is within 30 degrees of vertical, it is
- // considered a vertical drag. We don't actually have to compute the angle to
- // implement the check though. When the angle is exactly 30 degrees from
- // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from
- // vertical, 2*deltaX < distance.
- mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
+ // If the direction of the swipe motion is within 45 degrees of vertical, it is
+ // considered a vertical drag.
+ mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY);
}
}
} else if (action == MotionEvent.ACTION_CANCEL) {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 843700c..32c68bd 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -108,7 +108,7 @@
*
* @return the swap result, index 0 is the start index and index 1 is the end index.
*/
- private static int[] sortSelctionIndices(int selectionStart, int selectionEnd) {
+ private static int[] sortSelectionIndices(int selectionStart, int selectionEnd) {
if (selectionStart < selectionEnd) {
return new int[]{selectionStart, selectionEnd};
}
@@ -122,11 +122,11 @@
* @param textView the selected TextView.
* @return the swap result, index 0 is the start index and index 1 is the end index.
*/
- private static int[] sortSelctionIndicesFromTextView(TextView textView) {
+ private static int[] sortSelectionIndicesFromTextView(TextView textView) {
int selectionStart = textView.getSelectionStart();
int selectionEnd = textView.getSelectionEnd();
- return sortSelctionIndices(selectionStart, selectionEnd);
+ return sortSelectionIndices(selectionStart, selectionEnd);
}
/**
@@ -135,7 +135,7 @@
public void startSelectionActionModeAsync(boolean adjustSelection) {
// Check if the smart selection should run for editable text.
adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled();
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onOriginalSelection(
getText(mTextView),
@@ -165,7 +165,7 @@
* Starts Link ActionMode.
*/
public void startLinkActionModeAsync(int start, int end) {
- int[] indexResult = sortSelctionIndices(start, end);
+ int[] indexResult = sortSelectionIndices(start, end);
mSelectionTracker.onOriginalSelection(getText(mTextView), indexResult[0], indexResult[1],
true /*isLink*/);
cancelAsyncTask();
@@ -201,21 +201,21 @@
/** Reports a selection action event. */
public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionAction(
sortedSelectionIndices[0], sortedSelectionIndices[1],
getActionType(menuItemId), actionLabel, mTextClassification);
}
public void onSelectionDrag() {
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionAction(
sortedSelectionIndices[0], sortedSelectionIndices[1],
SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
}
public void onTextChanged(int start, int end) {
- int[] sortedSelectionIndices = sortSelctionIndices(start, end);
+ int[] sortedSelectionIndices = sortSelectionIndices(start, end);
mSelectionTracker.onTextChanged(sortedSelectionIndices[0], sortedSelectionIndices[1],
mTextClassification);
}
@@ -334,7 +334,7 @@
startSelectionActionMode(startSelectionResult);
};
// TODO do not trigger the animation if the change included only non-printable characters
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
final boolean didSelectionChange =
result != null && (sortedSelectionIndices[0] != result.mStart
|| sortedSelectionIndices[1] != result.mEnd);
@@ -486,7 +486,7 @@
if (actionMode != null) {
actionMode.invalidate();
}
- final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionUpdated(
sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification);
mTextClassificationAsyncTask = null;
@@ -495,7 +495,7 @@
private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
if (selectionStart < 0 || selectionEnd < 0) {
// Use selection indices
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
selectionStart = sortedSelectionIndices[0];
selectionEnd = sortedSelectionIndices[1];
}
@@ -637,7 +637,7 @@
mAllowReset = false;
boolean selected = editor.selectCurrentWord();
if (selected) {
- final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(textView);
+ final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(textView);
mSelectionStart = sortedSelectionIndices[0];
mSelectionEnd = sortedSelectionIndices[1];
mLogger.logSelectionAction(
@@ -1215,7 +1215,7 @@
SelectionResult(int start, int end,
@Nullable TextClassification classification, @Nullable TextSelection selection) {
- int[] sortedIndices = sortSelctionIndices(start, end);
+ int[] sortedIndices = sortSelectionIndices(start, end);
mStart = sortedIndices[0];
mEnd = sortedIndices[1];
mClassification = classification;
diff --git a/core/java/android/widget/TextViewRichContentReceiver.java b/core/java/android/widget/TextViewRichContentReceiver.java
index 5678a1e..4f2d954 100644
--- a/core/java/android/widget/TextViewRichContentReceiver.java
+++ b/core/java/android/widget/TextViewRichContentReceiver.java
@@ -116,9 +116,6 @@
private static boolean onReceiveForAutofill(@NonNull TextView textView, @NonNull ClipData clip,
@Flags int flags) {
final CharSequence text = coerceToText(clip, textView.getContext(), flags);
- if (text.length() == 0) {
- return false;
- }
// First autofill it...
textView.setText(text);
// ...then move cursor to the end.
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 71edfd5..350f358 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -61,8 +61,8 @@
mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
- mInfo.dispatchingTimeout = env->GetLongField(obj,
- gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
+ mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
+ env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutNanos));
jobject tokenObj = env->GetObjectField(obj,
gInputApplicationHandleClassInfo.token);
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 81569e0..792c005 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -117,8 +117,8 @@
gInputWindowHandleClassInfo.layoutParamsFlags);
mInfo.layoutParamsType = env->GetIntField(obj,
gInputWindowHandleClassInfo.layoutParamsType);
- mInfo.dispatchingTimeout = env->GetLongField(obj,
- gInputWindowHandleClassInfo.dispatchingTimeoutNanos);
+ mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
+ env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutNanos));
mInfo.frameLeft = env->GetIntField(obj,
gInputWindowHandleClassInfo.frameLeft);
mInfo.frameTop = env->GetIntField(obj,
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index a88f891..ff336ee 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -122,10 +122,18 @@
std::stringstream ss;
ss << "HwBinder Error: (" << err << ")";
- jniThrowException(
- env,
- canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException",
- ss.str().c_str());
+ const char* exception = nullptr;
+ if (canThrowRemoteException) {
+ if (err == DEAD_OBJECT) {
+ exception = "android/os/DeadObjectException";
+ } else {
+ exception = "android/os/RemoteException";
+ }
+ } else {
+ exception = "java/lang/RuntimeException";
+ }
+
+ jniThrowException(env, exception, ss.str().c_str());
break;
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 50b5ead..097af76 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -1722,7 +1722,7 @@
// OPEN: Settings > System > Language & Region
SETTINGS_LANGUAGE_CATEGORY = 750;
- // OPEN: Settings > System > Input & Gesture > Swipe to notification gesture
+ // OPEN: Settings > System > Input & Gesture > Swipe fingerprint for notifications
SETTINGS_GESTURE_SWIPE_TO_NOTIFICATION = 751;
// OPEN: Settings > System > Input & Gesture > Double tap power button gesture
@@ -2688,4 +2688,9 @@
// CATEGORY: SETTINGS
// OS: R
MEDIA_CONTROLS_SETTINGS = 1845;
+
+ // OPEN: Settings > System > Gestures > Swipe for notification
+ // CATEGORY: SETTINGS
+ // OS: R QPR
+ SETTINGS_SWIPE_BOTTOM_TO_NOTIFICATION = 1846;
}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 7627961..edafb0c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -381,6 +381,15 @@
}
optional Notification notification = 41;
+ message OneHanded {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional OneHanded onehanded = 80;
+
message PackageVerifier {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -502,6 +511,7 @@
// parent profile.
optional SettingProto sync_parent_sounds = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto system_navigation_keys_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto swipe_bottom_to_notification_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto theme_customization_overlay_packages = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto trust_agents_initialized = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -594,16 +604,7 @@
}
optional Zen zen = 71;
- message OneHanded {
- option (android.msg_privacy).dest = DEST_EXPLICIT;
-
- optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
- }
- optional OneHanded onehanded = 80;
-
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 82;
+ // Next tag = 83;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 16797fd..0985fea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -263,6 +263,7 @@
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE" />
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 00505ca..f5d7246 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1172,7 +1172,7 @@
<string name="Midnight" msgid="8176019203622191377">"منتصف الليل"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="1532369154488982046">"اختيار الكل"</string>
+ <string name="selectAll" msgid="1532369154488982046">"تحديد الكل"</string>
<string name="cut" msgid="2561199725874745819">"قص"</string>
<string name="copy" msgid="5472512047143665218">"نسخ"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"تعذّر النسخ في الحافظة"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 99665c4..4f3d890 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1548,7 +1548,7 @@
<string name="launchBrowserDefault" msgid="6328349989932924119">"ब्राउज़र लॉन्च करें?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"कॉल स्वीकार करें?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"हमेशा"</string>
- <string name="activity_resolver_use_once" msgid="948462794469672658">"केवल एक बार"</string>
+ <string name="activity_resolver_use_once" msgid="948462794469672658">"सिर्फ़ एक बार"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s वर्क प्रोफ़ाइल का समर्थन नहीं करता"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"टैबलेट"</string>
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टीवी"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index df8e33d..e250b50 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1111,7 +1111,7 @@
<string name="inputMethod" msgid="1784759500516314751">"Киргизүү ыкмасы"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Текст боюнча иштер"</string>
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string>
- <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
+ <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> иштөөдө"</string>
<string name="app_running_notification_text" msgid="5120815883400228566">"Көбүрөөк маалымат үчүн же колдонмону токтотуш үчүн таптап коюңуз."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index a24db66..b4982eb 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1547,7 +1547,7 @@
<string name="sending" msgid="206925243621664438">"അയയ്ക്കുന്നു…"</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"ബ്രൗസർ സമാരംഭിക്കണോ?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"കോൾ സ്വീകരിക്കണോ?"</string>
- <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്പ്പോഴും"</string>
+ <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്പ്പോഴും"</string>
<string name="activity_resolver_use_once" msgid="948462794469672658">"ഒരിക്കൽ മാത്രം"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s, ഔദ്യോഗിക പ്രൊഫൈലിനെ പിന്തുണയ്ക്കുന്നില്ല"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"ടാബ്ലെറ്റ്"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 8eb8cef..be21f7b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1093,7 +1093,7 @@
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="1532369154488982046">"Hammasini belgilash"</string>
- <string name="cut" msgid="2561199725874745819">"Kesish"</string>
+ <string name="cut" msgid="2561199725874745819">"Kesib olish"</string>
<string name="copy" msgid="5472512047143665218">"Nusxa olish"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Vaqtinchalik xotiraga nusxalab bo‘lmadi"</string>
<string name="paste" msgid="461843306215520225">"Joylash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 8061ed8..33675da 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -538,7 +538,7 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"đọc vị trí từ bộ sưu tập phương tiện"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string>
- <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh đó là bạn"</string>
+ <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh danh tính của bạn"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Không có phần cứng sinh trắc học"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Đã hủy xác thực"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Không nhận dạng được"</string>
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 89cc6e7..df2946c 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -37,7 +37,6 @@
import android.app.Activity;
import android.app.Instrumentation;
import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
@@ -96,7 +95,6 @@
mMotionEvents.clear();
}
- @Presubmit
@Test
public void testCursorDrag_horizontal_whenTextViewContentsFitOnScreen() throws Throwable {
String text = "Hello world!";
@@ -145,7 +143,7 @@
// Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
// the handle as the touch moves downwards (and because we have some slop to avoid jumping
// across lines), the cursor position will end up higher than the finger position.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
// Swipe right-down along a very steep diagonal path. This should not drag the cursor.
@@ -181,7 +179,7 @@
// Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
// the handle as the touch moves downwards (and because we have some slop to avoid jumping
// across lines), the cursor position will end up higher than the finger position.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
// Swipe right-down along a very steep diagonal path. This should not drag the cursor.
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index ec75e40..35fd4bd 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -326,9 +326,9 @@
mTouchState.update(event1, mConfig);
assertSingleTap(mTouchState, 0f, 0f, 0, 0);
- // Simulate an ACTION_MOVE event that is > 30 deg from vertical.
+ // Simulate an ACTION_MOVE event that is > 45 deg from vertical.
long event2Time = 1002;
- MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f);
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f);
mTouchState.update(event2, mConfig);
assertDrag(mTouchState, 0f, 0f, 0, 0, false);
diff --git a/data/keyboards/Vendor_1532_Product_1004.kl b/data/keyboards/Vendor_1532_Product_1004.kl
new file mode 100644
index 0000000..bfbfed5
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1004.kl
@@ -0,0 +1,65 @@
+# 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 Raiju Ultimate Edition Controller with wired USB interface.
+#
+
+# 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
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# 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/data/keyboards/Vendor_1532_Product_1007.kl b/data/keyboards/Vendor_1532_Product_1007.kl
new file mode 100644
index 0000000..6f6c286
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1007.kl
@@ -0,0 +1,65 @@
+# 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 Raiju Tournament Edition Controller with wired USB interface.
+#
+
+# 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
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# 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/data/keyboards/Vendor_1532_Product_1009.kl b/data/keyboards/Vendor_1532_Product_1009.kl
new file mode 100644
index 0000000..c380d5c3
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1009.kl
@@ -0,0 +1,65 @@
+# 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 Raiju Ultimate Edition Controller with wireless Bluetooth interface.
+#
+
+# 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
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# 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/data/keyboards/Vendor_1532_Product_100a.kl b/data/keyboards/Vendor_1532_Product_100a.kl
new file mode 100644
index 0000000..b0e966d
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_100a.kl
@@ -0,0 +1,65 @@
+# 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 Raiju Tournament Edition Controller with wireless Bluetooth interface.
+#
+
+# 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
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# 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/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 32c4173..c701353 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -208,8 +208,12 @@
return config;
}
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
void EglManager::initExtensions() {
auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+ auto extensionsAndroid =
+ StringUtils::split(eglQueryStringImplementationANDROID(mEglDisplay, EGL_EXTENSIONS));
// For our purposes we don't care if EGL_BUFFER_AGE is a result of
// EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
@@ -228,9 +232,12 @@
EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
- EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync");
EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync");
EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync");
+
+ // EGL_ANDROID_native_fence_sync is not exposed to applications, so access
+ // this through the private Android-specific query instead.
+ EglExtensions.nativeFenceSync = extensionsAndroid.has("EGL_ANDROID_native_fence_sync");
}
bool EglManager::hasEglContext() {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 3b494e9..5e480a6 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,31 +24,10 @@
#include <log/log.h>
+#include <memory>
+
namespace android {
-// --- WeakLooperCallback ---
-
-class WeakLooperCallback: public LooperCallback {
-protected:
- virtual ~WeakLooperCallback() { }
-
-public:
- explicit WeakLooperCallback(const wp<LooperCallback>& callback) :
- mCallback(callback) {
- }
-
- virtual int handleEvent(int fd, int events, void* data) {
- sp<LooperCallback> callback = mCallback.promote();
- if (callback != NULL) {
- return callback->handleEvent(fd, events, data);
- }
- return 0; // the client is gone, remove the callback
- }
-
-private:
- wp<LooperCallback> mCallback;
-};
-
// --- PointerController ---
// Time to wait before starting the fade when the pointer is inactive.
@@ -64,29 +43,50 @@
// The number of events to be read at once for DisplayEventReceiver.
static const int EVENT_BUFFER_SIZE = 100;
-// --- PointerController ---
+std::shared_ptr<PointerController> PointerController::create(
+ const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController) {
+ std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
+ new PointerController(policy, looper, spriteController));
-PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
- mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
- mHandler = new WeakMessageHandler(this);
- mCallback = new WeakLooperCallback(this);
+ /*
+ * Now we need to hook up the constructed PointerController object to its callbacks.
+ *
+ * This must be executed after the constructor but before any other methods on PointerController
+ * in order to ensure that the fully constructed object is visible on the Looper thread, since
+ * that may be a different thread than where the PointerController is initially constructed.
+ *
+ * Unfortunately, this cannot be done as part of the constructor since we need to hand out
+ * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr.
+ */
- if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
- mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
- Looper::EVENT_INPUT, mCallback, nullptr);
+ 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.");
}
+ 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.presentation = Presentation::POINTER;
mLocked.presentationChanged = false;
- mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
+ mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
mLocked.pointerFadeDirection = 0;
mLocked.pointerX = 0;
@@ -221,7 +221,7 @@
removeInactivityTimeoutLocked();
// Start fading.
- if (transition == TRANSITION_IMMEDIATE) {
+ if (transition == Transition::IMMEDIATE) {
mLocked.pointerFadeDirection = 0;
mLocked.pointerAlpha = 0.0f;
updatePointerLocked();
@@ -238,7 +238,7 @@
resetInactivityTimeoutLocked();
// Start unfading.
- if (transition == TRANSITION_IMMEDIATE) {
+ if (transition == Transition::IMMEDIATE) {
mLocked.pointerFadeDirection = 0;
mLocked.pointerAlpha = 1.0f;
updatePointerLocked();
@@ -262,7 +262,7 @@
return;
}
- if (presentation == PRESENTATION_POINTER) {
+ if (presentation == Presentation::POINTER) {
if (mLocked.additionalMouseResources.empty()) {
mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
&mLocked.animationResources,
@@ -480,24 +480,35 @@
updatePointerLocked();
}
-void PointerController::handleMessage(const Message& message) {
+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:
- doInactivityTimeout();
+ controller->doInactivityTimeout();
break;
}
}
-int PointerController::handleEvent(int /* fd */, int events, void* /* data */) {
+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);
+ 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);
+ ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events);
return 1; // keep the callback
}
@@ -505,7 +516,7 @@
ssize_t n;
nsecs_t timestamp;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
- while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ 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;
@@ -514,7 +525,7 @@
}
}
if (gotVsync) {
- doAnimate(timestamp);
+ controller->doAnimate(timestamp);
}
return 1; // keep the callback
}
@@ -613,7 +624,7 @@
}
void PointerController::doInactivityTimeout() {
- fade(TRANSITION_GRADUAL);
+ fade(Transition::GRADUAL);
}
void PointerController::startAnimationLocked() {
@@ -627,8 +638,9 @@
void PointerController::resetInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
- nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
- ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
+ nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
+ ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
+ : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
}
@@ -655,7 +667,7 @@
}
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
- if (mLocked.presentation == PRESENTATION_POINTER) {
+ if (mLocked.presentation == Presentation::POINTER) {
if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) {
mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
} else {
@@ -731,7 +743,7 @@
return spot;
}
}
- return NULL;
+ return nullptr;
}
void PointerController::releaseSpotLocked(Spot* spot) {
@@ -772,7 +784,7 @@
mLocked.additionalMouseResources.clear();
mLocked.animationResources.clear();
- if (mLocked.presentation == PRESENTATION_POINTER) {
+ if (mLocked.presentation == Presentation::POINTER) {
mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
&mLocked.animationResources, mLocked.viewport.displayId);
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index ebc622b..14c0679 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -17,19 +17,20 @@
#ifndef _UI_POINTER_CONTROLLER_H
#define _UI_POINTER_CONTROLLER_H
-#include "SpriteController.h"
-
-#include <map>
-#include <vector>
-
-#include <ui/DisplayInfo.h>
+#include <PointerControllerInterface.h>
+#include <gui/DisplayEventReceiver.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
-#include <PointerControllerInterface.h>
+#include <ui/DisplayInfo.h>
#include <utils/BitSet.h>
-#include <utils/RefBase.h>
#include <utils/Looper.h>
-#include <gui/DisplayEventReceiver.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "SpriteController.h"
namespace android {
@@ -70,25 +71,22 @@
virtual int32_t getCustomPointerIconId() = 0;
};
-
/*
* Tracks pointer movements and draws the pointer sprite to a surface.
*
* Handles pointer acceleration and animation.
*/
-class PointerController : public PointerControllerInterface, public MessageHandler,
- public LooperCallback {
-protected:
- virtual ~PointerController();
-
+class PointerController : public PointerControllerInterface {
public:
- enum InactivityTimeout {
- INACTIVITY_TIMEOUT_NORMAL = 0,
- INACTIVITY_TIMEOUT_SHORT = 1,
+ 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,
};
- PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, const sp<SpriteController>& spriteController);
+ virtual ~PointerController();
virtual bool getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const;
@@ -113,8 +111,8 @@
void reloadPointerResources();
private:
- static const size_t MAX_RECYCLED_SPRITES = 12;
- static const size_t MAX_SPOTS = 12;
+ static constexpr size_t MAX_RECYCLED_SPRITES = 12;
+ static constexpr size_t MAX_SPOTS = 12;
enum {
MSG_INACTIVITY_TIMEOUT,
@@ -130,8 +128,13 @@
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(NULL) { }
+ : 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);
@@ -139,12 +142,24 @@
const SpriteIcon* lastIcon;
};
+ class MessageHandler : public virtual android::MessageHandler {
+ public:
+ 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;
+ };
+
mutable Mutex mLock;
sp<PointerControllerPolicyInterface> mPolicy;
sp<Looper> mLooper;
sp<SpriteController> mSpriteController;
- sp<WeakMessageHandler> mHandler;
+ sp<MessageHandler> mHandler;
sp<LooperCallback> mCallback;
DisplayEventReceiver mDisplayEventReceiver;
@@ -181,14 +196,15 @@
int32_t buttonState;
std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
- std::vector<sp<Sprite> > recycledSprites;
+ std::vector<sp<Sprite>> recycledSprites;
} 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 handleMessage(const Message& message);
- int handleEvent(int fd, int events, void* data);
void doAnimate(nsecs_t timestamp);
bool doFadingAnimationLocked(nsecs_t timestamp);
bool doBitmapAnimationLocked(nsecs_t timestamp);
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index a157426..6e129a0 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -136,7 +136,7 @@
sp<MockSprite> mPointerSprite;
sp<MockPointerControllerPolicyInterface> mPolicy;
sp<MockSpriteController> mSpriteController;
- sp<PointerController> mPointerController;
+ std::shared_ptr<PointerController> mPointerController;
private:
void loopThread();
@@ -160,7 +160,7 @@
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+ mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
}
PointerControllerTest::~PointerControllerTest() {
@@ -193,7 +193,7 @@
TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
EXPECT_CALL(*mPointerSprite, setVisible(true));
@@ -208,7 +208,7 @@
TEST_F(PointerControllerTest, updatePointerIcon) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
int32_t type = CURSOR_TYPE_ADDITIONAL;
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
@@ -224,7 +224,7 @@
TEST_F(PointerControllerTest, setCustomPointerIcon) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
int32_t style = CURSOR_TYPE_CUSTOM;
float hotSpotX = 15;
@@ -246,13 +246,13 @@
}
TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
- mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+ 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);
- mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
+ mPointerController->fade(PointerController::Transition::IMMEDIATE);
EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
diff --git a/media/java/android/media/MediaFrameworkInitializer.java b/media/java/android/media/MediaFrameworkInitializer.java
new file mode 100644
index 0000000..577442e
--- /dev/null
+++ b/media/java/android/media/MediaFrameworkInitializer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.media.session.MediaSessionManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Class for performing registration for all media services
+ *
+ * TODO (b/160513103): Move this class when moving media service code to APEX
+ * @hide
+ */
+public class MediaFrameworkInitializer {
+ private MediaFrameworkInitializer() {
+ }
+
+ private static volatile MediaServiceManager sMediaServiceManager;
+
+ /**
+ * Sets an instance of {@link MediaServiceManager} that allows
+ * the media mainline module to register/obtain media binder services. This is called
+ * by the platform during the system initialization.
+ *
+ * @param mediaServiceManager instance of {@link MediaServiceManager} that allows
+ * the media mainline module to register/obtain media binder services.
+ */
+ public static void setMediaServiceManager(
+ @NonNull MediaServiceManager mediaServiceManager) {
+ Preconditions.checkState(sMediaServiceManager == null,
+ "setMediaServiceManager called twice!");
+ sMediaServiceManager = Objects.requireNonNull(mediaServiceManager);
+ }
+
+ /** @hide */
+ public static MediaServiceManager getMediaServiceManager() {
+ return sMediaServiceManager;
+ }
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all media
+ * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides
+ * {@link SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.MEDIA_SESSION_SERVICE,
+ MediaSessionManager.class,
+ context -> new MediaSessionManager(context)
+ );
+ }
+}
diff --git a/media/java/android/media/MediaServiceManager.java b/media/java/android/media/MediaServiceManager.java
new file mode 100644
index 0000000..21e2d84
--- /dev/null
+++ b/media/java/android/media/MediaServiceManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the media
+ * service.
+ *
+ * <p> Only the media mainline module will be able to access an instance of this class.
+ * @hide
+ */
+public class MediaServiceManager {
+ /**
+ * @hide
+ */
+ public MediaServiceManager() {}
+
+ /**
+ * A class that exposes the methods to register and obtain each system service.
+ */
+ public static final class ServiceRegisterer {
+ private final String mServiceName;
+
+ /**
+ * @hide
+ */
+ public ServiceRegisterer(String serviceName) {
+ mServiceName = serviceName;
+ }
+
+ /**
+ * Get the system server binding object for MediaServiceManager.
+ *
+ * <p> This blocks until the service instance is ready.
+ * or a timeout happens, in which case it returns null.
+ */
+ @Nullable
+ public IBinder get() {
+ return ServiceManager.getService(mServiceName);
+ }
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "media_session" service.
+ */
+ @NonNull
+ public ServiceRegisterer getMediaSessionServiceRegisterer() {
+ return new ServiceRegisterer("media_session");
+ }
+}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 2fd721e..6976a35 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -28,14 +28,13 @@
import android.content.pm.ParceledListSlice;
import android.media.AudioManager;
import android.media.IRemoteVolumeController;
+import android.media.MediaFrameworkInitializer;
import android.media.MediaSession2;
import android.media.Session2Token;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.media.MediaBrowserService;
import android.service.notification.NotificationListenerService;
@@ -115,8 +114,10 @@
// Consider rewriting like DisplayManagerGlobal
// Decide if we need context
mContext = context;
- IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE);
- mService = ISessionManager.Stub.asInterface(b);
+ mService = ISessionManager.Stub.asInterface(MediaFrameworkInitializer
+ .getMediaServiceManager()
+ .getMediaSessionServiceRegisterer()
+ .get());
}
/**
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
new file mode 100644
index 0000000..ee06720
--- /dev/null
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediatranscodingtest;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.MediaFormat;
+import android.media.MediaTranscodeManager;
+import android.media.MediaTranscodeManager.TranscodingJob;
+import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/*
+ * Benchmarking for MediaTranscodeManager in the media framework.
+ *
+ * Note: This benchmarking requires to push all the files from http://go/transcodingbenchmark
+ * to /data/user/0/com.android.mediatranscodingtest/cache/ directory after installing the apk.
+ *
+ * TODO(hkuang): Change it to download from server automatically instead of manually.
+ *
+ * To run this test suite:
+ make frameworks/base/media/tests/MediaTranscodingTest
+ make mediatranscodingtest
+
+ adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
+ // Push the files to /data/user/0/com.android.mediatranscodingtest/cache/
+ adb push $DOWNLOADPATH/*.mp4 /data/user/0/com.android.mediatranscodingtest/cache/
+
+ adb shell am instrument -e class \
+ com.android.mediatranscodingtest.MediaTranscodingBenchmark \
+ -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+ *
+ */
+public class MediaTranscodingBenchmark
+ extends ActivityInstrumentationTestCase2<MediaTranscodingTest> {
+ private static final String TAG = "MediaTranscodingBenchmark";
+ // TODO(hkuang): Change this to query from MediaCodecInfo.CodecCapabilities for different
+ // resolution.
+ private static final int MINIMUM_TRANSCODING_FPS = 80;
+ private static final int LOOP_COUNT = 10;
+ // Default Setting for transcoding to H.264.
+ private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private static final int BIT_RATE = 20000000; // 20Mbps
+ private static final int WIDTH = 1920;
+ private static final int HEIGHT = 1080;
+ private Context mContext;
+ private MediaTranscodeManager mMediaTranscodeManager = null;
+
+ public MediaTranscodingBenchmark() {
+ super("com.android.MediaTranscodingBenchmark", MediaTranscodingTest.class);
+ }
+
+ /**
+ * Creates a MediaFormat with the basic set of values.
+ */
+ private static MediaFormat createMediaFormat() {
+ MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+ return format;
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ Log.d(TAG, "setUp");
+ super.setUp();
+ mContext = getInstrumentation().getContext();
+ mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /*
+ * Transcode the sourceFileName to destinationFileName with LOOP_COUNT.
+ */
+ private void transcode(final String sourceFileName, final String destinationFileName)
+ throws IOException, InterruptedException {
+ AtomicLong totalTimeMs = new AtomicLong();
+ AtomicLong transcodingTime = new AtomicLong();
+ Uri srcUri = getUri(sourceFileName);
+ Uri dstUri = getUri(destinationFileName);
+
+ MediaTranscodingTestUtil.VideoFileInfo info =
+ MediaTranscodingTestUtil.extractVideoFileInfo(mContext, getUri(sourceFileName));
+ int timeoutSeconds = calMaxTranscodingWaitTimeSeconds(info.mNumVideoFrames,
+ MINIMUM_TRANSCODING_FPS);
+ Log.d(TAG, "Start Transcoding " + info.toString() + " " + timeoutSeconds);
+
+ for (int loop = 0; loop < LOOP_COUNT; ++loop) {
+ Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(srcUri)
+ .setDestinationUri(dstUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+ long startTimeMs = System.currentTimeMillis();
+ TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+ transcodingJob -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + transcodingJob.getResult());
+ assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
+ transcodeCompleteSemaphore.release();
+ transcodingTime.set(System.currentTimeMillis() - startTimeMs);
+ totalTimeMs.addAndGet(transcodingTime.get());
+ });
+
+ if (job != null) {
+ Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
+ boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+ timeoutSeconds, TimeUnit.SECONDS);
+ assertTrue("Transcode failed to complete in time.", finishedOnTime);
+ }
+ Log.i(TAG, "Loop: " + loop + " take " + transcodingTime.get() + " ms ");
+ }
+
+ float fps = info.mNumVideoFrames * 1000 * LOOP_COUNT / totalTimeMs.get();
+ Log.i(TAG, "Transcoding " + info.toString() + " Transcoding fps: " + fps);
+ }
+
+ // Calculate the maximum wait time based on minimum transcoding throughput and frame number.
+ private int calMaxTranscodingWaitTimeSeconds(int numberFrames, int minTranscodingFps) {
+ int waitTimeSeconds = numberFrames / minTranscodingFps;
+ // If waitTimeSeconds is 0, wait for 1 seconds at least.
+ return waitTimeSeconds == 0 ? 1 : waitTimeSeconds;
+ }
+
+ private Uri getUri(final String fileName) {
+ String path = mContext.getCacheDir().getAbsolutePath();
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_FILE).appendPath(path).appendPath(
+ fileName).build();
+ }
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith66FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith66FramesWithAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps_aac";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
index 3b044c7..53b2392 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
@@ -38,6 +38,7 @@
TestSuite suite = new InstrumentationTestSuite(this);
suite.addTestSuite(MediaTranscodeManagerTest.class);
suite.addTestSuite(MediaTranscodeManagerWithMockServiceTest.class);
+ suite.addTestSuite(MediaTranscodingBenchmark.class);
return suite;
}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
index a1c3251..69f124f 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
@@ -27,6 +27,7 @@
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.util.Log;
import android.util.Size;
@@ -42,6 +43,75 @@
/* package */ class MediaTranscodingTestUtil {
private static final String TAG = "MediaTranscodingTestUtil";
+ // Helper class to extract the information from source file and transcoded file.
+ static class VideoFileInfo {
+ String mUri;
+ int mNumVideoFrames = 0;
+ int mWidth = 0;
+ int mHeight = 0;
+ float mVideoFrameRate = 0.0f;
+ boolean mHasAudio = false;
+
+ public String toString() {
+ String str = mUri;
+ str += " Width:" + mWidth;
+ str += " Height:" + mHeight;
+ str += " FrameRate:" + mWidth;
+ str += " FrameCount:" + mNumVideoFrames;
+ str += " HasAudio:" + (mHasAudio ? "Yes" : "No");
+ return str;
+ }
+ }
+
+ static VideoFileInfo extractVideoFileInfo(Context ctx, Uri videoUri) throws IOException {
+ VideoFileInfo info = new VideoFileInfo();
+ AssetFileDescriptor afd = null;
+ MediaMetadataRetriever retriever = null;
+
+ try {
+ afd = ctx.getContentResolver().openAssetFileDescriptor(videoUri, "r");
+ retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+
+ info.mUri = videoUri.getLastPathSegment();
+ Log.i(TAG, "Trying to transcode to " + info.mUri);
+ String width = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+ String height = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+ if (width != null && height != null) {
+ info.mWidth = Integer.parseInt(width);
+ info.mHeight = Integer.parseInt(height);
+ }
+
+ String frameRate = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE);
+ if (frameRate != null) {
+ info.mVideoFrameRate = Float.parseFloat(frameRate);
+ }
+
+ String frameCount = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT);
+ if (frameCount != null) {
+ info.mNumVideoFrames = Integer.parseInt(frameCount);
+ }
+
+ String hasAudio = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
+ if (hasAudio != null) {
+ info.mHasAudio = hasAudio.equals("yes");
+ }
+ } finally {
+ if (retriever != null) {
+ retriever.close();
+ }
+ if (afd != null) {
+ afd.close();
+ }
+ }
+ return info;
+ }
+
static VideoTranscodingStatistics computeStats(final Context ctx, final Uri sourceMp4,
final Uri transcodedMp4)
throws Exception {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index e3aceec..b693b0e 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -3614,8 +3614,13 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+ field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START";
+ field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+ field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
field public static final long FUNCTION_ADB = 1L; // 0x1L
field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
index 1782d25..5aab0a1 100644
--- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
+++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
@@ -29,13 +29,12 @@
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/>
- <!-- Include a FocusParkingView at the beginning or end. The rotary controller "parks" the
- focus here when the user navigates to another window. This is also used to prevent
- wrap-around which is why it must be first or last in Tab order. -->
+ <!-- Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+ when the user navigates to another window. This is also used to prevent wrap-around. -->
<com.android.car.ui.FocusParkingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<View
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
index d60bc41..adf8d4d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
@@ -148,10 +148,9 @@
CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
/* root= */ null);
- // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when
- // the user navigates to another window. This is also used to prevent wrap-around which is
- // why it must be first or last in Tab order.
- view.addView(new FocusParkingView(mContext));
+ // Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+ // when the user navigates to another window. This is also used to prevent wrap-around.
+ view.addView(new FocusParkingView(mContext), 0);
mCachedViewMap.put(type, view);
return mCachedViewMap.get(type);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
index a423003..13f2b7e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
@@ -23,6 +23,7 @@
import com.android.systemui.car.window.SystemUIOverlayWindowController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -49,12 +50,14 @@
DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController,
SysuiColorExtractor colorExtractor,
DumpManager dumpManager,
SystemUIOverlayWindowController overlayWindowController) {
super(context, windowManager, activityManager, dozeParameters, statusBarStateController,
- configurationController, keyguardBypassController, colorExtractor, dumpManager);
+ configurationController, keyguardViewMediator, keyguardBypassController,
+ colorExtractor, dumpManager);
mOverlayWindowController = overlayWindowController;
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index abdfad5..88b181f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -276,21 +276,23 @@
intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString());
intent.setData(Uri.fromParts("printjob", printJobId.flattenToString(), null));
}
- return PendingIntent.getActivity(mContext, 0, intent, 0);
+ return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
private PendingIntent createCancelIntent(PrintJobInfo printJob) {
Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + printJob.getId().flattenToString());
intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId());
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ return PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
}
private PendingIntent createRestartIntent(PrintJobId printJobId) {
Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + printJobId.flattenToString());
intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId);
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ return PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
}
private static boolean shouldNotifyForState(int state) {
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index 3565b0e..e829974 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -16,9 +16,6 @@
package com.android.settingslib.widget;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_DEFAULT;
-
import android.annotation.ColorInt;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -48,11 +45,12 @@
private static final float ADVANCED_ICON_CENTER = 50f;
private static final float ADVANCED_ICON_RADIUS = 48f;
+ public static final int ICON_TYPE_DEFAULT = 0;
+ public static final int ICON_TYPE_ADVANCED = 1;
+
@Retention(RetentionPolicy.SOURCE)
- @IntDef({TYPE_DEFAULT, TYPE_ADVANCED})
+ @IntDef({ICON_TYPE_DEFAULT, ICON_TYPE_ADVANCED})
public @interface AdaptiveOutlineIconType {
- int TYPE_DEFAULT = 0;
- int TYPE_ADVANCED = 1;
}
@VisibleForTesting
@@ -66,7 +64,7 @@
public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
super(new AdaptiveIconShapeDrawable(resources));
- init(resources, bitmap, TYPE_DEFAULT);
+ init(resources, bitmap, ICON_TYPE_DEFAULT);
}
public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap,
@@ -96,10 +94,10 @@
private @ColorInt int getColor(Resources resources, @AdaptiveOutlineIconType int type) {
int resId;
switch (type) {
- case TYPE_ADVANCED:
+ case ICON_TYPE_ADVANCED:
resId = R.color.advanced_outline_color;
break;
- case TYPE_DEFAULT:
+ case ICON_TYPE_DEFAULT:
default:
resId = R.color.bt_outline_color;
break;
@@ -110,10 +108,10 @@
private int getDimensionPixelSize(Resources resources, @AdaptiveOutlineIconType int type) {
int resId;
switch (type) {
- case TYPE_ADVANCED:
+ case ICON_TYPE_ADVANCED:
resId = R.dimen.advanced_dashboard_tile_foreground_image_inset;
break;
- case TYPE_DEFAULT:
+ case ICON_TYPE_DEFAULT:
default:
resId = R.dimen.dashboard_tile_foreground_image_inset;
break;
@@ -133,7 +131,7 @@
final int count = canvas.save();
canvas.scale(scaleX, scaleY);
// Draw outline
- if (mType == TYPE_DEFAULT) {
+ if (mType == ICON_TYPE_DEFAULT) {
canvas.drawPath(mPath, mOutlinePaint);
} else {
canvas.drawCircle(ADVANCED_ICON_CENTER, ADVANCED_ICON_CENTER, ADVANCED_ICON_RADIUS,
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c27973f..287a1ac 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -22,7 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"No se pueden buscar las redes."</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ninguna"</string>
- <string name="wifi_remembered" msgid="3266709779723179188">"Guardada"</string>
+ <string name="wifi_remembered" msgid="3266709779723179188">"Guardado"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitada"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Error de configuración IP"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 3e8b1c1..f9d57c4 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de rede"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 08cf5f9..8407db6 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -211,8 +211,8 @@
<string name="adb_wireless_error" msgid="721958772149779856">"Алдаа"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Wireless debugging"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Боломжтой төхөөрөмжүүдийг харах болох ашиглахын тулд wireless debugging-г асаана уу"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Хурдан хариу үйлдлийн кодоор төхөөрөмжийг хослуул"</string>
- <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Хурдан хариу үйлдлийн кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR кодоор төхөөрөмжийг хослуул"</string>
+ <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"Хослуулах кодоор төхөөрөмжийг хослуулна уу"</string>
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Зургаан оронтой кодыг ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"Хослуулсан төхөөрөмжүүд"</string>
@@ -226,12 +226,12 @@
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi хослуулах код"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Хослуулалт амжилтгүй боллоо"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Төхөөрөмжийг ижил сүлжээнд холбосон эсэхийг шалгана уу."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Төхөөрөмжийг хослуулж байна…"</string>
- <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. Хурдан хариу үйлдлийн код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string>
+ <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. QR код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP хаяг ба порт"</string>
- <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Хурдан хариу үйлдлийн кодыг скан хийх"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
+ <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодыг скан хийх"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi сүлжээнд холбогдоно уу"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, дебаг хийх, dev"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Алдаа мэдээлэх товчлол"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index e33df4a..32cc39e 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string>
<string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via netwerkbeoordelaar"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 95e916b..68f7289 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,6 +1,6 @@
package com.android.settingslib.bluetooth;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED;
+import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -238,7 +238,7 @@
final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
iconSize, false);
bitmap.recycle();
- return new AdaptiveOutlineDrawable(resources, resizedBitmap, TYPE_ADVANCED);
+ return new AdaptiveOutlineDrawable(resources, resizedBitmap, ICON_TYPE_ADVANCED);
}
return drawable;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
index 278b57d..e5ea446 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -41,7 +41,7 @@
private final Bitmap mBitmap;
private final int mSize;
- private final Paint mPaint;
+ private Paint mIconPaint;
private float mScale;
private Rect mSrcRect;
@@ -75,18 +75,18 @@
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
// opaque circle matte
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setColor(Color.BLACK);
- mPaint.setStyle(Paint.Style.FILL);
- canvas.drawPath(fillPath, mPaint);
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setColor(Color.BLACK);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawPath(fillPath, paint);
// mask in the icon where the bitmap is opaque
- mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(icon, cropRect, circleRect, paint);
// prepare paint for frame drawing
- mPaint.setXfermode(null);
+ paint.setXfermode(null);
mScale = 1f;
@@ -100,7 +100,7 @@
final float pad = (mSize - inside) / 2f;
mDstRect.set(pad, pad, mSize - pad, mSize - pad);
- canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+ canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, mIconPaint);
}
public void setScale(float scale) {
@@ -122,8 +122,12 @@
@Override
public void setColorFilter(ColorFilter cf) {
+ if (mIconPaint == null) {
+ mIconPaint = new Paint();
+ }
+ mIconPaint.setColorFilter(cf);
}
-
+
@Override
public int getIntrinsicWidth() {
return mSize;
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2da84c2..482a381 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -172,5 +172,6 @@
Settings.Secure.ONE_HANDED_MODE_ENABLED,
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
Settings.Secure.TAPS_APP_TO_EXIT,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 3fd543d..b337e60 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -259,5 +259,6 @@
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a946888..a994fda 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2573,6 +2573,10 @@
Settings.Secure.TAPS_APP_TO_EXIT,
SecureSettingsProto.OneHanded.TAPS_APP_TO_EXIT);
+ dumpSetting(s, p,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+ SecureSettingsProto.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
+
// Please insert new settings using the same order as in SecureSettingsProto.
p.end(token);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index ee2d001..9d3620f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -25,6 +25,7 @@
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.util.Log;
@@ -62,10 +63,12 @@
public ThumbnailData(TaskSnapshot snapshot) {
final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- if (buffer != null && (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+ if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
// TODO(b/157562905): Workaround for a crash when we get a snapshot without this state
- Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE");
- thumbnail = Bitmap.createBitmap(buffer.getWidth(), buffer.getHeight(), ARGB_8888);
+ Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: "
+ + buffer);
+ Point taskSize = snapshot.getTaskSize();
+ thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888);
thumbnail.eraseColor(Color.BLACK);
} else {
thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index b966f93..255fffd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -114,6 +114,11 @@
t.deferTransactionUntil(surfaceControl, barrier, frameNumber);
}
+ public static void setRelativeLayer(Transaction t, SurfaceControl surfaceControl,
+ SurfaceControl relativeTo, int z) {
+ t.setRelativeLayer(surfaceControl, relativeTo, z);
+ }
+
@Deprecated
public static void setEarlyWakeup(Transaction t) {
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 10dcbd6..a84664c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -448,8 +448,8 @@
if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ " UpdateSim.onSimCheckResponse: "
+ " attemptsRemaining=" + result.getAttemptsRemaining());
- mStateMachine.reset();
}
+ mStateMachine.reset();
mCheckSimPukThread = null;
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 127c5dd..e12b7dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -5,6 +5,7 @@
import android.content.res.Configuration
import android.graphics.Color
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
+import android.util.Log
import android.util.MathUtils
import android.view.LayoutInflater
import android.view.View
@@ -25,6 +26,7 @@
import javax.inject.Provider
import javax.inject.Singleton
+private const val TAG = "MediaCarouselController"
private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
/**
@@ -236,7 +238,9 @@
val oldData = mediaPlayers[oldKey]
if (oldData != null) {
val oldData = mediaPlayers.remove(oldKey)
- mediaPlayers.put(key, oldData!!)
+ mediaPlayers.put(key, oldData!!)?.let {
+ Log.wtf(TAG, "new key $key already exists when migrating from $oldKey")
+ }
}
var existingPlayer = mediaPlayers[key]
if (existingPlayer == null) {
@@ -271,6 +275,11 @@
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
mediaCarousel.requiresRemeasuring = true
+ // Check postcondition: mediaContent should have the same number of children as there are
+ // elements in mediaPlayers.
+ if (mediaPlayers.size != mediaContent.childCount) {
+ Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
+ }
}
private fun removePlayer(key: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 8cb93bf..299ae5b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -517,22 +517,36 @@
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
- if (useMediaResumption && mediaEntries.get(key)?.resumeAction != null) {
+ val removed = mediaEntries.remove(key)
+ if (useMediaResumption && removed?.resumeAction != null) {
Log.d(TAG, "Not removing $key because resumable")
- // Move to resume key aka package name
- val data = mediaEntries.remove(key)!!
- val resumeAction = getResumeMediaAction(data.resumeAction!!)
- val updated = data.copy(token = null, actions = listOf(resumeAction),
+ // Move to resume key (aka package name) if that key doesn't already exist.
+ val resumeAction = getResumeMediaAction(removed.resumeAction!!)
+ val updated = removed.copy(token = null, actions = listOf(resumeAction),
actionsToShowInCompact = listOf(0), active = false, resumption = true)
- mediaEntries.put(data.packageName, updated)
- // Notify listeners of "new" controls
+ val pkg = removed?.packageName
+ val migrate = mediaEntries.put(pkg, updated) == null
+ // Notify listeners of "new" controls when migrating or removed and update when not
val listenersCopy = listeners.toSet()
- listenersCopy.forEach {
- it.onMediaDataLoaded(data.packageName, key, updated)
+ if (migrate) {
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(pkg, key, updated)
+ }
+ } else {
+ // Since packageName is used for the key of the resumption controls, it is
+ // possible that another notification has already been reused for the resumption
+ // controls of this package. In this case, rather than renaming this player as
+ // packageName, just remove it and then send a update to the existing resumption
+ // controls.
+ listenersCopy.forEach {
+ it.onMediaDataRemoved(key)
+ }
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(pkg, pkg, updated)
+ }
}
return
}
- val removed = mediaEntries.remove(key)
if (removed != null) {
val listenersCopy = listeners.toSet()
listenersCopy.forEach {
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
index 5d9439f..c581ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
@@ -16,6 +16,8 @@
package com.android.systemui.onehanded;
+import android.view.SurfaceControl;
+
/**
* Additional callback interface for OneHanded animation
*/
@@ -30,7 +32,7 @@
/**
* Called when OneHanded animation is ended.
*/
- default void onOneHandedAnimationEnd(
+ default void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
index 08f08c5..20ab114 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
@@ -29,6 +29,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
import javax.inject.Inject;
@@ -39,18 +40,6 @@
private static final float FRACTION_START = 0f;
private static final float FRACTION_END = 1f;
- public static final int ANIM_TYPE_TRANSLATE = 0;
- public static final int ANIM_TYPE_SCALE = 1;
-
- // Note: ANIM_TYPE_SCALE reserve for the future development
- @IntDef(prefix = {"ANIM_TYPE_"}, value = {
- ANIM_TYPE_TRANSLATE,
- ANIM_TYPE_SCALE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AnimationType {
- }
-
public static final int TRANSITION_DIRECTION_NONE = 0;
public static final int TRANSITION_DIRECTION_TRIGGER = 1;
public static final int TRANSITION_DIRECTION_EXIT = 2;
@@ -66,7 +55,8 @@
private final Interpolator mFastOutSlowInInterpolator;
private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
- private OneHandedTransitionAnimator mCurrentAnimator;
+ private final HashMap<SurfaceControl, OneHandedTransitionAnimator> mAnimatorMap =
+ new HashMap<>();
/**
* Constructor of OneHandedAnimationController
@@ -82,18 +72,33 @@
@SuppressWarnings("unchecked")
OneHandedTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds,
Rect endBounds) {
- if (mCurrentAnimator == null) {
- mCurrentAnimator = setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds));
- } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_TRANSLATE
- && mCurrentAnimator.isRunning()) {
- mCurrentAnimator.updateEndValue(endBounds);
+ final OneHandedTransitionAnimator animator = mAnimatorMap.get(leash);
+ if (animator == null) {
+ mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
+ OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
+ } else if (animator.isRunning()) {
+ animator.updateEndValue(endBounds);
} else {
- mCurrentAnimator.cancel();
- mCurrentAnimator = setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+ animator.cancel();
+ mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
+ OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
}
- return mCurrentAnimator;
+ return mAnimatorMap.get(leash);
+ }
+
+ HashMap<SurfaceControl, OneHandedTransitionAnimator> getAnimatorMap() {
+ return mAnimatorMap;
+ }
+
+ boolean isAnimatorsConsumed() {
+ return mAnimatorMap.isEmpty();
+ }
+
+ void removeAnimator(SurfaceControl key) {
+ final OneHandedTransitionAnimator animator = mAnimatorMap.remove(key);
+ if (animator != null && animator.isRunning()) {
+ animator.cancel();
+ }
}
OneHandedTransitionAnimator setupOneHandedTransitionAnimator(
@@ -114,7 +119,6 @@
ValueAnimator.AnimatorListener {
private final SurfaceControl mLeash;
- private final @AnimationType int mAnimationType;
private T mStartValue;
private T mEndValue;
private T mCurrentValue;
@@ -127,10 +131,8 @@
private @TransitionDirection int mTransitionDirection;
private int mTransitionOffset;
- private OneHandedTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
- T startValue, T endValue) {
+ private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) {
mLeash = leash;
- mAnimationType = animationType;
mStartValue = startValue;
mEndValue = endValue;
addListener(this);
@@ -150,8 +152,10 @@
@Override
public void onAnimationEnd(Animator animation) {
mCurrentValue = mEndValue;
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ onEndTransaction(mLeash, tx);
if (mOneHandedAnimationCallback != null) {
- mOneHandedAnimationCallback.onOneHandedAnimationEnd(this);
+ mOneHandedAnimationCallback.onOneHandedAnimationEnd(tx, this);
}
}
@@ -196,6 +200,10 @@
return this;
}
+ SurfaceControl getLeash() {
+ return mLeash;
+ }
+
Rect getDestinationBounds() {
return (Rect) mEndValue;
}
@@ -227,11 +235,6 @@
return mEndValue;
}
- @AnimationType
- int getAnimationType() {
- return mAnimationType;
- }
-
void setCurrentValue(T value) {
mCurrentValue = value;
}
@@ -250,11 +253,9 @@
@VisibleForTesting
static OneHandedTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Rect startValue, Rect endValue) {
- // At R, we only support translate type first.
- final int animType = ANIM_TYPE_TRANSLATE;
- return new OneHandedTransitionAnimator<Rect>(leash, animType,
- new Rect(startValue), new Rect(endValue)) {
+ return new OneHandedTransitionAnimator<Rect>(leash, new Rect(startValue),
+ new Rect(endValue)) {
private final Rect mTmpRect = new Rect();
@@ -282,7 +283,7 @@
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
getSurfaceTransactionHelper()
.alpha(tx, leash, 1f)
- .crop(tx, leash, getStartValue())
+ .translate(tx, leash, getEndValue().top - getStartValue().top)
.round(tx, leash);
tx.apply();
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index 063d7c8..28d7049 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -22,11 +22,14 @@
import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
@@ -39,10 +42,12 @@
import com.android.internal.os.SomeArgs;
import com.android.systemui.Dumpable;
import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayLayout;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -68,22 +73,22 @@
@VisibleForTesting
static final int MSG_OFFSET_FINISH = 3;
- private final Handler mUpdateHandler;
+ private final Rect mLastVisualDisplayBounds = new Rect();
+ private final Rect mDefaultDisplayBounds = new Rect();
+ private Handler mUpdateHandler;
private boolean mIsInOneHanded;
private int mEnterExitAnimationDurationMs;
@VisibleForTesting
- DisplayAreaInfo mDisplayAreaInfo;
+ HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
private DisplayController mDisplayController;
+ private DisplayLayout mDisplayLayout;
+ private DisplayInfo mDisplayInfo = new DisplayInfo();
private OneHandedAnimationController mAnimationController;
- private SurfaceControl mLeash;
- private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
- private OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
- private Rect mLastVisualDisplayBounds;
- private Rect mDefaultDisplayBounds;
+ private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
@VisibleForTesting
OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -94,17 +99,23 @@
}
@Override
- public void onOneHandedAnimationEnd(
+ public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
- obtainArgsFromAnimator(animator)));
+ mAnimationController.removeAnimator(animator.getLeash());
+ if (mAnimationController.isAnimatorsConsumed()) {
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
+ obtainArgsFromAnimator(animator)));
+ }
}
@Override
public void onOneHandedAnimationCancel(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
- obtainArgsFromAnimator(animator)));
+ mAnimationController.removeAnimator(animator.getLeash());
+ if (mAnimationController.isAnimatorsConsumed()) {
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
+ obtainArgsFromAnimator(animator)));
+ }
}
};
@@ -112,34 +123,24 @@
private Handler.Callback mUpdateCallback = (msg) -> {
SomeArgs args = (SomeArgs) msg.obj;
final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds;
- final int xOffset = args.argi1;
final int yOffset = args.argi2;
final int direction = args.argi3;
- if (mLeash == null || !mLeash.isValid()) {
- Log.w(TAG, "mLeash is null, abort transition");
- return false;
- }
switch (msg.what) {
case MSG_RESET_IMMEDIATE:
- final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(mLeash, currentBounds,
- mDefaultDisplayBounds);
- if (animator != null && animator.isRunning()) {
- animator.cancel();
- }
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- tx.setPosition(mLeash, 0, 0)
- .setWindowCrop(mLeash, -1/* reset */, -1/* reset */)
- .apply();
- finishOffset(currentBounds, yOffset, direction);
+ resetWindowsOffset();
+ mLastVisualDisplayBounds.set(currentBounds);
+ finishOffset(0, TRANSITION_DIRECTION_EXIT);
break;
case MSG_OFFSET_ANIMATE:
- offsetWindows(currentBounds, 0, yOffset, direction, mEnterExitAnimationDurationMs);
+ final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
+ mDefaultDisplayBounds.top + yOffset,
+ mDefaultDisplayBounds.right,
+ mDefaultDisplayBounds.bottom + yOffset);
+ offsetWindows(currentBounds, toBounds, direction, mEnterExitAnimationDurationMs);
break;
case MSG_OFFSET_FINISH:
- finishOffset(currentBounds, yOffset, direction);
+ finishOffset(yOffset, direction);
break;
}
args.recycle();
@@ -152,17 +153,15 @@
@Inject
public OneHandedDisplayAreaOrganizer(Context context,
DisplayController displayController,
- OneHandedAnimationController animationController,
- OneHandedSurfaceTransactionHelper surfaceTransactionHelper) {
-
+ OneHandedAnimationController animationController) {
mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
mAnimationController = animationController;
mDisplayController = displayController;
+ mDisplayLayout = getDisplayLayout(context);
+ mDisplayLayout.getStableBounds(mDefaultDisplayBounds);
+ mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
mEnterExitAnimationDurationMs = context.getResources().getInteger(
com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
- mDefaultDisplayBounds = new Rect(0, 0, getDisplayBounds().x, getDisplayBounds().y);
- mLastVisualDisplayBounds = new Rect(mDefaultDisplayBounds);
- mSurfaceTransactionHelper = surfaceTransactionHelper;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
}
@@ -171,40 +170,52 @@
@NonNull SurfaceControl leash) {
Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null");
Objects.requireNonNull(leash, "leash must not be null");
- mDisplayAreaInfo = displayAreaInfo;
- mLeash = leash;
- }
- @Override
- public void onDisplayAreaInfoChanged(@NonNull DisplayAreaInfo displayAreaInfo) {
- Objects.requireNonNull(displayAreaInfo, "displayArea must not be null");
- // Stop one handed without animation and reset cropped size immediately
- if (displayAreaInfo.configuration.orientation
- != mDisplayAreaInfo.configuration.orientation) {
- final Rect newBounds = displayAreaInfo.configuration.windowConfiguration.getBounds();
- resetImmediately(newBounds);
+ if (displayAreaInfo.featureId != FEATURE_ONE_HANDED) {
+ Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
+ return;
}
- mDisplayAreaInfo = displayAreaInfo;
+ mDisplayAreaMap.put(displayAreaInfo, leash);
}
@Override
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
Objects.requireNonNull(displayAreaInfo,
"Requires valid displayArea, and displayArea must not be null");
- if (displayAreaInfo.token.asBinder() != mDisplayAreaInfo.token.asBinder()) {
+
+ if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
return;
}
- mDisplayAreaInfo = displayAreaInfo;
+ mDisplayAreaMap.remove(displayAreaInfo);
+ }
- // Stop one handed immediately
- if (isInOneHanded()) {
- final Rect newBounds = mDisplayAreaInfo.configuration.windowConfiguration.getBounds();
- resetImmediately(newBounds);
+ @Override
+ public void unregisterOrganizer() {
+ super.unregisterOrganizer();
+ if (mDisplayAreaMap != null) {
+ mDisplayAreaMap.clear();
}
}
/**
+ * Handler for display rotation changes.
+ */
+ public void onRotateDisplay(Resources res, int toRotation) {
+ // Stop one handed without animation and reset cropped size immediately
+ final Rect newBounds = new Rect();
+ mDisplayLayout.rotateTo(res, toRotation);
+ mDisplayLayout.getStableBounds(newBounds);
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = newBounds;
+ args.argi1 = 0 /* xOffset */;
+ args.argi2 = 0 /* yOffset */;
+ args.argi3 = TRANSITION_DIRECTION_EXIT;
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
+ }
+
+ /**
* Offset the windows by a given offset on Y-axis, triggered also from screen rotation.
* Directly perform manipulation/offset on the leash.
*/
@@ -217,55 +228,43 @@
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
}
- /**
- * Immediately resize/reset leash from previous cropped boundary to default.
- * (i.e Screen rotation need to reset crop and position before applying new bounds)
- */
- public void resetImmediately(Rect newDisplayBounds) {
- updateDisplayBounds(newDisplayBounds);
- if (mDisplayAreaInfo != null && mLeash != null) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = newDisplayBounds;
- args.argi1 = 0 /* xOffset */;
- args.argi2 = 0 /* yOffset */;
- args.argi3 = TRANSITION_DIRECTION_EXIT;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
- }
- }
-
- private void offsetWindows(Rect currentbounds, int xOffset, int yOffset, int direction,
- int durationMs) {
+ private void offsetWindows(Rect fromBounds, Rect toBounds, int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleOffset() instead of this "
+ "directly");
}
- if (mDisplayAreaInfo == null || mLeash == null) {
- Log.w(TAG, "mToken is not set");
- return;
- }
-
- Rect toBounds = new Rect(mDefaultDisplayBounds.left,
- mDefaultDisplayBounds.top + yOffset,
- mDefaultDisplayBounds.right,
- mDefaultDisplayBounds.bottom + yOffset);
- animateWindows(currentbounds, toBounds, direction, durationMs);
+ mDisplayAreaMap.forEach(
+ (key, leash) -> animateWindows(leash, fromBounds, toBounds, direction,
+ durationMs));
}
- private void animateWindows(Rect fromBounds, Rect toBounds,
+ private void resetWindowsOffset() {
+ mUpdateHandler.post(() -> {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ mDisplayAreaMap.forEach(
+ (key, leash) -> {
+ final OneHandedAnimationController.OneHandedTransitionAnimator animator =
+ mAnimationController.getAnimatorMap().remove(leash);
+ if (animator != null && animator.isRunning()) {
+ animator.cancel();
+ }
+ tx.setPosition(leash, 0, 0)
+ .setWindowCrop(leash, -1/* reset */, -1/* reset */);
+ });
+ tx.apply();
+ });
+ }
+
+ private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds,
@OneHandedAnimationController.TransitionDirection int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleOffset() instead of "
+ "this directly");
}
- // Could happen when exit one handed
- if (mDisplayAreaInfo == null || mLeash == null) {
- Log.w(TAG, "Abort animation, invalid leash");
- return;
- }
-
mUpdateHandler.post(() -> {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(mLeash, fromBounds, toBounds);
+ mAnimationController.getAnimator(leash, fromBounds, toBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
.setOneHandedAnimationCallback(mOneHandedAnimationCallback)
@@ -275,7 +274,7 @@
});
}
- private void finishOffset(Rect currentBounds, int offset,
+ private void finishOffset(int offset,
@OneHandedAnimationController.TransitionDirection int direction) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException(
@@ -284,18 +283,14 @@
// Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence,
// the flag *MUST* be updated before dispatch mTransitionCallbacks
mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER);
- mLastVisualDisplayBounds.set(currentBounds);
- if (direction == TRANSITION_DIRECTION_TRIGGER) {
- mLastVisualDisplayBounds.offsetTo(0, offset);
- for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
- final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
- callback.onStartFinished(mLastVisualDisplayBounds);
- }
- } else {
- mLastVisualDisplayBounds.offsetTo(0, 0);
- for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
- final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
- callback.onStopFinished(mLastVisualDisplayBounds);
+ mLastVisualDisplayBounds.offsetTo(0,
+ direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0);
+ for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
+ if (direction == TRANSITION_DIRECTION_TRIGGER) {
+ callback.onStartFinished(getLastVisualDisplayBounds());
+ } else {
+ callback.onStopFinished(getLastVisualDisplayBounds());
}
}
}
@@ -332,6 +327,16 @@
return mUpdateHandler;
}
+ private DisplayLayout getDisplayLayout(Context context) {
+ final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
+ if (display != null) {
+ display.getDisplayInfo(mDisplayInfo);
+ } else {
+ Log.w(TAG, "get DEFAULT_DISPLAY return null");
+ }
+ return new DisplayLayout(mDisplayInfo, context.getResources(), false, false);
+ }
+
/**
* Register transition callback
*/
@@ -349,29 +354,23 @@
return args;
}
- private void updateDisplayBounds(Rect newDisplayBounds) {
- if (newDisplayBounds == null) {
- mDefaultDisplayBounds.set(0, 0, getDisplayBounds().x, getDisplayBounds().y);
- } else {
- mDefaultDisplayBounds.set(newDisplayBounds);
- }
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mIsInOneHanded=");
pw.println(mIsInOneHanded);
- pw.print(innerPrefix + "mDisplayAreaInfo=");
- pw.println(mDisplayAreaInfo);
- pw.print(innerPrefix + "mLeash=");
- pw.println(mLeash);
+ pw.print(innerPrefix + "mDisplayAreaMap=");
+ pw.println(mDisplayAreaMap);
pw.print(innerPrefix + "mDefaultDisplayBounds=");
pw.println(mDefaultDisplayBounds);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
pw.println(mLastVisualDisplayBounds);
pw.print(innerPrefix + "getDisplayBounds()=");
pw.println(getDisplayBounds());
+ if (mDisplayLayout != null) {
+ pw.print(innerPrefix + "mDisplayLayout(w, h)=");
+ pw.println(mDisplayLayout.width() + ", " + mDisplayLayout.height());
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index d60b43f..4dacdf3 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -27,11 +27,13 @@
import androidx.annotation.NonNull;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.wm.DisplayChangeController;
import com.android.systemui.wm.DisplayController;
import java.io.FileDescriptor;
@@ -50,15 +52,18 @@
private boolean mIsOneHandedEnabled;
private boolean mTaskChangeToExit;
private float mOffSetFraction;
- private DisplayController mDisplayController;
+
+ private final DisplayController mDisplayController;
+ private final OneHandedGestureHandler mGestureHandler;
+ private final OneHandedTimeoutHandler mTimeoutHandler;
+ private final OneHandedTouchHandler mTouchHandler;
+ private final SysUiState mSysUiFlagContainer;
+
+ private Context mContext;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
- private OneHandedGestureHandler mGestureHandler;
private OneHandedGestureHandler.OneHandedGestureEventCallback mGestureEventCallback;
- private OneHandedTimeoutHandler mTimeoutHandler;
- private OneHandedTouchHandler mTouchHandler;
private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback;
private OneHandedTransitionCallback mTransitionCallback;
- private SysUiState mSysUiFlagContainer;
/**
* Handler for system task stack changes, exit when user lunch new task or bring task to front
@@ -84,6 +89,17 @@
};
/**
+ * Handle rotation based on OnDisplayChangingListener callback
+ */
+ @VisibleForTesting
+ private final DisplayChangeController.OnDisplayChangingListener mRotationController =
+ (display, fromRotation, toRotation, wct) -> {
+ if (mDisplayAreaOrganizer != null) {
+ mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), toRotation);
+ }
+ };
+
+ /**
* Constructor of OneHandedManager
*/
@Inject
@@ -93,9 +109,10 @@
OneHandedTouchHandler touchHandler,
OneHandedGestureHandler gestureHandler,
SysUiState sysUiState) {
-
+ mContext = context;
mDisplayAreaOrganizer = displayAreaOrganizer;
mDisplayController = displayController;
+ mDisplayController.addDisplayChangingController(mRotationController);
mSysUiFlagContainer = sysUiState;
mOffSetFraction =
context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index b6e4e16..a8130a1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -171,7 +171,6 @@
private float mSavedSnapFraction = -1f;
private boolean mSendingHoverAccessibilityEvents;
private boolean mMovementWithinDismiss;
- private boolean mHideMenuAfterShown = false;
private PipAccessibilityInteractionConnection mConnection;
// Touch state
@@ -242,7 +241,8 @@
this::updateMovementBounds, sysUiState);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
- true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()));
+ true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
+ menuController::hideMenu);
Resources res = context.getResources();
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
@@ -708,6 +708,7 @@
// on and changing MotionEvents into HoverEvents.
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+ mTouchState.removeHoverExitTimeoutCallback();
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
false /* allowMenuTimeout */, false /* willResizeMenu */,
shouldShowResizeHandle());
@@ -724,8 +725,7 @@
// on and changing MotionEvents into HoverEvents.
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
- mHideMenuAfterShown = true;
- mMenuController.hideMenu();
+ mTouchState.scheduleHoverExitTimeoutCallback();
}
if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) {
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
@@ -811,9 +811,6 @@
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
mMovementBounds, mExpandedMovementBounds, callback);
}
- if (mHideMenuAfterShown) {
- mMenuController.hideMenu();
- }
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
// Try and restore the PiP to the closest edge, using the saved snap fraction
// if possible
@@ -851,7 +848,6 @@
}
}
mMenuState = menuState;
- mHideMenuAfterShown = false;
updateMovementBounds();
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
// as well, or it can't handle a11y focus and pip menu can't perform any action.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 132c04d..ecd1128a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -36,10 +36,12 @@
@VisibleForTesting
static final long DOUBLE_TAP_TIMEOUT = 200;
+ static final long HOVER_EXIT_TIMEOUT = 50;
private final Handler mHandler;
private final ViewConfiguration mViewConfig;
private final Runnable mDoubleTapTimeoutCallback;
+ private final Runnable mHoverExitTimeoutCallback;
private VelocityTracker mVelocityTracker;
private long mDownTouchTime = 0;
@@ -64,10 +66,11 @@
private int mActivePointerId;
public PipTouchState(ViewConfiguration viewConfig, Handler handler,
- Runnable doubleTapTimeoutCallback) {
+ Runnable doubleTapTimeoutCallback, Runnable hoverExitTimeoutCallback) {
mViewConfig = viewConfig;
mHandler = handler;
mDoubleTapTimeoutCallback = doubleTapTimeoutCallback;
+ mHoverExitTimeoutCallback = hoverExitTimeoutCallback;
}
/**
@@ -197,6 +200,10 @@
recycleVelocityTracker();
break;
}
+ case MotionEvent.ACTION_BUTTON_PRESS: {
+ removeHoverExitTimeoutCallback();
+ break;
+ }
}
}
@@ -326,6 +333,15 @@
mHandler.removeCallbacks(mDoubleTapTimeoutCallback);
}
+ void scheduleHoverExitTimeoutCallback() {
+ mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+ mHandler.postDelayed(mHoverExitTimeoutCallback, HOVER_EXIT_TIMEOUT);
+ }
+
+ void removeHoverExitTimeoutCallback() {
+ mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+ }
+
void addMovementToVelocityTracker(MotionEvent event) {
if (mVelocityTracker == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index fba7a22..2ef6934 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
@@ -33,6 +32,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.qs.PseudoGridView;
import com.android.systemui.qs.QSUserSwitcherEvent;
@@ -100,7 +100,8 @@
if (item.picture == null) {
v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
} else {
- Drawable drawable = new BitmapDrawable(v.getResources(), item.picture);
+ int avatarSize = (int) v.getResources().getDimension(R.dimen.qs_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
drawable.setColorFilter(
item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
v.bind(name, drawable, item.info.id);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 6f554e6..b6c6afd 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -675,6 +675,9 @@
}
private void notifySplitScreenBoundsChanged() {
+ if (mSplitLayout.mPrimary == null || mSplitLayout.mSecondary == null) {
+ return;
+ }
mOtherTaskRect.set(mSplitLayout.mSecondary);
mTmpRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 423f85f..bd65ef0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -282,7 +282,7 @@
+ " doesn't match existing key " + mKey);
}
- mRanking = ranking;
+ mRanking = ranking.withAudiblyAlertedInfo(mRanking);
}
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 304fe00..539158c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -614,8 +614,10 @@
// Bubble controller will give us a valid display id if it should get the back event
BubbleController bubbleController = Dependency.get(BubbleController.class);
int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
- if (code == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
+ if (bubbleDisplayId != INVALID_DISPLAY) {
ev.setDisplayId(bubbleDisplayId);
+ } else {
+ ev.setDisplayId(mContext.getDisplay().getDisplayId());
}
InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 2e4a929d..5164440 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -84,6 +84,7 @@
private final boolean mKeyguardScreenRotation;
private final long mLockScreenDisplayTimeout;
private final Display.Mode mKeyguardDisplayMode;
+ private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardBypassController mKeyguardBypassController;
private ViewGroup mNotificationShadeView;
private LayoutParams mLp;
@@ -104,6 +105,7 @@
IActivityManager activityManager, DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor,
DumpManager dumpManager) {
mContext = context;
@@ -113,6 +115,7 @@
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
mLpChanged = new LayoutParams();
+ mKeyguardViewMediator = keyguardViewMediator;
mKeyguardBypassController = keyguardBypassController;
mColorExtractor = colorExtractor;
dumpManager.registerDumpable(getClass().getName(), this);
@@ -202,6 +205,11 @@
mWindowManager.addView(mNotificationShadeView, mLp);
mLpChanged.copyFrom(mLp);
onThemeChanged();
+
+ // Make the state consistent with KeyguardViewMediator#setupLocked during initialization.
+ if (mKeyguardViewMediator.isShowingAndNotOccluded()) {
+ setKeyguardShowing(true);
+ }
}
public void setNotificationShadeView(ViewGroup view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 60fc17d..686b871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -348,10 +348,8 @@
}
if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
- // In case the user isn't unlocked, make sure to delay a bit because the system is hosed
- // with too many things at this case, in order to not skip the initial frames.
- mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
+ scheduleUpdate();
} else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD)
|| (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
// Scheduling a frame isn't enough when:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 07de388..c43ad36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -106,9 +106,9 @@
public void updateNotification(@NonNull String key, boolean alert) {
super.updateNotification(key, alert);
- AlertEntry alertEntry = getHeadsUpEntry(key);
- if (alert && alertEntry != null) {
- setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(alertEntry.mEntry));
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ if (alert && headsUpEntry != null) {
+ setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 512d0f3..5ec5ec6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -24,7 +24,6 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.database.DataSetObserver;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
@@ -36,6 +35,7 @@
import android.widget.FrameLayout;
import com.android.settingslib.animation.AppearAnimationUtils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -288,7 +288,8 @@
if (item.picture == null) {
v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
} else {
- Drawable drawable = new BitmapDrawable(v.getResources(), item.picture);
+ int avatarSize = (int) v.getResources().getDimension(R.dimen.kg_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
drawable.setColorFilter(
item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
v.bind(name, drawable, item.info.id);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index f29f042..3d679de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -26,11 +26,14 @@
import android.util.Log;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.Set;
public class SysuiTestableContext extends TestableContext {
- private Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
+ @GuardedBy("mRegisteredReceivers")
+ private final Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
public SysuiTestableContext(Context base) {
super(base);
@@ -54,7 +57,11 @@
}
public void cleanUpReceivers(String testName) {
- Set<BroadcastReceiver> copy = new ArraySet<>(mRegisteredReceivers);
+ Set<BroadcastReceiver> copy;
+ synchronized (mRegisteredReceivers) {
+ copy = new ArraySet<>(mRegisteredReceivers);
+ mRegisteredReceivers.clear();
+ }
for (BroadcastReceiver r : copy) {
try {
unregisterReceiver(r);
@@ -68,7 +75,9 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
if (receiver != null) {
- mRegisteredReceivers.add(receiver);
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
}
return super.registerReceiver(receiver, filter);
}
@@ -77,7 +86,9 @@
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
if (receiver != null) {
- mRegisteredReceivers.add(receiver);
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
}
return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
}
@@ -86,7 +97,9 @@
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
IntentFilter filter, String broadcastPermission, Handler scheduler) {
if (receiver != null) {
- mRegisteredReceivers.add(receiver);
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
}
return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler);
}
@@ -94,7 +107,9 @@
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
if (receiver != null) {
- mRegisteredReceivers.remove(receiver);
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.remove(receiver);
+ }
}
super.unregisterReceiver(receiver);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5b46b7f..15828b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -63,6 +63,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -135,6 +136,8 @@
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
+ private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -198,8 +201,8 @@
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 058478f..686a094 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -60,6 +60,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
@@ -132,6 +133,8 @@
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
+ private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -194,8 +197,8 @@
// Bubbles get added to status bar window view
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 6761b28..59c2d0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -31,6 +31,7 @@
import org.mockito.Mockito.`when` as whenever
private const val KEY = "KEY"
+private const val KEY_2 = "KEY_2"
private const val PACKAGE_NAME = "com.android.systemui"
private const val APP_NAME = "SystemUI"
private const val SESSION_ARTIST = "artist"
@@ -156,8 +157,43 @@
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
mediaDataManager.onNotificationRemoved(KEY)
- // THEN the media data indicates that it is
+ // THEN the media data indicates that it is for resumption
assertThat(listener.data!!.resumption).isTrue()
+ // AND the new key is the package name
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(KEY)
+ assertThat(listener.removedKey).isNull()
+ }
+
+ @Test
+ fun testOnNotificationRemoved_twoWithResumption() {
+ // GIVEN that the manager has two notifications with resume actions
+ val listener = TestListener()
+ mediaDataManager.addListener(listener)
+ whenever(controller.metadata).thenReturn(metadataBuilder.build())
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ mediaDataManager.onNotificationAdded(KEY_2, mediaNotification)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
+ val data = listener.data!!
+ assertThat(data.resumption).isFalse()
+ val resumableData = data.copy(resumeAction = Runnable {})
+ mediaDataManager.onMediaDataLoaded(KEY, null, resumableData)
+ mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData)
+ // WHEN the first is removed
+ mediaDataManager.onNotificationRemoved(KEY)
+ // THEN the data is for resumption and the key is migrated to the package name
+ assertThat(listener.data!!.resumption).isTrue()
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(KEY)
+ assertThat(listener.removedKey).isNull()
+ // WHEN the second is removed
+ mediaDataManager.onNotificationRemoved(KEY_2)
+ // THEN the data is for resumption and the second key is removed
+ assertThat(listener.data!!.resumption).isTrue()
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.removedKey!!).isEqualTo(KEY_2)
}
@Test
@@ -190,6 +226,7 @@
var data: MediaData? = null
var key: String? = null
var oldKey: String? = null
+ var removedKey: String? = null
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
this.key = key
@@ -198,9 +235,7 @@
}
override fun onMediaDataRemoved(key: String) {
- this.key = key
- oldKey = null
- data = null
+ removedKey = key
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
index 2d9fa1b..86f4414 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.onehanded;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -57,7 +57,7 @@
}
@Test
- public void testGetAnimator_withSameBounds_returnTranslateAnimator() {
+ public void testGetAnimator_withSameBounds_returnAnimator() {
final Rect originalBounds = new Rect(0, 0, TEST_BOUNDS_WIDTH, TEST_BOUNDS_HEIGHT);
final Rect destinationBounds = originalBounds;
destinationBounds.offset(0, 300);
@@ -65,7 +65,6 @@
mOneHandedAnimationController
.getAnimator(mMockLeash, originalBounds, destinationBounds);
- assertEquals("Expected translate animation",
- OneHandedAnimationController.ANIM_TYPE_TRANSLATE, animator.getAnimationType());
+ assertNotNull(animator);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 28dad14..07a32a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -29,11 +29,11 @@
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.DisplayInfo;
+import android.view.Display;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.IWindowContainerToken;
@@ -58,7 +58,7 @@
static final int DISPLAY_HEIGHT = 1000;
DisplayAreaInfo mDisplayAreaInfo;
- DisplayInfo mDisplayInfo;
+ Display mDisplay;
Handler mUpdateHandler;
OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
@@ -80,20 +80,13 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
- mMockDisplayController,
- mMockAnimationController,
- mMockSurfaceTransactionHelper);
- mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
mToken = new WindowContainerToken(mMockRealToken);
mLeash = new SurfaceControl();
+ mDisplay = mContext.getDisplay();
mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED);
mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
- mDisplayInfo = new DisplayInfo();
- mDisplayInfo.logicalWidth = DISPLAY_WIDTH;
- mDisplayInfo.logicalHeight = DISPLAY_HEIGHT;
-
when(mMockAnimationController.getAnimator(any(), any(), any())).thenReturn(null);
+ when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockSurfaceTransactionHelper.translate(any(), any(), anyFloat())).thenReturn(
mMockSurfaceTransactionHelper);
when(mMockSurfaceTransactionHelper.crop(any(), any(), any())).thenReturn(
@@ -106,8 +99,11 @@
when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator);
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
- when(mMockDisplayController.getDisplay(anyInt())).thenReturn(null);
+ mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
+ mMockDisplayController,
+ mMockAnimationController);
+ mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
}
@Test
@@ -135,7 +131,7 @@
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
- assertThat(mDisplayAreaOrganizer.mDisplayAreaInfo).isEqualTo(newDisplayAreaInfo);
+ assertThat(mDisplayAreaOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo)).isTrue();
}
@Test
@@ -154,13 +150,7 @@
public void testResetImmediately() {
// To prevent mNativeObject of Surface is null in the test flow
when(mMockLeash.isValid()).thenReturn(false);
- final Rect newBounds = new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
- final DisplayAreaInfo newDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
- FEATURE_ONE_HANDED);
- newDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_LANDSCAPE;
-
- mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mMockLeash);
- mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
+ mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), Surface.ROTATION_90);
assertThat(mUpdateHandler.hasMessages(
OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isNotNull();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
index f4fc7ea..7d4700f5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
@@ -28,6 +28,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.Display;
import androidx.test.filters.SmallTest;
@@ -45,6 +46,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedManagerImplTest extends OneHandedTestCase {
+ Display mDisplay;
OneHandedManagerImpl mOneHandedManagerImpl;
OneHandedTimeoutHandler mTimeoutHandler;
@@ -53,8 +55,6 @@
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
- OneHandedSurfaceTransactionHelper mMockSurfaceTransactionHelper;
- @Mock
OneHandedTouchHandler mMockTouchHandler;
@Mock
OneHandedGestureHandler mMockGestureHandler;
@@ -64,6 +64,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mDisplay = mContext.getDisplay();
mOneHandedManagerImpl = new OneHandedManagerImpl(getContext(),
mMockDisplayController,
mMockDisplayAreaOrganizer,
@@ -72,10 +73,10 @@
mMockSysUiState);
mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
+ when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
}
-
@Test
public void testDefaultShouldNotInOneHanded() {
final OneHandedSurfaceTransactionHelper transactionHelper =
@@ -83,8 +84,7 @@
final OneHandedAnimationController animationController = new OneHandedAnimationController(
mContext, transactionHelper);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
- mContext, mMockDisplayController, animationController,
- mMockSurfaceTransactionHelper);
+ mContext, mMockDisplayController, animationController);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
index 3155e57..17b2e32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip.phone;
+import static android.view.MotionEvent.ACTION_BUTTON_PRESS;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
@@ -49,13 +50,17 @@
private PipTouchState mTouchState;
private CountDownLatch mDoubleTapCallbackTriggeredLatch;
+ private CountDownLatch mHoverExitCallbackTriggeredLatch;
@Before
public void setUp() throws Exception {
mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1);
+ mHoverExitCallbackTriggeredLatch = new CountDownLatch(1);
mTouchState = new PipTouchState(ViewConfiguration.get(getContext()),
Handler.createAsync(Looper.myLooper()), () -> {
mDoubleTapCallbackTriggeredLatch.countDown();
+ }, () -> {
+ mHoverExitCallbackTriggeredLatch.countDown();
});
assertFalse(mTouchState.isDoubleTap());
assertFalse(mTouchState.isWaitingForDoubleTap());
@@ -120,6 +125,35 @@
assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1);
}
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackCalled() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+
+ // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
+ Thread.sleep(50);
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 0);
+ }
+
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackNotCalled() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
+ }
+
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackNotCalled_ifButtonPress() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+ mTouchState.onTouchEvent(createMotionEvent(ACTION_BUTTON_PRESS, SystemClock.uptimeMillis(),
+ 0, 0));
+
+ // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
+ Thread.sleep(50);
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
+ }
+
private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) {
return MotionEvent.obtain(0, eventTime, action, x, y, 0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
index ca6c16f..8c37cf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static com.google.common.truth.Truth.assertThat;
@@ -41,6 +42,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -63,6 +65,7 @@
@Mock private IActivityManager mActivityManager;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
+ @Mock private KeyguardViewMediator mKeyguardViewMediator;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock ColorExtractor.GradientColors mGradientColors;
@@ -79,8 +82,8 @@
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
@@ -120,6 +123,17 @@
}
@Test
+ public void attach_visibleWithWallpaper() {
+ clearInvocations(mWindowManager);
+ when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true);
+ mNotificationShadeWindowController.attach();
+
+ verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
+ verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+ assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) != 0).isTrue();
+ }
+
+ @Test
public void setBackgroundBlurRadius_expandedWithBlurs() {
mNotificationShadeWindowController.setBackgroundBlurRadius(10);
verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 74df113..e10bab4 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -42,6 +42,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.system.Os;
import android.util.Log;
@@ -224,9 +225,19 @@
}
+ private boolean isAdbOverNetwork() {
+ // If adb TCP port opened, this test may running by adb over network.
+ return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
+ || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
+ }
+
@Test
public void testPhysicalEthernet() throws Exception {
assumeTrue(mEm.isAvailable());
+ // Do not run this test if adb is over network and ethernet is connected.
+ // It is likely the adb run over ethernet, the adb would break when ethernet is switching
+ // from client mode to server mode. See b/160389275.
+ assumeFalse(isAdbOverNetwork());
// Get an interface to use.
final String iface = mTetheredInterfaceRequester.getInterface();
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index ba61d1c0..7c7e742 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,7 +1,12 @@
# Bug component: 656484
+aabhinav@google.com
alsutton@google.com
bryanmawhinney@google.com
+jstemmer@google.com
nathch@google.com
+niagra@google.com
+niamhfw@google.com
+philippov@google.com
rthakohov@google.com
tobiast@google.com
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7334c86..18fb7a3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1808,7 +1808,7 @@
// different
mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mContext = context;
mResolver = mContext.getContentResolver();
mCallbacks = new Callbacks(FgThread.get().getLooper());
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 90f87ce..ce66b2c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3318,6 +3318,9 @@
@Override
public boolean setProcessMemoryTrimLevel(String process, int userId, int level)
throws RemoteException {
+ if (!isCallerShell()) {
+ throw new SecurityException("Only shell can call it");
+ }
synchronized (this) {
final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel");
if (app == null) {
@@ -6356,6 +6359,9 @@
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
+ if (mInternal.isPendingTopUid(uid)) {
+ return ActivityManager.APP_START_MODE_NORMAL;
+ }
UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+ packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 04089e3..5a0ea75 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -721,7 +721,7 @@
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
boolean fuseEnabled = SystemProperties.getBoolean(ANDROID_FUSE_ENABLED, false);
boolean voldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
if (!fuseEnabled && voldAppDataIsolationEnabled) {
Slog.e(TAG, "Fuse is not enabled while vold app data isolation is enabled");
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 466465e..68cc884 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -47,12 +47,17 @@
private final VibrationEffect mSuccessVibrationEffect;
private final VibrationEffect mErrorVibrationEffect;
- AcquisitionClient(@NonNull Context context, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
+ /**
+ * Stops the HAL operation specific to the ClientMonitor subclass.
+ */
+ protected abstract void stopHalOperation();
+
+ AcquisitionClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int cookie, int sensorId, int statsModality,
int statsAction, int statsClient) {
- super(context, token, listener, userId, owner, cookie, sensorId, statsModality, statsAction,
- statsClient);
+ super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality,
+ statsAction, statsClient);
mPowerManager = context.getSystemService(PowerManager.class);
mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
@@ -78,7 +83,7 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendError", e);
}
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
/**
@@ -110,7 +115,7 @@
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendAcquired", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index a9cefba..df836d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -52,12 +52,13 @@
protected boolean mAuthAttempted;
- public AuthenticationClient(@NonNull Context context, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
- boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
- int sensorId, boolean isStrongBiometric, int statsModality, int statsClient,
- @NonNull TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker) {
- super(context, token, listener, targetUserId, owner, cookie, sensorId,
+ public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ int targetUserId, long operationId, boolean restricted, @NonNull String owner,
+ int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
+ int statsModality, int statsClient, @NonNull TaskStackListener taskStackListener,
+ @NonNull LockoutTracker lockoutTracker) {
+ super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
mIsStrongBiometric = isStrongBiometric;
mOperationId = operationId;
@@ -179,7 +180,7 @@
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify listener, finishing", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@@ -187,8 +188,8 @@
* Start authentication
*/
@Override
- public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
final @LockoutTracker.LockoutMode int lockoutMode =
mLockoutTracker.getLockoutModeForUser(getTargetUserId());
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
index a784b4b..9f5abd8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
@@ -34,17 +34,12 @@
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricService;
-import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.Fingerprint;
import android.os.Binder;
-import android.os.Bundle;
-import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHwBinder;
-import android.os.IRemoteCallback;
import android.os.Looper;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -82,11 +77,9 @@
private final Context mContext;
private final String mKeyguardPackage;
protected final IActivityTaskManager mActivityTaskManager;
- private final PowerManager mPowerManager;
protected final BiometricTaskStackListener mTaskStackListener =
new BiometricTaskStackListener();
private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
- private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
protected final IStatusBarService mStatusBarService;
protected final Map<Integer, Long> mAuthenticatorIds =
@@ -109,21 +102,14 @@
}
};
- protected final ClientMonitor.FinishCallback mClientFinishCallback = clientMonitor -> {
- if (clientMonitor instanceof RemovalConsumer) {
- // When the last biometric of a group is removed, update the authenticator id.
- // Note that 1) multiple ClientMonitors may be cause onRemoved (e.g. internal cleanup),
- // and 2) updateActiveGroup updates/relies on global state, so there's no good way to
- // compartmentalize this yet.
- final int userId = clientMonitor.getTargetUserId();
- if (!hasEnrolledBiometrics(userId)) {
- Slog.d(getTag(), "Last biometric removed for user: " + userId
- + ", updating active group");
- updateActiveGroup(userId);
- }
- }
-
+ protected final ClientMonitor.FinishCallback mClientFinishCallback =
+ (clientMonitor, success) -> {
removeClient(clientMonitor);
+ // When enrollment finishes, update this group's authenticator id, as the HAL has
+ // already generated a new authenticator id when the new biometric is enrolled.
+ if (clientMonitor instanceof EnrollClient) {
+ updateActiveGroup(clientMonitor.getTargetUserId());
+ }
};
private IBiometricService mBiometricService;
@@ -263,65 +249,6 @@
}
}
-
-
- private final class LockoutResetMonitor implements IBinder.DeathRecipient {
- private static final long WAKELOCK_TIMEOUT_MS = 2000;
- private final IBiometricServiceLockoutResetCallback mCallback;
- private final PowerManager.WakeLock mWakeLock;
-
- public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
- mCallback = callback;
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "lockout reset callback");
- try {
- mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
- } catch (RemoteException e) {
- Slog.w(getTag(), "caught remote exception in linkToDeath", e);
- }
- }
-
- public void sendLockoutReset() {
- if (mCallback != null) {
- try {
- mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
- mCallback.onLockoutReset(new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- releaseWakelock();
- }
- });
- } catch (DeadObjectException e) {
- Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
- mHandler.post(mRemoveCallbackRunnable);
- } catch (RemoteException e) {
- Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
- releaseWakelock();
- }
- }
- }
-
- private final Runnable mRemoveCallbackRunnable = new Runnable() {
- @Override
- public void run() {
- releaseWakelock();
- removeLockoutResetCallback(LockoutResetMonitor.this);
- }
- };
-
- @Override
- public void binderDied() {
- Slog.e(getTag(), "Lockout reset callback binder died");
- mHandler.post(mRemoveCallbackRunnable);
- }
-
- private void releaseWakelock() {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
- }
-
/**
* Initializes the system service.
* <p>
@@ -341,7 +268,6 @@
mKeyguardPackage = keyguardComponent != null ? keyguardComponent.getPackageName() : null;
mAppOps = context.getSystemService(AppOpsManager.class);
mActivityTaskManager = ActivityTaskManager.getService();
- mPowerManager = mContext.getSystemService(PowerManager.class);
mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId());
}
@@ -452,15 +378,7 @@
}
final EnrollClient enrollClient = (EnrollClient) client;
-
- if (enrollClient.onEnrollResult(identifier, remaining)) {
- removeClient(enrollClient);
- // When enrollment finishes, update this group's authenticator id, as the HAL has
- // already generated a new authenticator id when the new biometric is enrolled.
- if (identifier instanceof Fingerprint) {
- updateActiveGroup(((Fingerprint)identifier).getGroupId());
- }
- }
+ enrollClient.onEnrollResult(identifier, remaining);
}
protected void handleError(int error, int vendorCode) {
@@ -630,7 +548,8 @@
});
}
- protected void cleanupInternal(InternalCleanupClient<T> client) {
+ protected void cleanupInternal(
+ InternalCleanupClient<? extends BiometricAuthenticator.Identifier, T> client) {
mHandler.post(() -> {
if (DEBUG) {
Slog.v(getTag(), "Cleaning up templates for user("
@@ -647,19 +566,6 @@
startClient(client, true /* initiatedByClient */);
}
- protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
- if (callback == null) {
- Slog.w(getTag(), "Null LockoutResetCallback");
- return;
- }
- mHandler.post(() -> {
- final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
- if (!mLockoutMonitors.contains(monitor)) {
- mLockoutMonitors.add(monitor);
- }
- });
- }
-
/**
* Helper methods.
*/
@@ -816,7 +722,7 @@
return;
}
- final T daemon = getDaemon();
+ final T daemon = mCurrentClient.getFreshDaemon();
if (daemon == null) {
Slog.e(getTag(), "Daemon null, unable to start: "
+ mCurrentClient.getClass().getSimpleName());
@@ -825,7 +731,7 @@
return;
}
- mCurrentClient.start(daemon, mClientFinishCallback);
+ mCurrentClient.start(mClientFinishCallback);
notifyClientActiveCallbacks(true);
}
@@ -931,12 +837,6 @@
doTemplateCleanupForUser(userId);
}
- protected void notifyLockoutResetMonitors() {
- for (int i = 0; i < mLockoutMonitors.size(); i++) {
- mLockoutMonitors.get(i).sendLockoutReset();
- }
- }
-
private void listenForUserSwitches() {
try {
ActivityManager.getService().registerUserSwitchObserver(
@@ -951,8 +851,4 @@
Slog.w(getTag(), "Failed to listen for user switching event" ,e);
}
}
-
- private void removeLockoutResetCallback(LockoutResetMonitor monitor) {
- mLockoutMonitors.remove(monitor);
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 5a2d63d..3f301cc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -47,11 +47,23 @@
* implementation.
*
* @param clientMonitor Reference of the ClientMonitor that finished.
+ * @param success True if the operation completed successfully.
*/
- void onClientFinished(ClientMonitor clientMonitor);
+ void onClientFinished(ClientMonitor clientMonitor, boolean success);
+ }
+
+ /**
+ * Interface that allows ClientMonitor subclasses to retrieve a fresh instance to the HAL.
+ */
+ public interface LazyDaemon<T> {
+ /**
+ * @return A fresh instance to the biometric HAL
+ */
+ T getDaemon();
}
@NonNull private final Context mContext;
+ @NonNull protected final LazyDaemon<T> mLazyDaemon;
private final int mTargetUserId;
@NonNull private final String mOwner;
private final int mSensorId; // sensorId as configured by the framework
@@ -63,11 +75,11 @@
private final int mCookie;
boolean mAlreadyDone;
- @NonNull protected T mDaemon;
@NonNull protected FinishCallback mFinishCallback;
/**
* @param context system_server context
+ * @param lazyDaemon pointer for lazy retrieval of the HAL
* @param token a unique token for the client
* @param listener recipient of related events (e.g. authentication)
* @param userId target user id for operation
@@ -78,12 +90,13 @@
* @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants
* @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants
*/
- public ClientMonitor(@NonNull Context context, @Nullable IBinder token,
- @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int cookie, int sensorId, int statsModality, int statsAction,
+ public ClientMonitor(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
int statsClient) {
super(statsModality, statsAction, statsClient);
mContext = context;
+ mLazyDaemon = lazyDaemon;
mToken = token;
mListener = listener;
mTargetUserId = userId;
@@ -107,18 +120,16 @@
/**
* Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null).
* If such a problem is detected, the scheduler will not invoke
- * {@link #start(Object, FinishCallback)}.
+ * {@link #start(FinishCallback)}.
*/
public abstract void unableToStart();
/**
* Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book
* keeping is complete.
- * @param daemon reference to the HAL
* @param finishCallback invoked when the operation is complete (succeeds, fails, etc)
*/
- public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
- mDaemon = daemon;
+ public void start(@NonNull FinishCallback finishCallback) {
mFinishCallback = finishCallback;
}
@@ -127,11 +138,6 @@
*/
protected abstract void startHalOperation();
- /**
- * Stops the HAL operation specific to the ClientMonitor subclass.
- */
- protected abstract void stopHalOperation();
-
public boolean isAlreadyDone() {
return mAlreadyDone;
}
@@ -190,4 +196,8 @@
public final int getSensorId() {
return mSensorId;
}
+
+ public final T getFreshDaemon() {
+ return mLazyDaemon.getDaemon();
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 6637ede..a3d9677 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -41,54 +41,46 @@
private long mEnrollmentStartTimeMs;
private boolean mAlreadyCancelled;
- public EnrollClient(@NonNull Context context, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
+ public EnrollClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
int timeoutSec, int statsModality, int sensorId,
boolean shouldVibrate) {
- super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality,
- BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ statsModality, BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
mTimeoutSec = timeoutSec;
mShouldVibrate = shouldVibrate;
}
- public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- if (remaining == 0) {
- mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
- logOnEnrolled(getTargetUserId(),
- System.currentTimeMillis() - mEnrollmentStartTimeMs,
- true /* enrollSuccessful */);
- }
- notifyUserActivity();
- return sendEnrollResult(identifier, remaining);
- }
-
- /*
- * @return true if we're done.
- */
- private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
+ public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
if (mShouldVibrate) {
vibrateSuccess();
}
+ final ClientMonitorCallbackConverter listener = getListener();
try {
- final ClientMonitorCallbackConverter listener = getListener();
if (listener != null) {
listener.onEnrollResult(identifier, remaining);
}
- return remaining == 0;
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify EnrollResult:", e);
- return true;
+ Slog.e(TAG, "Remote exception", e);
}
+
+ if (remaining == 0) {
+ mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
+ logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
+ true /* enrollSuccessful */);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ }
+ notifyUserActivity();
}
@Override
- public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
mEnrollmentStartTimeMs = System.currentTimeMillis();
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 12584cf..dad5cad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -29,9 +29,10 @@
protected long mChallenge;
- public GenerateChallengeClient(Context context, IBinder token,
- ClientMonitorCallbackConverter listener, String owner, int sensorId) {
- super(context, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
+ public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
}
@@ -46,20 +47,16 @@
}
@Override
- public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
startHalOperation();
try {
getListener().onChallengeGenerated(mChallenge);
+ mFinishCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
- mFinishCallback.onClientFinished(this);
- }
-
- @Override
- protected void stopHalOperation() {
- // Not supported for GenerateChallenge
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index f3ade65..6d7b0fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Wraps {@link InternalEnumerateClient} and {@link RemovalClient}. Keeps track of all the
@@ -37,8 +38,8 @@
* 2) The HAL and Framework are not in sync, and
* {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/
*/
-public abstract class InternalCleanupClient<T> extends ClientMonitor<T>
- implements EnumerateConsumer, RemovalConsumer {
+public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Identifier, T>
+ extends ClientMonitor<T> implements EnumerateConsumer, RemovalConsumer {
private static final String TAG = "Biometrics/InternalCleanupClient";
@@ -57,10 +58,11 @@
private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
private final BiometricUtils mBiometricUtils;
- private final List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
+ private final Map<Integer, Long> mAuthenticatorIds;
+ private final List<S> mEnrolledList;
private ClientMonitor<T> mCurrentTask;
- private final FinishCallback mEnumerateFinishCallback = clientMonitor -> {
+ private final FinishCallback mEnumerateFinishCallback = (clientMonitor, success) -> {
final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
@@ -75,45 +77,46 @@
if (mUnknownHALTemplates.isEmpty()) {
// No unknown HAL templates. Unknown framework templates are already cleaned up in
// InternalEnumerateClient. Finish this client.
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, success);
} else {
startCleanupUnknownHalTemplates();
}
};
- private final FinishCallback mRemoveFinishCallback = clientMonitor -> {
- mFinishCallback.onClientFinished(this);
+ private final FinishCallback mRemoveFinishCallback = (clientMonitor, success) -> {
+ mFinishCallback.onClientFinished(this, success);
};
- protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, IBinder token,
- int userId, String owner,
- List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils,
- int sensorId);
+ protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
+ LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner,
+ List<S> enrolledList, BiometricUtils utils, int sensorId);
- protected abstract RemovalClient<T> getRemovalClient(Context context, IBinder token,
- int biometricId, int userId, String owner, BiometricUtils utils, int sensorId);
+ protected abstract RemovalClient<T> getRemovalClient(Context context, LazyDaemon<T> lazyDaemon,
+ IBinder token, int biometricId, int userId, String owner, BiometricUtils utils,
+ int sensorId, Map<Integer, Long> authenticatorIds);
- protected InternalCleanupClient(@NonNull Context context, int userId,
- @NonNull String owner, int sensorId, int statsModality,
- @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
- @NonNull BiometricUtils utils) {
- super(context, null /* token */, null /* ClientMonitorCallbackConverter */,
+ protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ int userId, @NonNull String owner, int sensorId, int statsModality,
+ @NonNull List<S> enrolledList, @NonNull BiometricUtils utils,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
userId, owner, 0 /* cookie */, sensorId, statsModality,
BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
+ mAuthenticatorIds = authenticatorIds;
mEnrolledList = enrolledList;
}
private void startCleanupUnknownHalTemplates() {
UserTemplate template = mUnknownHALTemplates.get(0);
mUnknownHALTemplates.remove(template);
- mCurrentTask = getRemovalClient(getContext(), getToken(),
+ mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
template.mIdentifier.getBiometricId(), template.mUserId,
- getContext().getPackageName(), mBiometricUtils, getSensorId());
+ getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
mStatsModality,
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
- mCurrentTask.start(mDaemon, mRemoveFinishCallback);
+ mCurrentTask.start(mRemoveFinishCallback);
}
@Override
@@ -122,13 +125,13 @@
}
@Override
- public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
// Start enumeration. Removal will start if necessary, when enumeration is completed.
- mCurrentTask = getEnumerateClient(getContext(), getToken(), getTargetUserId(),
+ mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
- mCurrentTask.start(daemon, mEnumerateFinishCallback);
+ mCurrentTask.start(mEnumerateFinishCallback);
}
@Override
@@ -138,11 +141,6 @@
}
@Override
- protected void stopHalOperation() {
- // Internal cleanup's cannot be stopped.
- }
-
- @Override
public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
if (!(mCurrentTask instanceof RemovalClient)) {
Slog.e(TAG, "onRemoved received during client: "
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 9ce271c..3f73cd5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -43,13 +43,13 @@
// List of templates to remove from the HAL
private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
- protected InternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
- @NonNull String owner,
+ protected InternalEnumerateClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, int userId, @NonNull String owner,
@NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
@NonNull BiometricUtils utils, int sensorId, int statsModality) {
// Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
- super(context, token, null /* ClientMonitorCallbackConverter */, userId, owner,
+ super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mEnrolledList = enrolledList;
@@ -62,7 +62,7 @@
handleEnumeratedTemplate(identifier);
if (remaining == 0) {
doTemplateCleanup();
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, true /* success */);
}
}
@@ -72,8 +72,8 @@
}
@Override
- public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
// The biometric template ids will be removed when we get confirmation from the HAL
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
new file mode 100644
index 0000000..9bb5c6e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.content.Context;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * Allows clients (such as keyguard) to register for notifications on when biometric lockout
+ * ends.
+ */
+public class LockoutResetTracker implements IBinder.DeathRecipient {
+
+ private static final String TAG = "LockoutResetTracker";
+
+ private final Context mContext;
+ private final ArrayList<ClientCallback> mClientCallbacks;
+
+ private static class ClientCallback {
+ private static final long WAKELOCK_TIMEOUT_MS = 2000;
+
+ private final String mOpPackageName;
+ private final IBiometricServiceLockoutResetCallback mCallback;
+ private final PowerManager.WakeLock mWakeLock;
+
+ ClientCallback(Context context, IBiometricServiceLockoutResetCallback callback,
+ String opPackageName) {
+ final PowerManager pm = context.getSystemService(PowerManager.class);
+ mOpPackageName = opPackageName;
+ mCallback = callback;
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "LockoutResetMonitor:SendLockoutReset");
+ }
+
+ void sendLockoutReset() {
+ if (mCallback != null) {
+ try {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+ mCallback.onLockoutReset(new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) {
+ releaseWakelock();
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
+ releaseWakelock();
+ }
+ }
+ }
+
+ private void releaseWakelock() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ }
+
+ public LockoutResetTracker(Context context) {
+ mContext = context;
+ mClientCallbacks = new ArrayList<>();
+ }
+
+ public void addCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName) {
+ if (callback == null) {
+ Slog.w(TAG, "Callback from : " + opPackageName + " is null");
+ return;
+ }
+
+ mClientCallbacks.add(new ClientCallback(mContext, callback, opPackageName));
+ try {
+ callback.asBinder().linkToDeath(this, 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death", e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // Do nothing, handled below
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
+ Slog.e(TAG, "Callback binder died: " + who);
+ for (ClientCallback callback : mClientCallbacks) {
+ if (callback.mCallback.asBinder().equals(who)) {
+ Slog.e(TAG, "Removing dead callback for: " + callback.mOpPackageName);
+ callback.releaseWakelock();
+ mClientCallbacks.remove(callback);
+ }
+ }
+ }
+
+ public void notifyLockoutResetCallbacks() {
+ for (ClientCallback callback : mClientCallbacks) {
+ callback.sendLockoutReset();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index b734516..1c49bcdb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -24,6 +24,8 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.Map;
+
/**
* A class to keep track of the remove state for a given client.
*/
@@ -33,14 +35,18 @@
protected final int mBiometricId;
private final BiometricUtils mBiometricUtils;
+ private final Map<Integer, Long> mAuthenticatorIds;
- public RemovalClient(@NonNull Context context, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
- @NonNull String owner, @NonNull BiometricUtils utils, int sensorId, int statsModality) {
- super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality,
- BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+ int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ statsModality, BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricId = biometricId;
mBiometricUtils = utils;
+ mAuthenticatorIds = authenticatorIds;
}
@Override
@@ -49,8 +55,8 @@
}
@Override
- public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
// The biometric template ids will be removed when we get confirmation from the HAL
startHalOperation();
@@ -72,7 +78,14 @@
}
if (remaining == 0) {
- mFinishCallback.onClientFinished(this);
+ if (mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty()) {
+ Slog.d(TAG, "Last biometric removed for user: " + getTargetUserId());
+ // When the last biometric of a group is removed, update the authenticator id.
+ // Note that multiple ClientMonitors may be cause onRemoved (e.g. internal
+ // cleanup).
+ mAuthenticatorIds.put(getTargetUserId(), 0L);
+ }
+ mFinishCallback.onClientFinished(this, true /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index e00396b..b78ee49 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -23,10 +23,11 @@
public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> {
- public RevokeChallengeClient(Context context, IBinder token, String owner, int sensorId) {
- super(context, token, null /* listener */, 0 /* userId */, owner, 0 /* cookie */, sensorId,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, null /* listener */, 0 /* userId */, owner,
+ 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
}
@Override
@@ -35,15 +36,10 @@
}
@Override
- public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
startHalOperation();
- mFinishCallback.onClientFinished(this);
- }
-
- @Override
- protected void stopHalOperation() {
- // Not supported for RevokeChallenge
+ mFinishCallback.onClientFinished(this, true /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
index a54357e..118cadc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
@@ -62,13 +62,14 @@
private int mLastAcquire;
- FaceAuthenticationClient(@NonNull Context context, @NonNull IBinder token,
+ FaceAuthenticationClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
boolean isStrongBiometric, int statsClient,
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker, @NonNull UsageStats usageStats) {
- super(context, token, listener, targetUserId, operationId, restricted,
+ super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FACE, statsClient, taskStackListener,
lockoutTracker);
@@ -89,22 +90,22 @@
@Override
protected void startHalOperation() {
try {
- mDaemon.authenticate(mOperationId);
+ getFreshDaemon().authenticate(mOperationId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
try {
- mDaemon.cancel();
+ getFreshDaemon().cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@@ -132,7 +133,7 @@
// 1) Authenticated == true
// 2) Error occurred
// 3) Authenticated == false
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, true /* success */);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index b63b39e..4f9f46a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -51,13 +51,14 @@
@NonNull private final int[] mEnrollIgnoreList;
@NonNull private final int[] mEnrollIgnoreListVendor;
- FaceEnrollClient(@NonNull Context context, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
+ FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
@NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle,
int sensorId) {
- super(context, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec,
- BiometricsProtoEnums.MODALITY_FACE, sensorId, false /* shouldVibrate */);
+ super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+ timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
+ false /* shouldVibrate */);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mSurfaceHandle = surfaceHandle;
mEnrollIgnoreList = getContext().getResources()
@@ -89,36 +90,36 @@
}
android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 =
- android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(mDaemon);
+ android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(getFreshDaemon());
try {
final int status;
if (daemon11 != null) {
status = daemon11.enroll_1_1(token, mTimeoutSec, disabledFeatures, mSurfaceHandle);
} else if (mSurfaceHandle == null) {
- status = mDaemon.enroll(token, mTimeoutSec, disabledFeatures);
+ status = getFreshDaemon().enroll(token, mTimeoutSec, disabledFeatures);
} else {
Slog.e(TAG, "enroll(): surface is only supported in @1.1 HAL");
status = BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
}
if (status != Status.OK) {
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
try {
- mDaemon.cancel();
+ getFreshDaemon().cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
index 72188a5..67f2712 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
@@ -36,15 +36,16 @@
private static final String TAG = "FaceGenerateChallengeClient";
private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
- FaceGenerateChallengeClient(@NonNull Context context, @NonNull IBinder token,
+ FaceGenerateChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
- super(context, token, listener, owner, sensorId);
+ super(context, lazyDaemon, token, listener, owner, sensorId);
}
@Override
protected void startHalOperation() {
try {
- mChallenge = mDaemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+ mChallenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
} catch (RemoteException e) {
Slog.e(TAG, "generateChallenge failed", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
index 227d817..ce57cb7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
@@ -40,10 +40,10 @@
private final int mFeature;
private final int mFaceId;
- FaceGetFeatureClient(@NonNull Context context, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
- int sensorId, int feature, int faceId) {
- super(context, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ FaceGetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int sensorId, int feature, int faceId) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mFeature = feature;
@@ -61,24 +61,20 @@
}
@Override
- public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
startHalOperation();
}
@Override
protected void startHalOperation() {
try {
- final OptionalBool result = mDaemon.getFeature(mFeature, mFaceId);
+ final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId);
getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value);
+ mFinishCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to getFeature", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
- mFinishCallback.onClientFinished(this);
- }
-
- @Override
- protected void stopHalOperation() {
- // Not supported for GetFeature
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
index 388baa2..93f35f4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
@@ -18,9 +18,9 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
import android.os.IBinder;
import com.android.server.biometrics.sensors.BiometricUtils;
@@ -29,36 +29,40 @@
import com.android.server.biometrics.sensors.RemovalClient;
import java.util.List;
+import java.util.Map;
/**
* Face-specific internal cleanup client supporting the
* {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
* HIDL interfaces.
*/
-class FaceInternalCleanupClient extends InternalCleanupClient<IBiometricsFace> {
+class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsFace> {
- FaceInternalCleanupClient(@NonNull Context context, int userId, @NonNull String owner,
- int sensorId, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
- @NonNull BiometricUtils utils) {
- super(context, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE, enrolledList,
- utils);
+ FaceInternalCleanupClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
+ enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
- IBinder token, int userId, String owner,
- List<? extends BiometricAuthenticator.Identifier> enrolledList,
- BiometricUtils utils, int sensorId) {
- return new FaceInternalEnumerateClient(context, token, userId, owner, enrolledList, utils,
- sensorId);
+ LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
+ List<Face> enrolledList, BiometricUtils utils, int sensorId) {
+ return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+ enrolledList, utils, sensorId);
}
@Override
- protected RemovalClient<IBiometricsFace> getRemovalClient(Context context, IBinder token,
- int biometricId, int userId, String owner, BiometricUtils utils, int sensorId) {
+ protected RemovalClient<IBiometricsFace> getRemovalClient(Context context,
+ LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token,
+ int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+ Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
- return new FaceRemovalClient(context, token, null /* ClientMonitorCallbackConverter */,
- biometricId, userId, owner, utils, sensorId);
+ return new FaceRemovalClient(context, lazyDaemon, token,
+ null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+ sensorId, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
index c6749c5..f25242e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
@@ -18,9 +18,9 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -38,31 +38,21 @@
class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFace> {
private static final String TAG = "FaceInternalEnumerateClient";
- FaceInternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
- @NonNull String owner,
- @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
- @NonNull BiometricUtils utils, int sensorId) {
- super(context, token, userId, owner, enrolledList, utils, sensorId,
+ FaceInternalEnumerateClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
+ @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+ int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
BiometricsProtoEnums.MODALITY_FACE);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.enumerate();
+ getFreshDaemon().enumerate();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enumerate", e);
- mFinishCallback.onClientFinished(this);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- mDaemon.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
index b0ee981..00d5f50 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
@@ -28,6 +28,8 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
+import java.util.Map;
+
/**
* Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
* and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
@@ -35,30 +37,21 @@
class FaceRemovalClient extends RemovalClient<IBiometricsFace> {
private static final String TAG = "FaceRemovalClient";
- FaceRemovalClient(@NonNull Context context, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
- @NonNull String owner, @NonNull BiometricUtils utils, int sensorId) {
- super(context, token, listener, biometricId, userId, owner, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FACE);
+ FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+ int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.remove(mBiometricId);
+ getFreshDaemon().remove(mBiometricId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting remove", e);
- mFinishCallback.onClientFinished(this);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- mDaemon.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
index 441cb14..69070da 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
@@ -37,10 +37,11 @@
private final ArrayList<Byte> mHardwareAuthToken;
- FaceResetLockoutClient(@NonNull Context context, int userId, String owner, int sensorId,
- byte[] hardwareAuthToken) {
- super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
- sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ FaceResetLockoutClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull byte[] hardwareAuthToken) {
+ super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+ 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mHardwareAuthToken = new ArrayList<>();
@@ -55,24 +56,19 @@
}
@Override
- public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
-
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
startHalOperation();
}
@Override
protected void startHalOperation() {
try {
- mDaemon.resetLockout(mHardwareAuthToken);
+ getFreshDaemon().resetLockout(mHardwareAuthToken);
+ mFinishCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to reset lockout", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
- mFinishCallback.onClientFinished(this);
- }
-
- @Override
- protected void stopHalOperation() {
- // Not supported for resetLockout
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
index 992a678d..a10c573 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
@@ -33,15 +33,16 @@
private static final String TAG = "FaceRevokeChallengeClient";
- FaceRevokeChallengeClient(@NonNull Context context, @NonNull IBinder token,
+ FaceRevokeChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
@NonNull String owner, int sensorId) {
- super(context, token, owner, sensorId);
+ super(context, lazyDaemon, token, owner, sensorId);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.revokeChallenge();
+ getFreshDaemon().revokeChallenge();
} catch (RemoteException e) {
Slog.e(TAG, "revokeChallenge failed", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 050fba2..a7f8220 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -25,7 +25,6 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -60,6 +59,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
+import com.android.server.biometrics.sensors.LockoutResetTracker;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -97,6 +97,9 @@
static final String NOTIFICATION_TAG = "FaceService";
static final int NOTIFICATION_ID = 1;
+ private final LockoutResetTracker mLockoutResetTracker;
+ private final ClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
+
/**
* Receives the incoming binder calls from FaceManager.
*/
@@ -113,7 +116,7 @@
checkPermission(MANAGE_BIOMETRIC);
final GenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(),
- token, new ClientMonitorCallbackConverter(receiver), opPackageName,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
getSensorId());
generateChallengeInternal(client);
}
@@ -122,8 +125,8 @@
public void revokeChallenge(IBinder token, String owner) {
checkPermission(MANAGE_BIOMETRIC);
- final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(), token,
- owner, getSensorId());
+ final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(),
+ mLazyDaemon, token, owner, getSensorId());
// TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
if (getCurrentClient() == null) {
@@ -149,7 +152,7 @@
UserHandle.CURRENT);
});
- final EnrollClient client = new FaceEnrollClient(getContext(), token,
+ final EnrollClient client = new FaceEnrollClient(getContext(), mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, getBiometricUtils(), disabledFeatures, ENROLL_TIMEOUT_SEC,
convertSurfaceToNativeHandle(surface), getSensorId());
@@ -180,11 +183,11 @@
final boolean restricted = isRestricted();
final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_UNKNOWN;
- final AuthenticationClient client = new FaceAuthenticationClient(getContext(), token,
- new ClientMonitorCallbackConverter(receiver), userId, opId, restricted,
- opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(),
- isStrongBiometric(), statsClient, mTaskStackListener, mLockoutTracker,
- mUsageStats);
+ final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
+ restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
+ getSensorId(), isStrongBiometric(), statsClient, mTaskStackListener,
+ mLockoutTracker, mUsageStats);
authenticateInternal(client, opPackageName);
}
@@ -197,11 +200,11 @@
updateActiveGroup(userId);
final boolean restricted = true; // BiometricPrompt is always restricted
- final AuthenticationClient client = new FaceAuthenticationClient(getContext(), token,
- new ClientMonitorCallbackConverter(sensorReceiver), userId, opId, restricted,
- opPackageName, cookie, requireConfirmation, getSensorId(), isStrongBiometric(),
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
- mLockoutTracker, mUsageStats);
+ final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
+ opId, restricted, opPackageName, cookie, requireConfirmation, getSensorId(),
+ isStrongBiometric(), BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+ mTaskStackListener, mLockoutTracker, mUsageStats);
authenticateInternal(client, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -238,16 +241,19 @@
return;
}
- final RemovalClient client = new FaceRemovalClient(getContext(), token,
+ final RemovalClient client = new FaceRemovalClient(getContext(), mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
- getBiometricUtils(), getSensorId());
+ getBiometricUtils(), getSensorId(), mAuthenticatorIds);
removeInternal(client);
}
@Override
- public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback) {
+ public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
+ final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
- FaceService.super.addLockoutResetCallback(callback);
+ mHandler.post(() -> {
+ mLockoutResetTracker.addCallback(callback, opPackageName);
+ });
}
@Override // Binder call
@@ -340,7 +346,8 @@
updateActiveGroup(userId);
final FaceResetLockoutClient client = new FaceResetLockoutClient(getContext(),
- userId, getContext().getOpPackageName(), getSensorId(), hardwareAuthToken);
+ mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(),
+ hardwareAuthToken);
startClient(client, true /* initiatedByClient */);
});
}
@@ -364,8 +371,8 @@
final int faceId = getFirstTemplateForUser(mCurrentUserId);
final FaceSetFeatureClient client = new FaceSetFeatureClient(getContext(),
- token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- getSensorId(), feature, enabled, hardwareAuthToken, faceId);
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, getSensorId(), feature, enabled, hardwareAuthToken, faceId);
startClient(client, true /* initiatedByClient */);
});
@@ -391,9 +398,9 @@
// TODO: Support multiple faces
final int faceId = getFirstTemplateForUser(mCurrentUserId);
- final FaceGetFeatureClient client = new FaceGetFeatureClient(getContext(), token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- getSensorId(), feature, faceId);
+ final FaceGetFeatureClient client = new FaceGetFeatureClient(getContext(),
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, getSensorId(), feature, faceId);
startClient(client, true /* initiatedByClient */);
});
@@ -539,7 +546,7 @@
mHandler.post(() -> {
if (duration == 0) {
- notifyLockoutResetMonitors();
+ mLockoutResetTracker.notifyLockoutResetCallbacks();
}
});
}
@@ -547,6 +554,8 @@
public FaceService(Context context) {
super(context);
+ mLazyDaemon = FaceService.this::getFaceDaemon;
+ mLockoutResetTracker = new LockoutResetTracker(context);
mLockoutTracker = new LockoutHalImpl();
mUsageStats = new UsageStats(context);
mNotificationManager = getContext().getSystemService(NotificationManager.class);
@@ -673,7 +682,7 @@
@Override
protected List<Face> getEnrolledTemplates(int userId) {
- return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ return FaceUtils.getInstance().getBiometricsForUser(getContext(), userId);
}
@Override
@@ -693,10 +702,10 @@
@Override
protected void doTemplateCleanupForUser(int userId) {
- final List<? extends BiometricAuthenticator.Identifier> enrolledList =
- getEnrolledTemplates(userId);
- final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(), userId,
- getContext().getOpPackageName(), getSensorId(), enrolledList, getBiometricUtils());
+ final List<Face> enrolledList = getEnrolledTemplates(userId);
+ final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(),
+ mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(), enrolledList,
+ getBiometricUtils(), mAuthenticatorIds);
cleanupInternal(client);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
index 91f63e1..e7d041a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
@@ -43,11 +43,11 @@
private final ArrayList<Byte> mHardwareAuthToken;
private final int mFaceId;
- FaceSetFeatureClient(@NonNull Context context, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
+ FaceSetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId, int feature, boolean enabled,
byte[] hardwareAuthToken, int faceId) {
- super(context, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mFeature = feature;
@@ -70,25 +70,22 @@
}
@Override
- public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
- super.start(daemon, finishCallback);
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
startHalOperation();
- mFinishCallback.onClientFinished(this);
}
@Override
protected void startHalOperation() {
try {
- final int result = mDaemon.setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
+ final int result = getFreshDaemon()
+ .setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
getListener().onFeatureSet(result == Status.OK, mFeature);
+ mFinishCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
-
- @Override
- protected void stopHalOperation() {
- // Not supported for SetFeature
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
index ba89d51..b01b856 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
@@ -47,13 +47,14 @@
private final LockoutFrameworkImpl mLockoutFrameworkImpl;
- FingerprintAuthenticationClient(@NonNull Context context, @NonNull IBinder token,
+ FingerprintAuthenticationClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
int sensorId, boolean isStrongBiometric, @Nullable Surface surface, int statsClient,
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker) {
- super(context, token, listener, targetUserId, operationId, restricted,
+ super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
lockoutTracker);
@@ -72,7 +73,7 @@
if (authenticated) {
resetFailedAttempts(getTargetUserId());
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, true /* success */);
} else {
final @LockoutTracker.LockoutMode int lockoutMode =
mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
@@ -83,7 +84,7 @@
? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
: BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
onError(errorCode, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, true /* success */);
}
}
}
@@ -102,24 +103,24 @@
protected void startHalOperation() {
try {
// GroupId was never used. In fact, groupId is always the same as userId.
- mDaemon.authenticate(mOperationId, getTargetUserId());
+ getFreshDaemon().authenticate(mOperationId, getTargetUserId());
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
try {
- mDaemon.cancel();
+ getFreshDaemon().cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
index 34681c3..1d56882 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
@@ -38,36 +38,38 @@
private static final String TAG = "FingerprintEnrollClient";
- FingerprintEnrollClient(@NonNull Context context, @NonNull IBinder token,
+ FingerprintEnrollClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
int timeoutSec, int sensorId) {
- super(context, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec,
- BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */);
+ super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+ timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
+ true /* shouldVibrate */);
}
@Override
protected void startHalOperation() {
try {
// GroupId was never used. In fact, groupId is always the same as userId.
- mDaemon.enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
+ getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
try {
- mDaemon.cancel();
+ getFreshDaemon().cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
index 2fa4333..8fb8c99 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
@@ -36,15 +36,16 @@
private static final String TAG = "FingerprintGenerateChallengeClient";
- FingerprintGenerateChallengeClient(@NonNull Context context, @NonNull IBinder token,
+ FingerprintGenerateChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
- super(context, token, listener, owner, sensorId);
+ super(context, lazyDaemon, token, listener, owner, sensorId);
}
@Override
protected void startHalOperation() {
try {
- mChallenge = mDaemon.preEnroll();
+ mChallenge = getFreshDaemon().preEnroll();
} catch (RemoteException e) {
Slog.e(TAG, "preEnroll failed", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
index 71e7670..1d72c2e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
@@ -21,6 +21,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import com.android.server.biometrics.sensors.BiometricUtils;
@@ -29,37 +30,43 @@
import com.android.server.biometrics.sensors.RemovalClient;
import java.util.List;
+import java.util.Map;
/**
* Fingerprint-specific internal cleanup client supporting the
* {@link android.hardware.biometrics.fingerprint.V2_1} and
* {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
*/
-class FingerprintInternalCleanupClient extends InternalCleanupClient<IBiometricsFingerprint> {
+class FingerprintInternalCleanupClient
+ extends InternalCleanupClient<Fingerprint, IBiometricsFingerprint> {
- FingerprintInternalCleanupClient(@NonNull Context context,int userId, @NonNull String owner,
- int sensorId, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
- @NonNull BiometricUtils utils) {
- super(context, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
- enrolledList, utils);
+ FingerprintInternalCleanupClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
+ @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
+ @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
}
@Override
protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
- Context context, IBinder token, int userId, String owner,
- List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils,
+ Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
+ int userId, String owner,
+ List<Fingerprint> enrolledList, BiometricUtils utils,
int sensorId) {
- return new FingerprintInternalEnumerateClient(context, token, userId, owner, enrolledList,
- utils, sensorId);
+ return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+ enrolledList, utils, sensorId);
}
@Override
- protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context, IBinder token,
- int biometricId, int userId, String owner, BiometricUtils utils, int sensorId) {
+ protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context,
+ LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
+ int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+ Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
- return new FingerprintRemovalClient(context, token,
+ return new FingerprintRemovalClient(context, lazyDaemon, token,
null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId);
+ sensorId, authenticatorIds);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
index ba412e3..240c3c5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
@@ -18,9 +18,9 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -38,31 +38,21 @@
class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFingerprint> {
private static final String TAG = "FingerprintInternalEnumerateClient";
- FingerprintInternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
- @NonNull String owner,
- @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
+ FingerprintInternalEnumerateClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
@NonNull BiometricUtils utils, int sensorId) {
- super(context, token, userId, owner, enrolledList, utils, sensorId,
+ super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
BiometricsProtoEnums.MODALITY_FINGERPRINT);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.enumerate();
+ getFreshDaemon().enumerate();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enumerate", e);
- mFinishCallback.onClientFinished(this);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- mDaemon.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
index 6d7e761..a9336ef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
@@ -28,6 +28,8 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
+import java.util.Map;
+
/**
* Fingerprint-specific removal client supporting the
* {@link android.hardware.biometrics.fingerprint.V2_1} and
@@ -36,31 +38,23 @@
class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> {
private static final String TAG = "FingerprintRemovalClient";
- FingerprintRemovalClient(@NonNull Context context, @NonNull IBinder token,
+ FingerprintRemovalClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
- @NonNull String owner, @NonNull BiometricUtils utils, int sensorId) {
- super(context, token, listener, biometricId, userId, owner, utils, sensorId,
- BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ @NonNull String owner, @NonNull BiometricUtils utils, int sensorId,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
}
@Override
protected void startHalOperation() {
try {
// GroupId was never used. In fact, groupId is always the same as userId.
- mDaemon.remove(getTargetUserId(), mBiometricId);
+ getFreshDaemon().remove(getTargetUserId(), mBiometricId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting remove", e);
- mFinishCallback.onClientFinished(this);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- mDaemon.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
index ccbea31d..882660e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
@@ -35,15 +35,16 @@
private static final String TAG = "FingerprintRevokeChallengeClient";
- FingerprintRevokeChallengeClient(@NonNull Context context, @NonNull IBinder token,
+ FingerprintRevokeChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull String owner, int sensorId) {
- super(context, token, owner, sensorId);
+ super(context, lazyDaemon, token, owner, sensorId);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.postEnroll();
+ getFreshDaemon().postEnroll();
} catch (RemoteException e) {
Slog.e(TAG, "revokeChallenge/postEnroll failed", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index d2a25db..b05400a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -29,7 +29,6 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -64,9 +63,11 @@
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricServiceBase;
import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
+import com.android.server.biometrics.sensors.LockoutResetTracker;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -82,7 +83,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -97,6 +97,10 @@
private static final boolean DEBUG = true;
private static final String FP_DATA_DIR = "fpdata";
+ private final LockoutResetTracker mLockoutResetTracker;
+ private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon;
+ private final GestureAvailabilityTracker mGestureAvailabilityTracker;
+
/**
* Receives the incoming binder calls from FingerprintManager.
*/
@@ -113,7 +117,7 @@
checkPermission(MANAGE_FINGERPRINT);
final GenerateChallengeClient client = new FingerprintGenerateChallengeClient(
- getContext(), token, new ClientMonitorCallbackConverter(receiver),
+ getContext(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
opPackageName, getSensorId());
generateChallengeInternal(client);
}
@@ -123,7 +127,7 @@
checkPermission(MANAGE_FINGERPRINT);
final RevokeChallengeClient client = new FingerprintRevokeChallengeClient(getContext(),
- token, owner, getSensorId());
+ mLazyDaemon, token, owner, getSensorId());
revokeChallengeInternal(client);
}
@@ -134,8 +138,8 @@
checkPermission(MANAGE_FINGERPRINT);
updateActiveGroup(userId);
- final EnrollClient client = new FingerprintEnrollClient(getContext(), token,
- new ClientMonitorCallbackConverter(receiver), userId, cryptoToken,
+ final EnrollClient client = new FingerprintEnrollClient(getContext(), mLazyDaemon,
+ token, new ClientMonitorCallbackConverter(receiver), userId, cryptoToken,
opPackageName, getBiometricUtils(), ENROLL_TIMEOUT_SEC, getSensorId());
enrollInternal(client, userId);
@@ -165,9 +169,10 @@
final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
- token, new ClientMonitorCallbackConverter(receiver), userId, opId, restricted,
- opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(),
- isStrongBiometric, surface, statsClient, mTaskStackListener, mLockoutTracker);
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
+ restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
+ getSensorId(), isStrongBiometric, surface, statsClient, mTaskStackListener,
+ mLockoutTracker);
authenticateInternal(client, opPackageName);
}
@@ -175,14 +180,14 @@
public void prepareForAuthentication(IBinder token, long opId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName,
int cookie, int callingUid, int callingPid, int callingUserId,
- Surface surface) throws RemoteException {
+ Surface surface) {
checkPermission(MANAGE_BIOMETRIC);
updateActiveGroup(userId);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
- token, new ClientMonitorCallbackConverter(sensorReceiver), userId, opId,
- restricted, opPackageName, cookie, false /* requireConfirmation */,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
+ opId, restricted, opPackageName, cookie, false /* requireConfirmation */,
getSensorId(), isStrongBiometric(), surface,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
mLockoutTracker);
@@ -223,17 +228,19 @@
return;
}
- final RemovalClient client = new FingerprintRemovalClient(getContext(), token,
- new ClientMonitorCallbackConverter(receiver), fingerId, userId, opPackageName,
- getBiometricUtils(), getSensorId());
+ final RemovalClient client = new FingerprintRemovalClient(getContext(), mLazyDaemon,
+ token, new ClientMonitorCallbackConverter(receiver), fingerId, userId,
+ opPackageName, getBiometricUtils(), getSensorId(), mAuthenticatorIds);
removeInternal(client);
}
@Override
- public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
- throws RemoteException {
+ public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
+ final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
- FingerprintService.super.addLockoutResetCallback(callback);
+ mHandler.post(() -> {
+ mLockoutResetTracker.addCallback(callback, opPackageName);
+ });
}
@Override // Binder call
@@ -350,13 +357,13 @@
@Override
public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.add(callback);
+ mGestureAvailabilityTracker.registerCallback(callback);
}
@Override
public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.remove(callback);
+ mGestureAvailabilityTracker.removeCallback(callback);
}
@Override // Binder call
@@ -464,17 +471,11 @@
}
private final LockoutFrameworkImpl mLockoutTracker;
- private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
- new CopyOnWriteArrayList<>();
private IUdfpsOverlayController mUdfpsOverlayController;
@GuardedBy("this")
private IBiometricsFingerprint mDaemon;
- private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback = userId -> {
- notifyLockoutResetMonitors();
- };
-
/**
* Receives callbacks from the HAL.
*/
@@ -550,7 +551,13 @@
public FingerprintService(Context context) {
super(context);
- mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
+ mLazyDaemon = FingerprintService.this::getFingerprintDaemon;
+ mGestureAvailabilityTracker = new GestureAvailabilityTracker();
+ mLockoutResetTracker = new LockoutResetTracker(context);
+ final LockoutFrameworkImpl.LockoutResetCallback lockoutResetCallback = userId -> {
+ mLockoutResetTracker.notifyLockoutResetCallbacks();
+ };
+ mLockoutTracker = new LockoutFrameworkImpl(context, lockoutResetCallback);
}
@Override
@@ -677,20 +684,12 @@
if (userId != UserHandle.getCallingUserId()) {
checkPermission(INTERACT_ACROSS_USERS);
}
- return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ return FingerprintUtils.getInstance().getBiometricsForUser(getContext(), userId);
}
@Override
protected void notifyClientActiveCallbacks(boolean isActive) {
- List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
- for (int i = 0; i < callbacks.size(); i++) {
- try {
- callbacks.get(i).onClientActiveChanged(isActive);
- } catch (RemoteException re) {
- // If the remote is dead, stop notifying it
- mClientActiveCallbacks.remove(callbacks.get(i));
- }
- }
+ mGestureAvailabilityTracker.notifyClientActiveCallbacks(isActive);
}
@Override
@@ -705,11 +704,10 @@
@Override
protected void doTemplateCleanupForUser(int userId) {
- final List<? extends BiometricAuthenticator.Identifier> enrolledList =
- getEnrolledTemplates(userId);
+ final List<Fingerprint> enrolledList = getEnrolledTemplates(userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
- getContext(), userId, getContext().getOpPackageName(), getSensorId(), enrolledList,
- getBiometricUtils());
+ getContext(), mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(),
+ enrolledList, getBiometricUtils(), mAuthenticatorIds);
cleanupInternal(client);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
new file mode 100644
index 0000000..6292ecf
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.os.RemoteException;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Keeps track of sensor gesture availability (e.g. swipe), and notifies clients when its
+ * availability changes
+ */
+class GestureAvailabilityTracker {
+ private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
+ new CopyOnWriteArrayList<>();
+
+ void registerCallback(IFingerprintClientActiveCallback callback) {
+ mClientActiveCallbacks.add(callback);
+ }
+
+ void removeCallback(IFingerprintClientActiveCallback callback) {
+ mClientActiveCallbacks.remove(callback);
+ }
+
+ void notifyClientActiveCallbacks(boolean isActive) {
+ for (IFingerprintClientActiveCallback callback : mClientActiveCallbacks) {
+ try {
+ callback.onClientActiveChanged(isActive);
+ } catch (RemoteException re) {
+ // If the remote is dead, stop notifying it
+ mClientActiveCallbacks.remove(callback);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index c5aa8d5..a75a80a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -82,6 +82,7 @@
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final INetd mNetd;
+ private final Dependencies mDeps;
// Values are User IDs.
@GuardedBy("this")
@@ -102,10 +103,30 @@
@GuardedBy("this")
private final Set<Integer> mAllApps = new HashSet<>();
- public PermissionMonitor(Context context, INetd netd) {
+ /**
+ * Dependencies of PermissionMonitor, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Get device first sdk version.
+ */
+ public int getDeviceFirstSdkInt() {
+ return Build.VERSION.FIRST_SDK_INT;
+ }
+ }
+
+ public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) {
+ this(context, netd, new Dependencies());
+ }
+
+ @VisibleForTesting
+ PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
+ @NonNull final Dependencies deps) {
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mNetd = netd;
+ mDeps = deps;
}
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -186,11 +207,6 @@
}
@VisibleForTesting
- protected int getDeviceFirstSdkInt() {
- return Build.VERSION.FIRST_SDK_INT;
- }
-
- @VisibleForTesting
boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) {
if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
return false;
@@ -212,7 +228,7 @@
if (app.applicationInfo != null) {
// Backward compatibility for b/114245686, on devices that launched before Q daemons
// and apps running as the system UID are exempted from this check.
- if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
+ if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) {
return true;
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 7b767b8..c4581c8 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -155,7 +155,7 @@
pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
pw.println("");
- pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
+ pw.println(" set-password [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
pw.println(" Sets the lock screen as password, using the given PASSOWRD to unlock.");
pw.println("");
pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID]");
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index 55e0795..b9822fcb 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -26,12 +26,12 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.IntArray;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -104,7 +104,7 @@
// TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
@GuardedBy("mLock")
@SuppressWarnings("WeakerAccess") /* synthetic access */
- final IntArray mSortedAudioPlaybackClientUids = new IntArray();
+ final List<Integer> mSortedAudioPlaybackClientUids = new ArrayList<>();
static AudioPlayerStateMonitor getInstance(Context context) {
synchronized (AudioPlayerStateMonitor.class) {
@@ -145,8 +145,8 @@
* audio/video) The UID whose audio is currently playing comes first, then the UID whose audio
* playback becomes active at the last comes next.
*/
- public IntArray getSortedAudioPlaybackClientUids() {
- IntArray sortedAudioPlaybackClientUids = new IntArray();
+ public List<Integer> getSortedAudioPlaybackClientUids() {
+ List<Integer> sortedAudioPlaybackClientUids = new ArrayList();
synchronized (mLock) {
sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index b678c89..953aae4 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -22,7 +22,6 @@
import android.media.session.MediaSession;
import android.os.Debug;
import android.os.UserHandle;
-import android.util.IntArray;
import android.util.Log;
import android.util.SparseArray;
@@ -190,7 +189,8 @@
if (DEBUG) {
Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2));
}
- IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
+ List<Integer> audioPlaybackUids =
+ mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
for (int i = 0; i < audioPlaybackUids.size(); i++) {
int audioPlaybackUid = audioPlaybackUids.get(i);
MediaSessionRecordImpl mediaButtonSession = findMediaButtonSession(audioPlaybackUid);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5f48871..ca16d57 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -405,7 +405,7 @@
private boolean mDataLoaderFinished = false;
- // TODO(b/159663586): should be protected by mLock
+ @GuardedBy("mLock")
private IncrementalFileStorages mIncrementalFileStorages;
private static final FileFilter sAddedApkFilter = new FileFilter() {
@@ -1125,13 +1125,18 @@
return;
}
if (isMultiPackage()) {
- final SparseIntArray remainingSessions = mChildSessionIds.clone();
+ final SparseIntArray remainingSessions;
+ final int[] childSessionIds;
+ synchronized (mLock) {
+ remainingSessions = mChildSessionIds.clone();
+ childSessionIds = mChildSessionIds.copyKeys();
+ }
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
boolean sealFailed = false;
- for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
- final int childSessionId = mChildSessionIds.keyAt(i);
+ for (int i = childSessionIds.length - 1; i >= 0; --i) {
+ final int childSessionId = childSessionIds[i];
// seal all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mSessionProvider.getSession(childSessionId)
@@ -1163,13 +1168,17 @@
}
if (isMultiPackage()) {
- int childCount = mChildSessionIds.size();
+ final int[] childSessionIds;
+ synchronized (mLock) {
+ childSessionIds = mChildSessionIds.copyKeys();
+ }
+ int childCount = childSessionIds.length;
// This will contain all child sessions that do not encounter an unrecoverable failure
ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount);
for (int i = childCount - 1; i >= 0; --i) {
- final int childSessionId = mChildSessionIds.keyAt(i);
+ final int childSessionId = childSessionIds[i];
// commit all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
try {
@@ -2588,7 +2597,8 @@
* Adds a child session ID without any safety / sanity checks. This should only be used to
* build a session from XML or similar.
*/
- void addChildSessionIdInternal(int sessionId) {
+ @GuardedBy("mLock")
+ void addChildSessionIdLocked(int sessionId) {
mChildSessionIds.put(sessionId, 0);
}
@@ -2964,7 +2974,10 @@
@Override
public int[] getChildSessionIds() {
- final int[] childSessionIds = mChildSessionIds.copyKeys();
+ final int[] childSessionIds;
+ synchronized (mLock) {
+ childSessionIds = mChildSessionIds.copyKeys();
+ }
if (childSessionIds != null) {
return childSessionIds;
}
@@ -2990,7 +3003,7 @@
return;
}
childSession.setParentSessionId(this.sessionId);
- addChildSessionIdInternal(childSessionId);
+ addChildSessionIdLocked(childSessionId);
}
}
@@ -3157,10 +3170,10 @@
for (FileBridge bridge : mBridges) {
bridge.forceClose();
}
- }
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.cleanUp();
- mIncrementalFileStorages = null;
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
+ }
}
// For staged sessions, we don't delete the directory where the packages have been copied,
// since these packages are supposed to be read on reboot.
@@ -3197,9 +3210,11 @@
}
private void cleanStageDir() {
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.cleanUp();
- mIncrementalFileStorages = null;
+ synchronized (mLock) {
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
+ }
}
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 734b718..de06c92 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -49,11 +49,8 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.SystemService;
-import com.android.server.pm.InstructionSets;
import com.android.server.pm.PackageManagerService;
-import dalvik.system.VMRuntime;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -214,7 +211,7 @@
newLevel = State.LEVEL_FULL;
} else if (usableBytes <= lowBytes) {
newLevel = State.LEVEL_LOW;
- } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
+ } else if (StorageManager.UUID_DEFAULT.equals(uuid)
&& usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
newLevel = State.LEVEL_LOW;
} else {
@@ -261,15 +258,6 @@
};
}
- private static boolean isBootImageOnDisk() {
- for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
- if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
- return false;
- }
- }
- return true;
- }
-
@Override
public void onStart() {
final Context context = getContext();
@@ -481,15 +469,8 @@
final CharSequence title = context.getText(
com.android.internal.R.string.low_internal_storage_view_title);
- final CharSequence details;
- if (StorageManager.UUID_DEFAULT.equals(uuid)) {
- details = context.getText(isBootImageOnDisk()
- ? com.android.internal.R.string.low_internal_storage_view_text
- : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
- } else {
- details = context.getText(
- com.android.internal.R.string.low_internal_storage_view_text);
- }
+ final CharSequence details = context.getText(
+ com.android.internal.R.string.low_internal_storage_view_text);
PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
null, UserHandle.CURRENT);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 61c127c..601d64b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -925,8 +925,6 @@
void awakeFromSleepingLocked() {
// Ensure activities are no longer sleeping.
forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
if (mPausingActivity != null) {
Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
mPausingActivity.activityPaused(true);
@@ -2416,7 +2414,7 @@
forAllActivities(ActivityRecord::removeLaunchTickRunnable);
}
- private void updateTransitLocked(int transit, ActivityOptions options) {
+ private void updateTransitLocked(int transit, ActivityOptions options, boolean forceOverride) {
if (options != null) {
ActivityRecord r = topRunningActivity();
if (r != null && !r.isState(RESUMED)) {
@@ -2425,7 +2423,8 @@
ActivityOptions.abort(options);
}
}
- getDisplay().mDisplayContent.prepareAppTransition(transit, false);
+ getDisplay().mDisplayContent.prepareAppTransition(transit, false,
+ 0 /* flags */, forceOverride);
}
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
@@ -2445,8 +2444,17 @@
// nothing to do!
if (noAnimation) {
ActivityOptions.abort(options);
+ } else if (isSingleTaskInstance()) {
+ // When a task is moved front on the display which can only contain one task, start
+ // a special transition.
+ // {@link AppTransitionController#handleAppTransitionReady} later picks up the
+ // transition, and schedules
+ // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is triggered
+ // after contents are drawn on the display.
+ updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
+ true /* forceOverride */);
} else {
- updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
+ updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
}
return;
}
@@ -2492,9 +2500,13 @@
mStackSupervisor.mNoAnimActivities.add(r);
}
ActivityOptions.abort(options);
+ } else if (isSingleTaskInstance()) {
+ updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
+ true /* forceOverride */);
} else {
- updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
+ updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
}
+
// If a new task is moved to the front, then mark the existing top activity as
// supporting
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 63e14bb..9852208 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -252,6 +252,7 @@
req = mLastKeyguardForcedOrientation;
}
}
+ mLastOrientationSource = win;
return req;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9c330ec..4f25609 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1170,6 +1170,8 @@
activity.onRemovedFromDisplay();
if (activity == mFixedRotationLaunchingApp) {
+ // Make sure the states of associated tokens are also cleared.
+ activity.finishFixedRotationTransform();
setFixedRotationLaunchingAppUnchecked(null);
}
}
@@ -1468,6 +1470,12 @@
// window was transferred ({@link #mSkipAppTransitionAnimation}).
return false;
}
+ if ((mAppTransition.getTransitFlags()
+ & WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) {
+ // The transition may be finished before keyguard hidden. In order to avoid the
+ // intermediate orientation change, it is more stable to freeze the display.
+ return false;
+ }
} else if (r != topRunningActivity()) {
// If the transition has not started yet, the activity must be the top.
return false;
@@ -2291,12 +2299,23 @@
void onAppTransitionDone() {
super.onAppTransitionDone();
mWmService.mWindowsChanged = true;
+ // If the transition finished callback cannot match the token for some reason, make sure the
+ // rotated state is cleared if it is already invisible.
+ if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.mVisibleRequested
+ && !mFixedRotationLaunchingApp.isVisible()
+ && !mDisplayRotation.isRotatingSeamlessly()) {
+ clearFixedRotationLaunchingApp();
+ }
}
@Override
public void setWindowingMode(int windowingMode) {
- super.setWindowingMode(windowingMode);
- super.setDisplayWindowingMode(windowingMode);
+ // Intentionally call onRequestedOverrideConfigurationChanged() directly to change windowing
+ // mode and display windowing mode atomically.
+ mTmpConfiguration.setTo(getRequestedOverrideConfiguration());
+ mTmpConfiguration.windowConfiguration.setWindowingMode(windowingMode);
+ mTmpConfiguration.windowConfiguration.setDisplayWindowingMode(windowingMode);
+ onRequestedOverrideConfigurationChanged(mTmpConfiguration);
}
@Override
@@ -2952,11 +2971,9 @@
final ScreenRotationAnimation rotationAnimation = getRotationAnimation();
if (rotationAnimation != null) {
- pw.print(subPrefix);
pw.println(" mScreenRotationAnimation:");
- rotationAnimation.printTo(" ", pw);
+ rotationAnimation.printTo(subPrefix, pw);
} else if (dumpAll) {
- pw.print(subPrefix);
pw.println(" no ScreenRotationAnimation ");
}
@@ -3647,15 +3664,15 @@
drawnWindowTypes.put(TYPE_NOTIFICATION_SHADE, true);
final WindowState visibleNotDrawnWindow = getWindow(w -> {
- boolean isVisible = w.mViewVisibility == View.VISIBLE && !w.mObscured;
- boolean hasDrawn = w.isDrawnLw() && w.hasDrawnLw();
- if (isVisible && !hasDrawn) {
+ final boolean isVisible = w.isVisible() && !w.mObscured;
+ final boolean isDrawn = w.isDrawnLw();
+ if (isVisible && !isDrawn) {
ProtoLog.d(WM_DEBUG_BOOT,
"DisplayContent: boot is waiting for window of type %d to be drawn",
w.mAttrs.type);
return true;
}
- if (hasDrawn) {
+ if (isDrawn) {
switch (w.mAttrs.type) {
case TYPE_BOOT_PROGRESS:
case TYPE_BASE_APPLICATION:
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 3e88566..8734b5e 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -19,13 +19,10 @@
import static android.os.Process.myPid;
import static android.os.Process.myUid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
@@ -480,11 +477,12 @@
mService.getRecentsAnimationController();
final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
&& recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
- if (inputWindowHandle == null || w.mRemoved) {
+ if (inputChannel == null || inputWindowHandle == null || w.mRemoved
+ || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
if (w.mWinAnimator.hasSurface()) {
mInputTransaction.setInputWindowInfo(
- w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
- mInvalidInputWindow);
+ w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
+ mInvalidInputWindow);
}
// Skip this window because it cannot possibly receive input.
return;
@@ -493,23 +491,9 @@
final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags;
final int type = w.mAttrs.type;
+ final boolean hasFocus = w.isFocused();
final boolean isVisible = w.isVisibleLw();
- // Assign an InputInfo with type to the overlay window which can't receive input event.
- // This is used to omit Surfaces from occlusion detection.
- if (inputChannel == null
- || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
- if (!w.mWinAnimator.hasSurface()) {
- return;
- }
- populateOverlayInputInfo(inputWindowHandle, w.getName(), type, isVisible);
- mInputTransaction.setInputWindowInfo(
- w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
- inputWindowHandle);
- return;
- }
-
- final boolean hasFocus = w.isFocused();
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
@@ -571,28 +555,6 @@
}
}
- // This would reset InputWindowHandle fields to prevent it could be found by input event.
- // We need to check if any new field of InputWindowHandle could impact the result.
- private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle,
- final String name, final int type, final boolean isVisible) {
- inputWindowHandle.name = name;
- inputWindowHandle.layoutParamsType = type;
- inputWindowHandle.dispatchingTimeoutNanos =
- WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- inputWindowHandle.visible = isVisible;
- inputWindowHandle.canReceiveKeys = false;
- inputWindowHandle.hasFocus = false;
- inputWindowHandle.ownerPid = myPid();
- inputWindowHandle.ownerUid = myUid();
- inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
- inputWindowHandle.scaleFactor = 1;
- inputWindowHandle.layoutParamsFlags =
- FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE;
- inputWindowHandle.portalToDisplayId = INVALID_DISPLAY;
- inputWindowHandle.touchableRegion.setEmpty();
- inputWindowHandle.setTouchableRegionCrop(null);
- }
-
/**
* Helper function to generate an InputInfo with type SECURE_SYSTEM_OVERLAY. This input
* info will not have an input channel or be touchable, but is used to omit Surfaces
@@ -602,7 +564,16 @@
static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t,
int displayId, String name) {
InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId);
- populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true);
+ inputWindowHandle.name = name;
+ inputWindowHandle.layoutParamsType = TYPE_SECURE_SYSTEM_OVERLAY;
+ inputWindowHandle.dispatchingTimeoutNanos = -1;
+ inputWindowHandle.visible = true;
+ inputWindowHandle.canReceiveKeys = false;
+ inputWindowHandle.hasFocus = false;
+ inputWindowHandle.ownerPid = myPid();
+ inputWindowHandle.ownerUid = myUid();
+ inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
+ inputWindowHandle.scaleFactor = 1;
t.setInputWindowInfo(sc, inputWindowHandle);
}
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c7f7834..c255a18 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -359,17 +359,20 @@
RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
Rect endBounds, Rect startBounds) {
mWindowContainer = windowContainer;
- mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
+ mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
+ mStartBounds);
mTmpRect.set(startBounds);
mTmpRect.offsetTo(0, 0);
if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
mThumbnailAdapter =
new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
- mTmpRect);
+ mTmpRect, new Rect());
}
} else {
+ mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
+ new Rect(endPos.x, endPos.y, endBounds.right, endBounds.bottom));
mStartBounds = null;
}
}
@@ -407,13 +410,15 @@
final Point mPosition = new Point();
final Rect mLocalBounds;
final Rect mStackBounds = new Rect();
+ final Rect mStartBounds = new Rect();
RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
- Rect localBounds, Rect stackBounds) {
+ Rect localBounds, Rect stackBounds, Rect startBounds) {
mRecord = record;
mPosition.set(position.x, position.y);
mLocalBounds = localBounds;
mStackBounds.set(stackBounds);
+ mStartBounds.set(startBounds);
}
@Override
@@ -427,13 +432,12 @@
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore position and stack crop until client has a chance to modify it.
- if (mRecord.mStartBounds != null) {
- t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
- t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
- mRecord.mStartBounds.height());
+ if (mStartBounds.isEmpty()) {
+ t.setPosition(animationLeash, 0, 0);
+ t.setWindowCrop(animationLeash, -1, -1);
} else {
- t.setPosition(animationLeash, mPosition.x, mPosition.y);
- t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height());
+ t.setPosition(animationLeash, mStartBounds.left, mStartBounds.top);
+ t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
}
mCapturedLeash = animationLeash;
mCapturedFinishCallback = finishCallback;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e787aac..a795867 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2375,7 +2375,8 @@
// triggered after contents are drawn on the display.
if (display.isSingleTaskInstance()) {
display.mDisplayContent.prepareAppTransition(
- TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
+ TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false,
+ 0 /* flags */, true /* forceOverride*/);
}
stack.awakeFromSleepingLocked();
if (display.isSingleTaskInstance()) {
@@ -2391,6 +2392,12 @@
// activity here.
resumeFocusedStacksTopActivities();
}
+ // The visibility update must not be called before resuming the top, so the
+ // display orientation can be updated first if needed. Otherwise there may
+ // have redundant configuration changes due to apply outdated display
+ // orientation (from keyguard) to activity.
+ stack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 83b7543..bcd71c9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1419,7 +1419,7 @@
void removeChild(WindowContainer r, String reason) {
// A rootable child task that is now being removed from an organized task. Making sure
// the stack references is keep updated.
- if (mTaskOrganizer != null && mCreatedByOrganizer && r.asTask() != null) {
+ if (mCreatedByOrganizer && r.asTask() != null) {
getDisplayArea().removeStackReferenceIfNeeded((ActivityStack) r);
}
if (!mChildren.contains(r)) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 67673dc..24cf751 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1859,14 +1859,14 @@
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
pw.println(prefix + "TaskDisplayArea " + getName());
- super.dump(pw, prefix, dumpAll);
+ final String doublePrefix = prefix + " ";
+ super.dump(pw, doublePrefix, dumpAll);
if (mPreferredTopFocusableStack != null) {
- pw.println(prefix + " mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+ pw.println(doublePrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
}
if (mLastFocusedStack != null) {
- pw.println(prefix + " mLastFocusedStack=" + mLastFocusedStack);
+ pw.println(doublePrefix + "mLastFocusedStack=" + mLastFocusedStack);
}
- final String doublePrefix = prefix + " ";
final String triplePrefix = doublePrefix + " ";
pw.println(doublePrefix + "Application tokens in top down Z order:");
for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e3d6530..4de7772 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2557,6 +2557,9 @@
pw.print(prefix); pw.println("ContainerAnimator:");
mSurfaceAnimator.dump(pw, prefix + " ");
}
+ if (mLastOrientationSource != null) {
+ pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
+ }
}
final void updateSurfacePosition() {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 75ec224..7bd455a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -194,7 +194,7 @@
public:
NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);
- inline sp<InputManager> getInputManager() const { return mInputManager; }
+ inline sp<InputManagerInterface> getInputManager() const { return mInputManager; }
void dump(std::string& dump);
@@ -225,7 +225,7 @@
/* --- InputReaderPolicyInterface implementation --- */
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
- virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
+ virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId);
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
@@ -236,29 +236,26 @@
/* --- InputDispatcherPolicyInterface implementation --- */
- virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags) override;
- virtual void notifyConfigurationChanged(nsecs_t when);
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) override;
- virtual void notifyInputChannelBroken(const sp<IBinder>& token);
- virtual void notifyFocusChanged(const sp<IBinder>& oldToken,
- const sp<IBinder>& newToken) override;
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
- uint32_t& policyFlags) override;
- virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
- uint32_t& policyFlags) override;
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent* keyEvent,
- uint32_t policyFlags) override;
- virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
- uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
- int32_t injectorUid) override;
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
+ void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override;
+ void notifyConfigurationChanged(nsecs_t when) override;
+ std::chrono::nanoseconds notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token,
+ const std::string& reason) override;
+ void notifyInputChannelBroken(const sp<IBinder>& token) override;
+ void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
+ void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) override;
+ void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+ uint32_t& policyFlags) override;
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags) override;
+ bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
+ void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
+ bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
+ void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -270,7 +267,7 @@
virtual int32_t getCustomPointerIconId();
private:
- sp<InputManager> mInputManager;
+ sp<InputManagerInterface> mInputManager;
jobject mServiceObj;
sp<Looper> mLooper;
@@ -299,7 +296,7 @@
sp<SpriteController> spriteController;
// Pointer controller singleton, created and destroyed as needed.
- wp<PointerController> pointerController;
+ std::weak_ptr<PointerController> pointerController;
// Input devices to be disabled
std::set<int32_t> disabledInputDevices;
@@ -342,9 +339,9 @@
}
mInteractive = true;
- mInputManager = new InputManager(this, this);
- defaultServiceManager()->addService(String16("inputflinger"),
- mInputManager, false);
+ InputManager* im = new InputManager(this, this);
+ mInputManager = im;
+ defaultServiceManager()->addService(String16("inputflinger"), im);
}
NativeInputManager::~NativeInputManager() {
@@ -544,15 +541,16 @@
} // release lock
}
-sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) {
+std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController(
+ int32_t /* deviceId */) {
ATRACE_CALL();
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller == nullptr) {
ensureSpriteControllerLocked();
- controller = new PointerController(this, mLooper, mLocked.spriteController);
+ controller = PointerController::create(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
updateInactivityTimeoutLocked();
}
@@ -694,8 +692,9 @@
return handle->getInputApplicationHandleObjLocalRef(env);
}
-nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) {
+std::chrono::nanoseconds NativeInputManager::notifyAnr(
+ const sp<InputApplicationHandle>& inputApplicationHandle, const sp<IBinder>& token,
+ const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyANR");
#endif
@@ -718,7 +717,7 @@
} else {
assert(newTimeout >= 0);
}
- return newTimeout;
+ return std::chrono::nanoseconds(newTimeout);
}
void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
@@ -803,15 +802,14 @@
}
void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller == nullptr) {
return;
}
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
- controller->setInactivityTimeout(lightsOut
- ? PointerController::INACTIVITY_TIMEOUT_SHORT
- : PointerController::INACTIVITY_TIMEOUT_NORMAL);
+ controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT
+ : PointerController::InactivityTimeout::NORMAL);
}
void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -891,7 +889,7 @@
void NativeInputManager::setPointerIconType(int32_t iconId) {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->updatePointerIcon(iconId);
}
@@ -899,7 +897,7 @@
void NativeInputManager::reloadPointerIcons() {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->reloadPointerResources();
}
@@ -907,7 +905,7 @@
void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->setCustomPointerIcon(icon);
}
@@ -1253,7 +1251,7 @@
}
void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
- mInputManager->setMotionClassifierEnabled(enabled);
+ mInputManager->getClassifier()->setMotionClassifierEnabled(enabled);
}
// ----------------------------------------------------------------------------
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 24da81e..a2e310a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -618,12 +618,6 @@
}
}
- // Diagnostic to ensure that the system is in a base healthy state. Done here as a common
- // non-zygote process.
- if (!VMRuntime.hasBootImageSpaces()) {
- Slog.wtf(TAG, "Runtime is not running with a boot image!");
- }
-
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 668f047..c7b45ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -127,8 +127,7 @@
mTask = mStack.getBottomMostTask();
mActivity = mTask.getTopNonFinishingActivity();
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
}
@Test
@@ -1535,7 +1534,7 @@
* Sets orientation without notifying the parent to simulate that the display has not applied
* the requested orientation yet.
*/
- private static void setRotatedScreenOrientationSilently(ActivityRecord r) {
+ static void setRotatedScreenOrientationSilently(ActivityRecord r) {
final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT
? SCREEN_ORIENTATION_LANDSCAPE
: SCREEN_ORIENTATION_PORTRAIT;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 37882bb..1b42a04 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1392,8 +1392,7 @@
}
mSupervisor.endDeferResume();
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
// 2 activities are started while keyguard is locked, so they are waiting to be resolved.
assertFalse(unknownAppVisibilityController.allResolved());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 64b5eca..f65d6e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -67,8 +67,7 @@
@Before
public void setUp() throws Exception {
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
}
/** Verify that activity is finished correctly upon request. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4466a66..f4f199c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -482,12 +482,17 @@
final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
TYPE_WALLPAPER, TYPE_APPLICATION);
+ final WindowState wallpaper = windows[0];
+ assertTrue(wallpaper.mIsWallpaper);
+ // By default WindowState#mWallpaperVisible is false.
+ assertFalse(wallpaper.isVisible());
// Verify waiting for windows to be drawn.
assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
- // Verify not waiting for drawn windows.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
+ // Verify not waiting for drawn window and invisible wallpaper.
+ setDrawnState(WindowStateAnimator.READY_TO_SHOW, wallpaper);
+ setDrawnState(WindowStateAnimator.HAS_DRAWN, windows[1]);
assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
}
@@ -508,26 +513,10 @@
assertTrue(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
// Verify not waiting for drawn windows on display with system decorations.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
+ setDrawnState(WindowStateAnimator.HAS_DRAWN, windows);
assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
}
- @Test
- public void testShouldWaitForSystemDecorWindowsOnBoot_OnWindowReadyToShowAndDrawn() {
- mWm.mSystemBooted = true;
- final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
- final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
- TYPE_WALLPAPER, TYPE_APPLICATION);
-
- // Verify waiting for windows to be drawn.
- makeWindowsDrawnState(windows, WindowStateAnimator.READY_TO_SHOW);
- assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
-
- // Verify not waiting for drawn windows.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
- assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
- }
-
private WindowState[] createNotDrawnWindowsOn(DisplayContent displayContent, int... types) {
final WindowState[] windows = new WindowState[types.length];
for (int i = 0; i < types.length; i++) {
@@ -538,9 +527,9 @@
return windows;
}
- private static void makeWindowsDrawnState(WindowState[] windows, int state) {
+ private static void setDrawnState(int state, WindowState... windows) {
for (WindowState window : windows) {
- window.mHasSurface = true;
+ window.mHasSurface = state != WindowStateAnimator.NO_SURFACE;
window.mWinAnimator.mDrawState = state;
}
}
@@ -1291,8 +1280,7 @@
@Test
public void testNoFixedRotationWithPip() {
// Make resume-top really update the activity state.
- doReturn(false).when(mWm.mAtmService).isBooting();
- doReturn(true).when(mWm.mAtmService).isBooted();
+ setBooted(mWm.mAtmService);
// Speed up the test by a few seconds.
mWm.mAtmService.deferWindowLayout();
doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
@@ -1512,6 +1500,24 @@
mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
}
+ @Test
+ public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
+ final DisplayContent dc = createNewDisplay();
+ final ActivityStack stack =
+ new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
+ .setDisplay(dc)
+ .build();
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ final Configuration config = ((Configuration) args[0]);
+ assertEquals(config.windowConfiguration.getWindowingMode(),
+ config.windowConfiguration.getDisplayWindowingMode());
+ return null;
+ }).when(stack).onConfigurationChanged(any());
+ dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index add2054..13f04d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -46,7 +46,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.testutils.OffsettableClock;
@@ -244,7 +243,7 @@
}
@Test
- public void testChange() throws Exception {
+ public void testChangeToSmallerSize() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
@@ -279,8 +278,61 @@
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(
mMockLeash, app.startBounds.left, app.startBounds.top);
- verify(mMockTransaction).setWindowCrop(mMockLeash, 200, 200);
+ verify(mMockTransaction).setWindowCrop(
+ mMockLeash, app.startBounds.width(), app.startBounds.height());
verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
+ eq(record.mAdapter));
+ verify(mThumbnailFinishedCallback).onAnimationFinished(
+ eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
+ } finally {
+ mDisplayContent.mChangingContainers.clear();
+ }
+ }
+
+ @Test
+ public void testChangeTolargerSize() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mChangingContainers.add(win.mActivityRecord);
+ try {
+ final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(0, 0), null, new Rect(0, 0, 200, 200),
+ new Rect(50, 100, 150, 150));
+ assertNotNull(record.mThumbnailAdapter);
+ ((AnimationAdapter) record.mAdapter)
+ .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
+ mFinishedCallback);
+ ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
+ mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
+ mController.goodToGo();
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode);
+ assertEquals(new Point(0, 0), app.position);
+ assertEquals(new Rect(0, 0, 200, 200), app.sourceContainerBounds);
+ assertEquals(new Rect(50, 100, 150, 150), app.startBounds);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(mMockThumbnailLeash, app.startLeash);
+ assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setPosition(
+ mMockLeash, app.startBounds.left, app.startBounds.top);
+ verify(mMockTransaction).setWindowCrop(
+ mMockLeash, app.startBounds.width(), app.startBounds.height());
+ verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
finishedCaptor.getValue().onAnimationFinished();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 5dba0045..51db099 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -42,6 +42,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -57,8 +58,10 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
import android.util.Pair;
import androidx.test.filters.MediumTest;
@@ -221,6 +224,35 @@
null /* target */, null /* targetOptions */);
}
+ @Test
+ public void testAwakeFromSleepingWithAppConfiguration() {
+ final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true).build();
+ activity.moveFocusableActivityToTop("test");
+ assertTrue(activity.getStack().isFocusedStackOnDisplay());
+ ActivityRecordTests.setRotatedScreenOrientationSilently(activity);
+
+ final Configuration rotatedConfig = new Configuration();
+ display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation()
+ .rotationForOrientation(activity.getOrientation(), display.getRotation()));
+ assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
+ // Assume the activity was shown in different orientation. For example, the top activity is
+ // landscape and the portrait lockscreen is shown.
+ activity.setLastReportedConfiguration(
+ new MergedConfiguration(mService.getGlobalConfiguration(), rotatedConfig));
+ activity.setState(ActivityState.STOPPED, "sleep");
+
+ display.setIsSleeping(true);
+ doReturn(false).when(display).shouldSleep();
+ // Allow to resume when awaking.
+ setBooted(mService);
+ mRootWindowContainer.applySleepTokens(true);
+
+ // The display orientation should be changed by the activity so there is no relaunch.
+ verify(activity, never()).relaunchActivityLocked(anyBoolean());
+ assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
+ }
+
/**
* Verifies that removal of activity with task and stack is done correctly.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index d7462f8..53c2a5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
import android.os.Handler;
import android.testing.DexmakerShareClassLoaderRule;
@@ -67,6 +69,15 @@
}
/**
+ * Make the system booted, so that {@link ActivityStack#resumeTopActivityInnerLocked} can really
+ * be executed to update activity state and configuration when resuming the current top.
+ */
+ static void setBooted(ActivityTaskManagerService atmService) {
+ doReturn(false).when(atmService).isBooting();
+ doReturn(true).when(atmService).isBooted();
+ }
+
+ /**
* Utility class to compare the output of T#toString. It is convenient to have readable output
* of assertion if the string content can represent the expected states.
*/
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7595e3f..6cf0eec 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -164,6 +164,8 @@
private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
private static final int MSG_GADGET_HAL_REGISTERED = 18;
private static final int MSG_RESET_USB_GADGET = 19;
+ private static final int MSG_ACCESSORY_HANDSHAKE_TIMEOUT = 20;
+ private static final int MSG_INCREASE_SENDSTRING_COUNT = 21;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -176,6 +178,13 @@
// Request is cancelled if host does not configure device within 10 seconds.
private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000;
+ /**
+ * Timeout for receiving USB accessory request
+ * Reset when receive next control request
+ * Broadcast intent if not receive next control request or enter next step.
+ */
+ private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000;
+
private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
@@ -222,8 +231,19 @@
String accessory = event.get("ACCESSORY");
if (state != null) {
mHandler.updateState(state);
+ } else if ("GETPROTOCOL".equals(accessory)) {
+ if (DEBUG) Slog.d(TAG, "got accessory get protocol");
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ mHandler.setAccessoryUEventTime(elapsedRealtime);
+ resetAccessoryHandshakeTimeoutHandler();
+ } else if ("SENDSTRING".equals(accessory)) {
+ if (DEBUG) Slog.d(TAG, "got accessory send string");
+ mHandler.sendEmptyMessage(MSG_INCREASE_SENDSTRING_COUNT);
+ resetAccessoryHandshakeTimeoutHandler();
} else if ("START".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory start");
+ mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ mHandler.setStartAccessoryTrue();
startAccessoryMode();
}
}
@@ -395,6 +415,23 @@
mHandler.sendEmptyMessage(MSG_UPDATE_USER_RESTRICTIONS);
}
+ /*
+ * Start the timeout-timer upon receiving "get_protocol" uevent.
+ * Restart the timer every time if any succeeding uevnet received.
+ * (Only when USB is under accessory mode)
+ * <p>About the details of the related control request and sequence ordering, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ */
+ private void resetAccessoryHandshakeTimeoutHandler() {
+ long functions = getCurrentFunctions();
+ // some accesories send get_protocol after start accessory
+ if ((functions & UsbManager.FUNCTION_ACCESSORY) == 0) {
+ mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT),
+ ACCESSORY_HANDSHAKE_TIMEOUT);
+ }
+ }
+
private void startAccessoryMode() {
if (!mHasUsbAccessory) return;
@@ -416,6 +453,8 @@
if (functions != UsbManager.FUNCTION_NONE) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_MODE_ENTER_TIMEOUT),
ACCESSORY_REQUEST_TIMEOUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT),
+ ACCESSORY_HANDSHAKE_TIMEOUT);
setCurrentFunctions(functions);
}
}
@@ -468,6 +507,15 @@
private int mMidiCard;
private int mMidiDevice;
+ /**
+ * mAccessoryConnectionStartTime record the timing of start_accessory
+ * mSendStringCount count the number of receiving uevent send_string
+ * mStartAccessory record whether start_accessory is received
+ */
+ private long mAccessoryConnectionStartTime = 0L;
+ private int mSendStringCount = 0;
+ private boolean mStartAccessory = false;
+
private final Context mContext;
private final UsbAlsaManager mUsbAlsaManager;
private final UsbPermissionManager mPermissionManager;
@@ -645,6 +693,8 @@
// defer accessoryAttached if system is not ready
if (mBootCompleted) {
mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ broadcastUsbAccessoryHandshake();
} // else handle in boot completed
} else {
Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -701,6 +751,30 @@
return false;
}
+ private void broadcastUsbAccessoryHandshake() {
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long accessoryHandShakeEnd = elapsedRealtime;
+
+ // send a sticky broadcast containing USB accessory handshake information
+ Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
+ mAccessoryConnectionStartTime)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_STRING_COUNT,
+ mSendStringCount)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_START,
+ mStartAccessory)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
+ accessoryHandShakeEnd);
+
+ if (DEBUG) {
+ Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
+ }
+
+ sendStickyBroadcast(intent);
+ resetUsbAccessoryHandshakeDebuggingInfo();
+ accessoryHandShakeEnd = 0L;
+ }
+
protected void updateUsbStateBroadcastIfNeeded(long functions) {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
@@ -998,6 +1072,16 @@
}
break;
}
+ case MSG_ACCESSORY_HANDSHAKE_TIMEOUT: {
+ if (DEBUG) {
+ Slog.v(TAG, "Accessory handshake timeout");
+ }
+ broadcastUsbAccessoryHandshake();
+ break;
+ }
+ case MSG_INCREASE_SENDSTRING_COUNT: {
+ mSendStringCount = mSendStringCount + 1;
+ }
}
}
@@ -1015,6 +1099,7 @@
}
if (mCurrentAccessory != null) {
mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ broadcastUsbAccessoryHandshake();
}
updateUsbNotification(false);
@@ -1282,6 +1367,9 @@
try {
writeStringIfNotNull(dump, "kernel_state", UsbHandlerProto.KERNEL_STATE,
FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
+ } catch (FileNotFoundException exNotFound) {
+ Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: "
+ + "kernel state:" + STATE_PATH);
} catch (Exception e) {
Slog.e(TAG, "Could not read kernel state", e);
}
@@ -1290,6 +1378,9 @@
writeStringIfNotNull(dump, "kernel_function_list",
UsbHandlerProto.KERNEL_FUNCTION_LIST,
FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
+ } catch (FileNotFoundException exNotFound) {
+ Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: "
+ + "kernel function list:" + FUNCTIONS_PATH);
} catch (Exception e) {
Slog.e(TAG, "Could not read kernel function list", e);
}
@@ -1301,6 +1392,20 @@
* Evaluates USB function policies and applies the change accordingly.
*/
protected abstract void setEnabledFunctions(long functions, boolean forceRestart);
+
+ public void setAccessoryUEventTime(long accessoryConnectionStartTime) {
+ mAccessoryConnectionStartTime = accessoryConnectionStartTime;
+ }
+
+ public void setStartAccessoryTrue() {
+ mStartAccessory = true;
+ }
+
+ public void resetUsbAccessoryHandshakeDebuggingInfo() {
+ mAccessoryConnectionStartTime = 0L;
+ mSendStringCount = 0;
+ mStartAccessory = false;
+ }
}
private static final class UsbHandlerLegacy extends UsbHandler {
diff --git a/test-base/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java
index 6b79314..9f7a2fa 100644
--- a/test-base/src/android/test/InstrumentationTestCase.java
+++ b/test-base/src/android/test/InstrumentationTestCase.java
@@ -34,9 +34,9 @@
* A test case that has access to {@link Instrumentation}.
*
* @deprecated Use
- * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * <a href="{@docRoot}reference/androidx/test/platform/app/InstrumentationRegistry.html">
* InstrumentationRegistry</a> instead. New tests should be written using the
- * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ * <a href="{@docRoot}training/testing/index.html">AndroidX Test Library</a>.
*/
@Deprecated
public class InstrumentationTestCase extends TestCase {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
index 372765d..00da62f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -19,11 +19,9 @@
import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import org.junit.FixMethodOrder;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
@@ -36,8 +34,6 @@
@LargeTest
@RunWith(Parameterized.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 151632128)
-@Ignore("Waiting bug feedback")
public class OpenAppToSplitScreenTest extends NonRotationTestBase {
public OpenAppToSplitScreenTest(String beginRotationName, int beginRotation) {
@@ -67,15 +63,6 @@
}
@Test
- public void checkVisibility_dividerWindowBecomesVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
- .then()
- .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-
- @Test
public void checkVisibility_dividerLayerBecomesVisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
.hidesLayer(DOCKED_STACK_DIVIDER)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
index bd9529d..9e9c829 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
@@ -24,14 +24,12 @@
import android.view.Surface;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.UiDevice;
import org.junit.AfterClass;
import org.junit.FixMethodOrder;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
@@ -43,8 +41,6 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 140856143)
-@Ignore("Waiting bug feedback")
public class SplitScreenToLauncherTest extends FlickerTestBase {
public SplitScreenToLauncherTest() {
@@ -95,15 +91,6 @@
.showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
}
- @Test
- public void checkVisibility_dividerWindowBecomesInVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
- .then()
- .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-
@AfterClass
public static void teardown() {
UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 9e3aeef..5a29c2c 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -114,6 +114,7 @@
@Mock private INetd mNetdService;
@Mock private PackageManagerInternal mMockPmi;
@Mock private UserManager mUserManager;
+ @Mock private PermissionMonitor.Dependencies mDeps;
private PermissionMonitor mPermissionMonitor;
@@ -128,7 +129,7 @@
new UserInfo(MOCK_USER2, "", 0),
}));
- mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService));
+ mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPmi);
@@ -283,14 +284,14 @@
@Test
public void testHasRestrictedNetworkPermissionSystemUid() {
- doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt();
+ 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));
- doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt();
+ doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertFalse(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));