Merge "Implement fallback line spacing for BoringLayout"
diff --git a/core/api/current.txt b/core/api/current.txt
index e0414c0..edf6944 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3131,11 +3131,13 @@
method public void addListener(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, @Nullable android.os.Handler);
method public float getCenterX();
method public float getCenterY();
+ method @NonNull public android.graphics.Region getCurrentMagnificationRegion();
method @Nullable public android.accessibilityservice.MagnificationConfig getMagnificationConfig();
method @NonNull public android.graphics.Region getMagnificationRegion();
method public float getScale();
method public boolean removeListener(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
method public boolean reset(boolean);
+ method public boolean resetCurrentMagnification(boolean);
method public boolean setCenter(float, float, boolean);
method public boolean setMagnificationConfig(@NonNull android.accessibilityservice.MagnificationConfig, boolean);
method public boolean setScale(float, boolean);
@@ -3143,6 +3145,7 @@
public static interface AccessibilityService.MagnificationController.OnMagnificationChangedListener {
method public void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, float, float, float);
+ method public default void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, @NonNull android.accessibilityservice.MagnificationConfig);
}
public static final class AccessibilityService.ScreenshotResult {
@@ -9159,6 +9162,7 @@
field public static final int AUDIO = 2097152; // 0x200000
field public static final int CAPTURE = 524288; // 0x80000
field public static final int INFORMATION = 8388608; // 0x800000
+ field public static final int LE_AUDIO = 16384; // 0x4000
field public static final int LIMITED_DISCOVERABILITY = 8192; // 0x2000
field public static final int NETWORKING = 131072; // 0x20000
field public static final int OBJECT_TRANSFER = 1048576; // 0x100000
@@ -41700,6 +41704,7 @@
field public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_ctr_key_size_int_array";
field public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = "iwlan.diffie_hellman_groups_int_array";
field public static final String KEY_DPD_TIMER_SEC_INT = "iwlan.dpd_timer_sec_int";
+ field public static final String KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL = "iwlan.enable_support_for_eap_aka_fast_reauth_bool";
field public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = "iwlan.epdg_address_priority_int_array";
field public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT = "iwlan.epdg_authentication_method_int";
field public static final String KEY_EPDG_PCO_ID_IPV4_INT = "iwlan.epdg_pco_id_ipv4_int";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1f96a24..0991e5f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11865,11 +11865,18 @@
method public int getCallDuration();
method public int getCodecType();
method public int getDownlinkCallQualityLevel();
+ method public long getMaxPlayoutDelayMillis();
method public int getMaxRelativeJitter();
+ method public long getMinPlayoutDelayMillis();
+ method public int getNumDroppedRtpPackets();
+ method public int getNumNoDataFrames();
+ method public int getNumRtpDuplicatePackets();
method public int getNumRtpPacketsNotReceived();
method public int getNumRtpPacketsReceived();
method public int getNumRtpPacketsTransmitted();
method public int getNumRtpPacketsTransmittedLost();
+ method public int getNumRtpSidPacketsRx();
+ method public int getNumVoiceFrames();
method public int getUplinkCallQualityLevel();
method public boolean isIncomingSilenceDetectedAtCallSetup();
method public boolean isOutgoingSilenceDetectedAtCallSetup();
@@ -11884,6 +11891,32 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
}
+ public static final class CallQuality.Builder {
+ ctor public CallQuality.Builder();
+ method @NonNull public android.telephony.CallQuality build();
+ method @NonNull public android.telephony.CallQuality.Builder setAverageRelativeJitter(int);
+ method @NonNull public android.telephony.CallQuality.Builder setAverageRoundTripTime(int);
+ method @NonNull public android.telephony.CallQuality.Builder setCallDuration(int);
+ method @NonNull public android.telephony.CallQuality.Builder setCodecType(int);
+ method @NonNull public android.telephony.CallQuality.Builder setDownlinkCallQualityLevel(int);
+ method @NonNull public android.telephony.CallQuality.Builder setIncomingSilenceDetectedAtCallSetup(boolean);
+ method @NonNull public android.telephony.CallQuality.Builder setMaxPlayoutDelayMillis(long);
+ method @NonNull public android.telephony.CallQuality.Builder setMaxRelativeJitter(int);
+ method @NonNull public android.telephony.CallQuality.Builder setMinPlayoutDelayMillis(long);
+ method @NonNull public android.telephony.CallQuality.Builder setNumDroppedRtpPackets(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumNoDataFrames(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpDuplicatePackets(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsNotReceived(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsReceived(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsTransmitted(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsTransmittedLost(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpSidPacketsRx(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumVoiceFrames(int);
+ method @NonNull public android.telephony.CallQuality.Builder setOutgoingSilenceDetectedAtCallSetup(boolean);
+ method @NonNull public android.telephony.CallQuality.Builder setRtpInactivityDetected(boolean);
+ method @NonNull public android.telephony.CallQuality.Builder setUplinkCallQualityLevel(int);
+ }
+
public class CarrierConfigManager {
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName();
method @NonNull public static android.os.PersistableBundle getDefaultConfig();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 9a55867..479e6bf 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -588,7 +588,7 @@
boolean onKeyEvent(KeyEvent event);
/** Magnification changed callbacks for different displays */
void onMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY);
+ MagnificationConfig config);
/** Callbacks for receiving motion events. */
void onMotionEvent(MotionEvent event);
/** Callback for tuch state changes. */
@@ -1183,14 +1183,14 @@
}
}
- private void onMagnificationChanged(int displayId, @NonNull Region region, float scale,
- float centerX, float centerY) {
+ private void onMagnificationChanged(int displayId, @NonNull Region region,
+ MagnificationConfig config) {
MagnificationController controller;
synchronized (mLock) {
controller = mMagnificationControllers.get(displayId);
}
if (controller != null) {
- controller.dispatchMagnificationChanged(region, scale, centerX, centerY);
+ controller.dispatchMagnificationChanged(region, config);
}
}
@@ -1328,8 +1328,8 @@
* Dispatches magnification changes to any registered listeners. This
* should be called on the service's main thread.
*/
- void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
- final float centerX, final float centerY) {
+ void dispatchMagnificationChanged(final @NonNull Region region,
+ final MagnificationConfig config) {
final ArrayMap<OnMagnificationChangedListener, Handler> entries;
synchronized (mLock) {
if (mListeners == null || mListeners.isEmpty()) {
@@ -1348,16 +1348,13 @@
final OnMagnificationChangedListener listener = entries.keyAt(i);
final Handler handler = entries.valueAt(i);
if (handler != null) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- listener.onMagnificationChanged(MagnificationController.this,
- region, scale, centerX, centerY);
- }
+ handler.post(() -> {
+ listener.onMagnificationChanged(MagnificationController.this,
+ region, config);
});
} else {
// We're already on the main thread, just run the listener.
- listener.onMagnificationChanged(this, region, scale, centerX, centerY);
+ listener.onMagnificationChanged(this, region, config);
}
}
}
@@ -1503,6 +1500,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return an empty region.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the magnification region of full-screen
+ * magnification. To get the magnification region of the current controlling magnifier,
+ * use {@link #getCurrentMagnificationRegion()} instead.
+ * </p>
*
* @return the region of the screen currently active for magnification, or an empty region
* if magnification is not active.
@@ -1524,6 +1527,45 @@
}
/**
+ * Returns the region of the screen currently active for magnification if the
+ * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}.
+ * Returns the region of screen projected on the magnification window if the
+ * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}.
+ *
+ * <p>
+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+ * the returned region will be empty if the magnification is
+ * not active. And the magnification is active if magnification gestures are enabled
+ * or if a service is running that can control magnification.
+ * </p><p>
+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+ * the returned region will be empty if the magnification is not activated.
+ * </p><p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will
+ * return an empty region.
+ * </p>
+ *
+ * @return the magnification region of the currently controlling magnification
+ */
+ @NonNull
+ public Region getCurrentMagnificationRegion() {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance(mService).getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getCurrentMagnificationRegion(mDisplayId);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to obtain the current magnified region", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ return Region.obtain();
+ }
+
+ /**
* Resets magnification scale and center to their default (e.g. no
* magnification) values.
* <p>
@@ -1531,6 +1573,11 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
+ * <p>
+ * <strong>Note:</strong> This legacy API reset full-screen magnification.
+ * To reset the current controlling magnifier, use
+ * {@link #resetCurrentMagnification(boolean)} ()} instead.
+ * </p>
*
* @param animate {@code true} to animate from the current scale and
* center or {@code false} to reset the scale and center
@@ -1553,6 +1600,36 @@
}
/**
+ * Resets magnification scale and center of the controlling magnification
+ * to their default (e.g. no magnification) values.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will have
+ * no effect and return {@code false}.
+ * </p>
+ *
+ * @param animate {@code true} to animate from the current scale and
+ * center or {@code false} to reset the scale and center
+ * immediately
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public boolean resetCurrentMagnification(boolean animate) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance(mService).getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.resetCurrentMagnification(mDisplayId, animate);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to reset", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Sets the {@link MagnificationConfig}. The service controls the magnification by
* setting the config.
* <p>
@@ -1665,6 +1742,10 @@
public interface OnMagnificationChangedListener {
/**
* Called when the magnified region, scale, or center changes.
+ * <p>
+ * <strong>Note:</strong> This legacy callback notifies only full-screen
+ * magnification change.
+ * </p>
*
* @param controller the magnification controller
* @param region the magnification region
@@ -1676,6 +1757,38 @@
*/
void onMagnificationChanged(@NonNull MagnificationController controller,
@NonNull Region region, float scale, float centerX, float centerY);
+
+ /**
+ * Called when the magnified region, mode, scale, or center changes of
+ * all magnification modes.
+ * <p>
+ * <strong>Note:</strong> This method can be overridden to listen to the
+ * magnification changes of all magnification modes then the legacy callback
+ * would not receive the notifications.
+ * Skipping calling super when overriding this method results in
+ * {@link #onMagnificationChanged(MagnificationController, Region, float, float, float)}
+ * not getting called.
+ * </p>
+ *
+ * @param controller the magnification controller
+ * @param region the magnification region
+ * If the config mode is
+ * {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+ * it is the region of the screen currently active for magnification.
+ * that is the same region as {@link #getMagnificationRegion()}.
+ * If the config mode is
+ * {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+ * it is the region of screen projected on the magnification window.
+ * @param config The magnification config. That has the controlling magnification
+ * mode, the new scale and the new screen-relative center position
+ */
+ default void onMagnificationChanged(@NonNull MagnificationController controller,
+ @NonNull Region region, @NonNull MagnificationConfig config) {
+ if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) {
+ onMagnificationChanged(controller, region,
+ config.getScale(), config.getCenterX(), config.getCenterY());
+ }
+ }
}
}
@@ -2449,9 +2562,8 @@
@Override
public void onMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
- AccessibilityService.this.onMagnificationChanged(displayId, region, scale,
- centerX, centerY);
+ MagnificationConfig config) {
+ AccessibilityService.this.onMagnificationChanged(displayId, region, config);
}
@Override
@@ -2575,12 +2687,10 @@
/** Magnification changed callbacks for different displays */
public void onMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ MagnificationConfig config) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = region;
- args.arg2 = scale;
- args.arg3 = centerX;
- args.arg4 = centerY;
+ args.arg2 = config;
args.argi1 = displayId;
final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
@@ -2739,13 +2849,10 @@
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final SomeArgs args = (SomeArgs) message.obj;
final Region region = (Region) args.arg1;
- final float scale = (float) args.arg2;
- final float centerX = (float) args.arg3;
- final float centerY = (float) args.arg4;
+ final MagnificationConfig config = (MagnificationConfig) args.arg2;
final int displayId = args.argi1;
args.recycle();
- mCallback.onMagnificationChanged(displayId, region, scale,
- centerX, centerY);
+ mCallback.onMagnificationChanged(displayId, region, config);
}
return;
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 651c50f..375383d 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -21,6 +21,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
import android.accessibilityservice.AccessibilityGestureEvent;
+import android.accessibilityservice.MagnificationConfig;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -43,7 +44,7 @@
void onKeyEvent(in KeyEvent event, int sequence);
- void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY);
+ void onMagnificationChanged(int displayId, in Region region, in MagnificationConfig config);
void onMotionEvent(in MotionEvent event);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7d76bbf..2cc15b4 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -88,8 +88,12 @@
Region getMagnificationRegion(int displayId);
+ Region getCurrentMagnificationRegion(int displayId);
+
boolean resetMagnification(int displayId, boolean animate);
+ boolean resetCurrentMagnification(int displayId, boolean animate);
+
boolean setMagnificationConfig(int displayId, in MagnificationConfig config, boolean animate);
void setMagnificationCallbackEnabled(int displayId, boolean enabled);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 2bbf280..fa9de6e 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -384,7 +384,7 @@
new PropertyInvalidatedCache<UserIdPackage, Account[]>(
CACHE_ACCOUNTS_DATA_SIZE, CACHE_KEY_ACCOUNTS_DATA_PROPERTY) {
@Override
- protected Account[] recompute(UserIdPackage userAndPackage) {
+ public Account[] recompute(UserIdPackage userAndPackage) {
try {
return mService.getAccountsAsUser(null, userAndPackage.userId, userAndPackage.packageName);
} catch (RemoteException e) {
@@ -392,11 +392,11 @@
}
}
@Override
- protected boolean bypass(UserIdPackage query) {
+ public boolean bypass(UserIdPackage query) {
return query.userId < 0;
}
@Override
- protected boolean debugCompareQueryResults(Account[] l, Account[] r) {
+ public boolean resultEquals(Account[] l, Account[] r) {
if (l == r) {
return true;
} else if (l == null || r == null) {
@@ -455,7 +455,7 @@
new PropertyInvalidatedCache<AccountKeyData, String>(CACHE_USER_DATA_SIZE,
CACHE_KEY_USER_DATA_PROPERTY) {
@Override
- protected String recompute(AccountKeyData accountKeyData) {
+ public String recompute(AccountKeyData accountKeyData) {
Account account = accountKeyData.account;
String key = accountKeyData.key;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1778ea4..b2c9439 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4645,12 +4645,14 @@
}
private void handleDumpGfxInfo(DumpComponentInfo info) {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
ThreadedRenderer.handleDumpGfxInfo(info.fd.getFileDescriptor(), info.args);
} catch (Exception e) {
Log.w(TAG, "Caught exception from dumpGfxInfo()", e);
} finally {
IoUtils.closeQuietly(info.fd);
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 44fb5db..49c75c4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -802,7 +802,7 @@
new PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>(
256, "cache_key.has_system_feature") {
@Override
- protected Boolean recompute(HasSystemFeatureQuery query) {
+ public Boolean recompute(HasSystemFeatureQuery query) {
try {
return ActivityThread.currentActivityThread().getPackageManager().
hasSystemFeature(query.name, query.version);
@@ -1098,7 +1098,7 @@
new PropertyInvalidatedCache<Integer, GetPackagesForUidResult>(
32, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) {
@Override
- protected GetPackagesForUidResult recompute(Integer uid) {
+ public GetPackagesForUidResult recompute(Integer uid) {
try {
return new GetPackagesForUidResult(
ActivityThread.currentActivityThread().
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c895636..f3e9f10 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1796,7 +1796,6 @@
&& ((flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
flags = flags | Context.RECEIVER_EXPORTED;
}
-
final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
@@ -1811,9 +1810,6 @@
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } catch (WtfException e) {
- Log.wtf(TAG, e.getMessage());
- return null;
}
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index ef4d7b1..978160c 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -505,13 +505,13 @@
* block. If this function returns null, the result of the cache query is null. There is no
* "negative cache" in the query: we don't cache null results at all.
*/
- protected abstract Result recompute(Query query);
+ public abstract Result recompute(Query query);
/**
* Return true if the query should bypass the cache. The default behavior is to
* always use the cache but the method can be overridden for a specific class.
*/
- protected boolean bypass(Query query) {
+ public boolean bypass(Query query) {
return false;
}
@@ -519,7 +519,7 @@
* Determines if a pair of responses are considered equal. Used to determine whether
* a cache is inadvertently returning stale results when VERIFY is set to true.
*/
- protected boolean debugCompareQueryResults(Result cachedResult, Result fetchedResult) {
+ protected boolean resultEquals(Result cachedResult, Result fetchedResult) {
// If a service crashes and returns a null result, the cached value remains valid.
if (fetchedResult != null) {
return Objects.equals(cachedResult, fetchedResult);
@@ -990,11 +990,11 @@
}
}
- protected Result maybeCheckConsistency(Query query, Result proposedResult) {
+ private Result maybeCheckConsistency(Query query, Result proposedResult) {
if (VERIFY) {
Result resultToCompare = recompute(query);
boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
- if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
+ if (!nonceChanged && !resultEquals(proposedResult, resultToCompare)) {
Log.e(TAG, TextUtils.formatSimple(
"cache %s inconsistent for %s is %s should be %s",
cacheName(), queryToString(query),
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 58ded71..00903a8 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -22,6 +22,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.MagnificationConfig;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1581,7 +1582,7 @@
@Override
public void onMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ MagnificationConfig config) {
/* do nothing */
}
diff --git a/core/java/android/app/WtfException.java b/core/java/android/app/WtfException.java
deleted file mode 100644
index ba8dbef..0000000
--- a/core/java/android/app/WtfException.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Exception meant to be thrown instead of calling Log.wtf() such that server side code can
- * throw this exception, and it will carry across the binder to do client side logging.
- * {@hide}
- */
-public final class WtfException extends RuntimeException implements Parcelable {
- public static final @android.annotation.NonNull
- Creator<WtfException> CREATOR = new Creator<WtfException>() {
- @Override
- public WtfException createFromParcel(Parcel source) {
- return new WtfException(source.readString8());
- }
-
- @Override
- public WtfException[] newArray(int size) {
- return new WtfException[size];
- }
- };
-
- public WtfException(@android.annotation.NonNull String message) {
- super(message);
- }
-
- /** {@hide} */
- public static Throwable readFromParcel(Parcel in) {
- final String msg = in.readString8();
- return new WtfException(msg);
- }
-
- /** {@hide} */
- public static void writeToParcel(Parcel out, Throwable t) {
- out.writeString8(t.getMessage());
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
- dest.writeString8(getMessage());
- }
-}
-
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index 3d0bf91..acd404b 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -84,7 +84,7 @@
}
@Override
- protected Boolean recompute(ChangeIdStateQuery query) {
+ public Boolean recompute(ChangeIdStateQuery query) {
final long token = Binder.clearCallingIdentity();
try {
if (query.type == ChangeIdStateQuery.QUERY_BY_PACKAGE_NAME) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9a0f02e..856a8e1 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1062,7 +1062,7 @@
8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Integer recompute(Void query) {
+ public Integer recompute(Void query) {
try {
return mService.getState();
} catch (RemoteException e) {
@@ -2085,7 +2085,7 @@
8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Boolean recompute(Void query) {
+ public Boolean recompute(Void query) {
try {
mServiceLock.readLock().lock();
if (mService != null) {
@@ -2540,7 +2540,7 @@
*/
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Integer recompute(Void query) {
+ public Integer recompute(Void query) {
try {
return mService.getAdapterConnectionState();
} catch (RemoteException e) {
@@ -2605,7 +2605,7 @@
8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Integer recompute(Integer query) {
+ public Integer recompute(Integer query) {
try {
mServiceLock.readLock().lock();
if (mService != null) {
@@ -3064,9 +3064,6 @@
BluetoothCsipSetCoordinator csipSetCoordinator =
new BluetoothCsipSetCoordinator(context, listener, this);
return true;
- } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
- BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
- return true;
} else {
return false;
}
@@ -3169,10 +3166,6 @@
(BluetoothCsipSetCoordinator) proxy;
csipSetCoordinator.close();
break;
- case BluetoothProfile.LE_CALL_CONTROL:
- BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
- tbs.close();
- break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 69525b5..a3c45d0 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -120,6 +120,7 @@
private static final int BITMASK = 0xFFE000;
public static final int LIMITED_DISCOVERABILITY = 0x002000;
+ public static final int LE_AUDIO = 0x004000;
public static final int POSITIONING = 0x010000;
public static final int NETWORKING = 0x020000;
public static final int RENDER = 0x040000;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 93f0268..fc99942 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1604,7 +1604,7 @@
8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Integer recompute(BluetoothDevice query) {
+ public Integer recompute(BluetoothDevice query) {
try {
return sService.getBondState(query, mAttributionSource);
} catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
new file mode 100644
index 0000000..b866cce
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.bluetooth.le.ScanResult;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class provides a set of callbacks that are invoked when scanning for Broadcast Sources is
+ * offloaded to a Broadcast Assistant.
+ *
+ * <p>An LE Audio Broadcast Assistant can help a Broadcast Sink to scan for available Broadcast
+ * Sources. The Broadcast Sink achieves this by offloading the scan to a Broadcast Assistant. This
+ * is facilitated by the Broadcast Audio Scan Service (BASS). A BASS server is a GATT server that is
+ * part of the Scan Delegator on a Broadcast Sink. A BASS client instead runs on the Broadcast
+ * Assistant.
+ *
+ * <p>Once a GATT connection is established between the BASS client and the BASS server, the
+ * Broadcast Sink can offload the scans to the Broadcast Assistant. Upon finding new Broadcast
+ * Sources, the Broadcast Assistant then notifies the Broadcast Sink about these over the
+ * established GATT connection. The Scan Delegator on the Broadcast Sink can also notify the
+ * Assistant about changes such as addition and removal of Broadcast Sources.
+ *
+ * @hide
+ */
+public abstract class BluetoothLeBroadcastAssistantCallback {
+
+ /**
+ * Broadcast Audio Scan Service (BASS) codes returned by a BASS Server
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = "BASS_STATUS_",
+ value = {
+ BASS_STATUS_SUCCESS,
+ BASS_STATUS_FAILURE,
+ BASS_STATUS_INVALID_GATT_HANDLE,
+ BASS_STATUS_TXN_TIMEOUT,
+ BASS_STATUS_INVALID_SOURCE_ID,
+ BASS_STATUS_COLOCATED_SRC_UNAVAILABLE,
+ BASS_STATUS_INVALID_SOURCE_SELECTED,
+ BASS_STATUS_SOURCE_UNAVAILABLE,
+ BASS_STATUS_DUPLICATE_ADDITION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BassStatus {}
+
+ public static final int BASS_STATUS_SUCCESS = 0x00;
+ public static final int BASS_STATUS_FAILURE = 0x01;
+ public static final int BASS_STATUS_INVALID_GATT_HANDLE = 0x02;
+ public static final int BASS_STATUS_TXN_TIMEOUT = 0x03;
+
+ public static final int BASS_STATUS_INVALID_SOURCE_ID = 0x04;
+ public static final int BASS_STATUS_COLOCATED_SRC_UNAVAILABLE = 0x05;
+ public static final int BASS_STATUS_INVALID_SOURCE_SELECTED = 0x06;
+ public static final int BASS_STATUS_SOURCE_UNAVAILABLE = 0x07;
+ public static final int BASS_STATUS_DUPLICATE_ADDITION = 0x08;
+ public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09;
+ public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10;
+
+ /**
+ * Callback invoked when a new LE Audio Broadcast Source is found.
+ *
+ * @param result {@link ScanResult} scan result representing a Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceFound(@NonNull ScanResult result) {}
+
+ /**
+ * Callback invoked when the Broadcast Assistant synchronizes with Periodic Advertisements (PAs)
+ * of an LE Audio Broadcast Source.
+ *
+ * @param source the selected Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceSelected(
+ @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
+
+ /**
+ * Callback invoked when the Broadcast Assistant loses synchronization with an LE Audio
+ * Broadcast Source.
+ *
+ * @param source the Broadcast Source with which synchronization was lost
+ */
+ public void onBluetoothLeBroadcastSourceLost(
+ @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
+
+ /**
+ * Callback invoked when a new LE Audio Broadcast Source has been successfully added to the Scan
+ * Delegator (within a Broadcast Sink, for example).
+ *
+ * @param sink Scan Delegator device on which a new Broadcast Source has been added
+ * @param source the added Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceAdded(
+ @NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastSourceInfo source,
+ @BassStatus int status) {}
+
+ /**
+ * Callback invoked when an existing LE Audio Broadcast Source within a remote Scan Delegator
+ * has been updated.
+ *
+ * @param sink Scan Delegator device on which a Broadcast Source has been updated
+ * @param source the updated Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceUpdated(
+ @NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastSourceInfo source,
+ @BassStatus int status) {}
+
+ /**
+ * Callback invoked when an LE Audio Broadcast Source has been successfully removed from the
+ * Scan Delegator (within a Broadcast Sink, for example).
+ *
+ * @param sink Scan Delegator device from which a Broadcast Source has been removed
+ * @param source the removed Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceRemoved(
+ @NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastSourceInfo source,
+ @BassStatus int status) {}
+}
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
deleted file mode 100644
index fb7789d..0000000
--- a/core/java/android/bluetooth/BluetoothLeCall.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright 2021 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.ParcelUuid;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Representation of Call
- *
- * @hide
- */
-public final class BluetoothLeCall implements Parcelable {
-
- /** @hide */
- @IntDef(prefix = "STATE_", value = {
- STATE_INCOMING,
- STATE_DIALING,
- STATE_ALERTING,
- STATE_ACTIVE,
- STATE_LOCALLY_HELD,
- STATE_REMOTELY_HELD,
- STATE_LOCALLY_AND_REMOTELY_HELD
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface State {
- }
-
- /**
- * A remote party is calling (incoming call).
- *
- * @hide
- */
- public static final int STATE_INCOMING = 0x00;
-
- /**
- * The process to call the remote party has started but the remote party is not
- * being alerted (outgoing call).
- *
- * @hide
- */
- public static final int STATE_DIALING = 0x01;
-
- /**
- * A remote party is being alerted (outgoing call).
- *
- * @hide
- */
- public static final int STATE_ALERTING = 0x02;
-
- /**
- * The call is in an active conversation.
- *
- * @hide
- */
- public static final int STATE_ACTIVE = 0x03;
-
- /**
- * The call is connected but held locally. “Locally Held” implies that either
- * the server or the client can affect the state.
- *
- * @hide
- */
- public static final int STATE_LOCALLY_HELD = 0x04;
-
- /**
- * The call is connected but held remotely. “Remotely Held” means that the state
- * is controlled by the remote party of a call.
- *
- * @hide
- */
- public static final int STATE_REMOTELY_HELD = 0x05;
-
- /**
- * The call is connected but held both locally and remotely.
- *
- * @hide
- */
- public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
-
- /**
- * Whether the call direction is outgoing.
- *
- * @hide
- */
- public static final int FLAG_OUTGOING_CALL = 0x00000001;
-
- /**
- * Whether the call URI and Friendly Name are withheld by server.
- *
- * @hide
- */
- public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
-
- /**
- * Whether the call URI and Friendly Name are withheld by network.
- *
- * @hide
- */
- public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
-
- /** Unique UUID that identifies this call */
- private UUID mUuid;
-
- /** Remote Caller URI */
- private String mUri;
-
- /** Caller friendly name */
- private String mFriendlyName;
-
- /** Call state */
- private @State int mState;
-
- /** Call flags */
- private int mCallFlags;
-
- /** @hide */
- public BluetoothLeCall(@NonNull BluetoothLeCall that) {
- mUuid = new UUID(that.getUuid().getMostSignificantBits(),
- that.getUuid().getLeastSignificantBits());
- mUri = that.mUri;
- mFriendlyName = that.mFriendlyName;
- mState = that.mState;
- mCallFlags = that.mCallFlags;
- }
-
- /** @hide */
- public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
- @State int state, int callFlags) {
- mUuid = uuid;
- mUri = uri;
- mFriendlyName = friendlyName;
- mState = state;
- mCallFlags = callFlags;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
- BluetoothLeCall that = (BluetoothLeCall) o;
- return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
- && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
- && mCallFlags == that.mCallFlags;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
- }
-
- /**
- * Returns a string representation of this BluetoothLeCall.
- *
- * <p>
- * Currently this is the UUID.
- *
- * @return string representation of this BluetoothLeCall
- */
- @Override
- public String toString() {
- return mUuid.toString();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeParcelable(new ParcelUuid(mUuid), 0);
- out.writeString(mUri);
- out.writeString(mFriendlyName);
- out.writeInt(mState);
- out.writeInt(mCallFlags);
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
- new Parcelable.Creator<BluetoothLeCall>() {
- public BluetoothLeCall createFromParcel(Parcel in) {
- return new BluetoothLeCall(in);
- }
-
- public BluetoothLeCall[] newArray(int size) {
- return new BluetoothLeCall[size];
- }
- };
-
- private BluetoothLeCall(Parcel in) {
- mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
- mUri = in.readString();
- mFriendlyName = in.readString();
- mState = in.readInt();
- mCallFlags = in.readInt();
- }
-
- /**
- * Returns an UUID of this BluetoothLeCall.
- *
- * <p>
- * An UUID is unique identifier of a BluetoothLeCall.
- *
- * @return UUID of this BluetoothLeCall
- * @hide
- */
- public @NonNull UUID getUuid() {
- return mUuid;
- }
-
- /**
- * Returns a URI of the remote party of this BluetoothLeCall.
- *
- * @return string representation of this BluetoothLeCall
- * @hide
- */
- public @NonNull String getUri() {
- return mUri;
- }
-
- /**
- * Returns a friendly name of the call.
- *
- * @return friendly name representation of this BluetoothLeCall
- * @hide
- */
- public @NonNull String getFriendlyName() {
- return mFriendlyName;
- }
-
- /**
- * Returns the call state.
- *
- * @return the state of this BluetoothLeCall
- * @hide
- */
- public @State int getState() {
- return mState;
- }
-
- /**
- * Returns the call flags.
- *
- * @return call flags
- * @hide
- */
- public int getCallFlags() {
- return mCallFlags;
- }
-
- /**
- * Whether the call direction is incoming.
- *
- * @return true if incoming call, false otherwise
- * @hide
- */
- public boolean isIncomingCall() {
- return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
- }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
deleted file mode 100644
index 5283e08..0000000
--- a/core/java/android/bluetooth/BluetoothLeCallControl.java
+++ /dev/null
@@ -1,911 +0,0 @@
-/*
- * Copyright 2019 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-import android.annotation.SuppressLint;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.Executor;
-
-/**
- * This class provides the APIs to control the Call Control profile.
- *
- * <p>
- * This class provides Bluetooth Telephone Bearer Service functionality,
- * allowing applications to expose a GATT Service based interface to control the
- * state of the calls by remote devices such as LE audio devices.
- *
- * <p>
- * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
- * BluetoothLeCallControl proxy object.
- *
- * @hide
- */
-public final class BluetoothLeCallControl implements BluetoothProfile {
- private static final String TAG = "BluetoothLeCallControl";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- /** @hide */
- @IntDef(prefix = "RESULT_", value = {
- RESULT_SUCCESS,
- RESULT_ERROR_UNKNOWN_CALL_ID,
- RESULT_ERROR_INVALID_URI,
- RESULT_ERROR_APPLICATION
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Result {
- }
-
- /**
- * Opcode write was successful.
- *
- * @hide
- */
- public static final int RESULT_SUCCESS = 0;
-
- /**
- * Unknown call Id has been used in the operation.
- *
- * @hide
- */
- public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
-
- /**
- * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
- *
- * @hide
- */
- public static final int RESULT_ERROR_INVALID_URI = 2;
-
- /**
- * Application internal error.
- *
- * @hide
- */
- public static final int RESULT_ERROR_APPLICATION = 3;
-
- /** @hide */
- @IntDef(prefix = "TERMINATION_REASON_", value = {
- TERMINATION_REASON_INVALID_URI,
- TERMINATION_REASON_FAIL,
- TERMINATION_REASON_REMOTE_HANGUP,
- TERMINATION_REASON_SERVER_HANGUP,
- TERMINATION_REASON_LINE_BUSY,
- TERMINATION_REASON_NETWORK_CONGESTION,
- TERMINATION_REASON_CLIENT_HANGUP,
- TERMINATION_REASON_NO_SERVICE,
- TERMINATION_REASON_NO_ANSWER
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TerminationReason {
- }
-
- /**
- * Remote Caller ID value used to place a call was formed improperly.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_INVALID_URI = 0x00;
-
- /**
- * Call fail.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_FAIL = 0x01;
-
- /**
- * Remote party ended call.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
-
- /**
- * Call ended from the server.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
-
- /**
- * Line busy.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
-
- /**
- * Network congestion.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
-
- /**
- * Client terminated.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
-
- /**
- * No service.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
-
- /**
- * No answer.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
-
- /*
- * Flag indicating support for hold/unhold call feature.
- *
- * @hide
- */
- public static final int CAPABILITY_HOLD_CALL = 0x00000001;
-
- /**
- * Flag indicating support for joining calls feature.
- *
- * @hide
- */
- public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
-
- private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
- private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
-
- private static final int REG_TIMEOUT = 10000;
-
- /**
- * The template class is used to call callback functions on events from the TBS
- * server. Callback functions are wrapped in this class and registered to the
- * Android system during app registration.
- *
- * @hide
- */
- public abstract static class Callback {
-
- private static final String TAG = "BluetoothLeCallControl.Callback";
-
- /**
- * Called when a remote client requested to accept the call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The call Id requested to be accepted
- * @hide
- */
- public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
-
- /**
- * A remote client has requested to terminate the call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The call Id requested to terminate
- * @hide
- */
- public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
-
- /**
- * A remote client has requested to hold the call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The call Id requested to be put on hold
- * @hide
- */
- public void onHoldCall(int requestId, @NonNull UUID callId) {
- Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
- }
-
- /**
- * A remote client has requested to unhold the call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The call Id requested to unhold
- * @hide
- */
- public void onUnholdCall(int requestId, @NonNull UUID callId) {
- Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
- }
-
- /**
- * A remote client has requested to place a call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The Id to be assigned for the new call
- * @param uri The caller URI requested
- * @hide
- */
- public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
-
- /**
- * A remote client has requested to join the calls.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callIds The call Id list requested to join
- * @hide
- */
- public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
- Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
- }
- }
-
- private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
-
- private final Executor mExecutor;
- private final Callback mCallback;
-
- CallbackWrapper(Executor executor, Callback callback) {
- mExecutor = executor;
- mCallback = callback;
- }
-
- @Override
- public void onBearerRegistered(int ccid) {
- synchronized (mServerIfLock) {
- if (mCallback != null) {
- mCcid = ccid;
- mServerIfLock.notifyAll();
- } else {
- // registration timeout
- Log.e(TAG, "onBearerRegistered: mCallback is null");
- }
- }
- }
-
- @Override
- public void onAcceptCall(int requestId, ParcelUuid uuid) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onTerminateCall(int requestId, ParcelUuid uuid) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onHoldCall(int requestId, ParcelUuid uuid) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onUnholdCall(int requestId, ParcelUuid uuid) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
- List<UUID> uuids = new ArrayList<>();
- for (ParcelUuid parcelUuid : parcelUuids) {
- uuids.add(parcelUuid.getUuid());
- }
-
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
- };
-
- private Context mContext;
- private ServiceListener mServiceListener;
- private volatile IBluetoothLeCallControl mService;
- private BluetoothAdapter mAdapter;
- private int mCcid = 0;
- private String mToken;
- private Callback mCallback = null;
- private Object mServerIfLock = new Object();
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG)
- Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- doUnbind();
- } else {
- doBind();
- }
- }
- };
-
- /**
- * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
- * telephone bearer service.
- */
- /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
- mContext = context;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- mServiceListener = listener;
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- private boolean doBind() {
- synchronized (mConnection) {
- if (mService == null) {
- if (VDBG)
- Log.d(TAG, "Binding service...");
- try {
- return mAdapter.getBluetoothManager().
- bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
- mConnection);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to bind TelephoneBearerService", e);
- }
- }
- }
- return false;
- }
-
- private void doUnbind() {
- synchronized (mConnection) {
- if (mService != null) {
- if (VDBG)
- Log.d(TAG, "Unbinding service...");
- try {
- mAdapter.getBluetoothManager().
- unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
- mConnection);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
- } finally {
- mService = null;
- }
- }
- }
- }
-
- /* package */ void close() {
- if (VDBG)
- log("close()");
- unregisterBearer();
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "", re);
- }
- }
- mServiceListener = null;
- doUnbind();
- }
-
- private IBluetoothLeCallControl getService() {
- return mService;
- }
-
- /**
- * Not supported
- *
- * @throws UnsupportedOperationException
- */
- @Override
- public int getConnectionState(@Nullable BluetoothDevice device) {
- throw new UnsupportedOperationException("not supported");
- }
-
- /**
- * Not supported
- *
- * @throws UnsupportedOperationException
- */
- @Override
- public @NonNull List<BluetoothDevice> getConnectedDevices() {
- throw new UnsupportedOperationException("not supported");
- }
-
- /**
- * Not supported
- *
- * @throws UnsupportedOperationException
- */
- @Override
- public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
- @NonNull int[] states) {
- throw new UnsupportedOperationException("not supported");
- }
-
- /**
- * Register Telephone Bearer exposing the interface that allows remote devices
- * to track and control the call states.
- *
- * <p>
- * This is an asynchronous call. The callback is used to notify success or
- * failure if the function returns true.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * <!-- The UCI is a String identifier of the telephone bearer as defined at
- * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
- * (login required). -->
- *
- * <!-- The examples of common URI schemes can be found in
- * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
- *
- * <!-- The Technology is an integer value. The possible values are defined at
- * https://www.bluetooth.com/specifications/assigned-numbers (login required).
- * -->
- *
- * @param uci Bearer Unique Client Identifier
- * @param uriSchemes URI Schemes supported list
- * @param capabilities bearer capabilities
- * @param provider Network provider name
- * @param technology Network technology
- * @param executor {@link Executor} object on which callback will be
- * executed. The Executor object is required.
- * @param callback {@link Callback} object to which callback messages will
- * be sent. The Callback object is required.
- * @return true on success, false otherwise
- * @hide
- */
- @SuppressLint("ExecutorRegistration")
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public boolean registerBearer(@Nullable String uci,
- @NonNull List<String> uriSchemes, int capabilities,
- @NonNull String provider, int technology,
- @NonNull Executor executor, @NonNull Callback callback) {
- if (DBG) {
- Log.d(TAG, "registerBearer");
- }
- if (callback == null) {
- throw new IllegalArgumentException("null parameter: " + callback);
- }
- if (mCcid != 0) {
- return false;
- }
-
- mToken = uci;
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- synchronized (mServerIfLock) {
- if (mCallback != null) {
- Log.e(TAG, "Bearer can be opened only once");
- return false;
- }
-
- mCallback = callback;
- try {
- CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
- service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
- provider, technology);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- mCallback = null;
- return false;
- }
-
- try {
- mServerIfLock.wait(REG_TIMEOUT);
- } catch (InterruptedException e) {
- Log.e(TAG, "" + e);
- mCallback = null;
- }
-
- if (mCcid == 0) {
- mCallback = null;
- return false;
- }
-
- return true;
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
-
- return false;
- }
-
- /**
- * Unregister Telephone Bearer Service and destroy all the associated data.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void unregisterBearer() {
- if (DBG) {
- Log.d(TAG, "unregisterBearer");
- }
- if (mCcid == 0) {
- return;
- }
-
- int ccid = mCcid;
- mCcid = 0;
- mCallback = null;
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.unregisterBearer(mToken);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Get the Content Control ID (CCID) value.
- *
- * @return ccid Content Control ID value
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public int getContentControlId() {
- return mCcid;
- }
-
- /**
- * Notify about the newly added call.
- *
- * <p>
- * This shall be called as early as possible after the call has been added.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param call Newly added call
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void onCallAdded(@NonNull BluetoothLeCall call) {
- if (DBG) {
- Log.d(TAG, "onCallAdded: call=" + call);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.callAdded(mCcid, call);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Notify about the removed call.
- *
- * <p>
- * This shall be called as early as possible after the call has been removed.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param callId The Id of a call that has been removed
- * @param reason Call termination reason
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
- if (DBG) {
- Log.d(TAG, "callRemoved: callId=" + callId);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.callRemoved(mCcid, new ParcelUuid(callId), reason);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Notify the call state change
- *
- * <p>
- * This shall be called as early as possible after the state of the call has
- * changed.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param callId The call Id that state has been changed
- * @param state Call state
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
- if (DBG) {
- Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.callStateChanged(mCcid, new ParcelUuid(callId), state);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Provide the current calls list
- *
- * <p>
- * This function must be invoked after registration if application has any
- * calls.
- *
- * @param calls current calls list
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.currentCallsList(mCcid, calls);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- }
-
- /**
- * Provide the network current status
- *
- * <p>
- * This function must be invoked on change of network state.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * <!-- The Technology is an integer value. The possible values are defined at
- * https://www.bluetooth.com/specifications/assigned-numbers (login required).
- * -->
- *
- * @param provider Network provider name
- * @param technology Network technology
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void networkStateChanged(@NonNull String provider, int technology) {
- if (DBG) {
- Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.networkStateChanged(mCcid, provider, technology);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Send a response to a call control request to a remote device.
- *
- * <p>
- * This function must be invoked in when a request is received by one of these
- * callback methods:
- *
- * <ul>
- * <li>{@link Callback#onAcceptCall}
- * <li>{@link Callback#onTerminateCall}
- * <li>{@link Callback#onHoldCall}
- * <li>{@link Callback#onUnholdCall}
- * <li>{@link Callback#onPlaceCall}
- * <li>{@link Callback#onJoinCalls}
- * </ul>
- *
- * @param requestId The ID of the request that was received with the callback
- * @param result The result of the request to be sent to the remote devices
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void requestResult(int requestId, @Result int result) {
- if (DBG) {
- Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.requestResult(mCcid, requestId, result);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- }
-
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- private static boolean isValidDevice(@Nullable BluetoothDevice device) {
- return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
- }
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
-
- private final IBluetoothProfileServiceConnection mConnection =
- new IBluetoothProfileServiceConnection.Stub() {
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) {
- Log.d(TAG, "Proxy object connected");
- }
- mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) {
- Log.d(TAG, "Proxy object disconnected");
- }
- doUnbind();
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
- }
- };
-
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_TBS_SERVICE_CONNECTED: {
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
- BluetoothLeCallControl.this);
- }
- break;
- }
- case MESSAGE_TBS_SERVICE_DISCONNECTED: {
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
- }
- break;
- }
- }
- }
- };
-}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index d0f74e9..e047e5d 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -240,19 +240,12 @@
int LE_AUDIO_BROADCAST = 26;
/**
- * @hide
- * Telephone Bearer Service from Call Control Profile
- *
- */
- int LE_CALL_CONTROL = 27;
-
- /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 27;
+ int MAX_PROFILE_ID = 26;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 01d231c..7b9d37e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2670,6 +2670,9 @@
* {@link ContentObserver#onChange(boolean, Collection, int, UserHandle)} should be
* overwritten to get the corresponding {@link UserHandle} for that notification.
*
+ * <p> If you don't hold the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission, you can register the {@link ContentObserver} only for current user.
+ *
* @param uri The URI to watch for changes. This can be a specific row URI,
* or a base URI for a whole class of content.
* @param notifyForDescendants When false, the observer will be notified
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 338dfd6..819cbb0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -10187,16 +10187,15 @@
16, PermissionManager.CACHE_KEY_PACKAGE_INFO,
"getApplicationInfo") {
@Override
- protected ApplicationInfo recompute(ApplicationInfoQuery query) {
+ public ApplicationInfo recompute(ApplicationInfoQuery query) {
return getApplicationInfoAsUserUncached(
query.packageName, query.flags, query.userId);
}
@Override
- protected ApplicationInfo maybeCheckConsistency(
- ApplicationInfoQuery query, ApplicationInfo proposedResult) {
+ public boolean resultEquals(ApplicationInfo cached, ApplicationInfo fetched) {
// Implementing this debug check for ApplicationInfo would require a
// complicated deep comparison, so just bypass it for now.
- return proposedResult;
+ return true;
}
};
@@ -10289,16 +10288,15 @@
32, PermissionManager.CACHE_KEY_PACKAGE_INFO,
"getPackageInfo") {
@Override
- protected PackageInfo recompute(PackageInfoQuery query) {
+ public PackageInfo recompute(PackageInfoQuery query) {
return getPackageInfoAsUserUncached(
query.packageName, query.flags, query.userId);
}
@Override
- protected PackageInfo maybeCheckConsistency(
- PackageInfoQuery query, PackageInfo proposedResult) {
+ public boolean resultEquals(PackageInfo cached, PackageInfo fetched) {
// Implementing this debug check for PackageInfo would require a
// complicated deep comparison, so just bypass it for now.
- return proposedResult;
+ return true;
}
};
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index dbd3d5c..23cae4c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -1152,7 +1152,7 @@
*/
@Nullable
private ArrayMap<String, String> buildAppClassNamesByProcess() {
- if (processes == null) {
+ if (ArrayUtils.size(processes) == 0) {
return null;
}
final ArrayMap<String, String> ret = new ArrayMap<>(4);
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index fd1a331..6c83057 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -78,7 +78,7 @@
public static boolean isSurfaceForHwVideoEncoder(Surface surface) {
checkNotNull(surface);
long usageFlags = nativeDetectSurfaceUsageFlags(surface);
- long disallowedFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | USAGE_HW_COMPOSER
+ long disallowedFlags = USAGE_HW_COMPOSER
| USAGE_RENDERSCRIPT | HardwareBuffer.USAGE_CPU_READ_OFTEN;
long allowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE;
boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0037464..de5c9ad 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1206,6 +1206,17 @@
}
/**
+ * Returns whether the specified display supports DISPLAY_DECORATION.
+ *
+ * @param displayId The display to query support.
+ *
+ * @hide
+ */
+ public boolean getDisplayDecorationSupport(int displayId) {
+ return mGlobal.getDisplayDecorationSupport(displayId);
+ }
+
+ /**
* Returns the user preference for "Match content frame rate".
* <p>
* Never: Even if the app requests it, the device will never try to match its output to the
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index e731165..bf6e665 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -128,7 +128,7 @@
8, // size of display cache
CACHE_KEY_DISPLAY_INFO_PROPERTY) {
@Override
- protected DisplayInfo recompute(Integer id) {
+ public DisplayInfo recompute(Integer id) {
try {
return mDm.getDisplayInfo(id);
} catch (RemoteException ex) {
@@ -812,6 +812,21 @@
}
/**
+ * Report whether the display supports DISPLAY_DECORATION.
+ *
+ * @param displayId The display whose support is being queried.
+ *
+ * @hide
+ */
+ public boolean getDisplayDecorationSupport(int displayId) {
+ try {
+ return mDm.getDisplayDecorationSupport(displayId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the brightness of the display.
*
* @param displayId The display from which to get the brightness
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 82b31d4..d38d388 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -180,4 +180,7 @@
// Returns the refresh rate switching type.
int getRefreshRateSwitchingType();
+
+ // Query for DISPLAY_DECORATION support.
+ boolean getDisplayDecorationSupport(int displayId);
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 56f8142..b970559 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -306,22 +306,21 @@
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollment already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollment already canceled");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enroll");
- mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
- mContext.getOpPackageName(), disabledFeatures, previewSurface,
- debugConsent);
+ final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken,
+ mServiceReceiver, mContext.getOpPackageName(), disabledFeatures,
+ previewSurface, debugConsent);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
@@ -359,21 +358,20 @@
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollRemotely is already canceled.");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollRemotely is already canceled.");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enrollRemotely");
- mService.enrollRemotely(userId, mToken, hardwareAuthToken, mServiceReceiver,
- mContext.getOpPackageName(), disabledFeatures);
+ final long enrolId = mService.enrollRemotely(userId, mToken, hardwareAuthToken,
+ mServiceReceiver, mContext.getOpPackageName(), disabledFeatures);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrolId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enrollRemotely: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
@@ -713,10 +711,10 @@
}
}
- private void cancelEnrollment() {
+ private void cancelEnrollment(long requestId) {
if (mService != null) {
try {
- mService.cancelEnrollment(mToken);
+ mService.cancelEnrollment(mToken, requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1100,9 +1098,16 @@
}
private class OnEnrollCancelListener implements OnCancelListener {
+ private final long mAuthRequestId;
+
+ private OnEnrollCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelEnrollment();
+ Slog.d(TAG, "Cancel face enrollment requested for: " + mAuthRequestId);
+ cancelEnrollment(mAuthRequestId);
}
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index e919824..989b001 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -76,15 +76,16 @@
void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
// Start face enrollment
- void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
- String opPackageName, in int [] disabledFeatures, in Surface previewSurface, boolean debugConsent);
+ long enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+ String opPackageName, in int [] disabledFeatures,
+ in Surface previewSurface, boolean debugConsent);
// Start remote face enrollment
- void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+ long enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
String opPackageName, in int [] disabledFeatures);
// Cancel enrollment in progress
- void cancelEnrollment(IBinder token);
+ void cancelEnrollment(IBinder token, long requestId);
// Removes the specified face enrollment for the specified userId.
void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fe04e5d..acf9427 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -183,9 +183,16 @@
}
private class OnEnrollCancelListener implements OnCancelListener {
+ private final long mAuthRequestId;
+
+ private OnEnrollCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelEnrollment();
+ Slog.d(TAG, "Cancel fingerprint enrollment requested for: " + mAuthRequestId);
+ cancelEnrollment(mAuthRequestId);
}
}
@@ -646,20 +653,19 @@
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollment already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollment already canceled");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
- mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
- mContext.getOpPackageName(), enrollReason);
+ final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId,
+ mServiceReceiver, mContext.getOpPackageName(), enrollReason);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or try
@@ -1302,9 +1308,9 @@
return allSensors.isEmpty() ? null : allSensors.get(0);
}
- private void cancelEnrollment() {
+ private void cancelEnrollment(long requestId) {
if (mService != null) try {
- mService.cancelEnrollment(mToken);
+ mService.cancelEnrollment(mToken, requestId);
} 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 ba1dc6d..cbff8b1 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -84,11 +84,11 @@
void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
// Start fingerprint enrollment
- void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
+ long enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
String opPackageName, int enrollReason);
// Cancel enrollment in progress
- void cancelEnrollment(IBinder token);
+ void cancelEnrollment(IBinder token, long requestId);
// Any errors resulting from this call will be returned to the listener
void remove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6253fb9..ef349a9 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -215,14 +215,6 @@
public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L;
/**
- * Check whether apps are using FLAG_SLIPPERY for their windows. We expect that this flag is
- * only used by the system components. If so, we can lock it down.
- * @hide
- */
- @ChangeId
- public static final long BLOCK_FLAG_SLIPPERY = android.os.IInputConstants.BLOCK_FLAG_SLIPPERY;
-
- /**
* Input Event Injection Synchronization Mode: None.
* Never blocks. Injection is asynchronous and is assumed always to be successful.
* @hide
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
similarity index 94%
rename from core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
rename to core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index b3f7345..1ac3f0a 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -39,7 +39,7 @@
// TODO: Add documents
/** @hide */
-public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority {
+public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds";
@NonNull private final Set<String> mAllowedNetworkPlmnIds;
private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds";
@@ -51,7 +51,7 @@
private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic";
private final boolean mRequireOpportunistic;
- private VcnCellUnderlyingNetworkPriority(
+ private VcnCellUnderlyingNetworkTemplate(
int networkQuality,
boolean allowMetered,
Set<String> allowedNetworkPlmnIds,
@@ -92,7 +92,7 @@
/** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
- public static VcnCellUnderlyingNetworkPriority fromPersistableBundle(
+ public static VcnCellUnderlyingNetworkTemplate fromPersistableBundle(
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
@@ -117,7 +117,7 @@
final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY);
final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY);
- return new VcnCellUnderlyingNetworkPriority(
+ return new VcnCellUnderlyingNetworkTemplate(
networkQuality,
allowMetered,
allowedNetworkPlmnIds,
@@ -190,11 +190,11 @@
return false;
}
- if (!(other instanceof VcnCellUnderlyingNetworkPriority)) {
+ if (!(other instanceof VcnCellUnderlyingNetworkTemplate)) {
return false;
}
- final VcnCellUnderlyingNetworkPriority rhs = (VcnCellUnderlyingNetworkPriority) other;
+ final VcnCellUnderlyingNetworkTemplate rhs = (VcnCellUnderlyingNetworkTemplate) other;
return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
&& Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
&& mAllowRoaming == rhs.mAllowRoaming
@@ -211,7 +211,7 @@
}
/** This class is used to incrementally build WifiNetworkPriority objects. */
- public static final class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
+ public static final class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
@@ -280,10 +280,10 @@
return this;
}
- /** Build the VcnCellUnderlyingNetworkPriority. */
+ /** Build the VcnCellUnderlyingNetworkTemplate. */
@NonNull
- public VcnCellUnderlyingNetworkPriority build() {
- return new VcnCellUnderlyingNetworkPriority(
+ public VcnCellUnderlyingNetworkTemplate build() {
+ return new VcnCellUnderlyingNetworkTemplate(
mNetworkQuality,
mAllowMetered,
mAllowedNetworkPlmnIds,
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 55d3ecd..d07c24a 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,7 +16,7 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -162,12 +162,12 @@
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
- public static final LinkedHashSet<VcnUnderlyingNetworkPriority>
+ public static final LinkedHashSet<VcnUnderlyingNetworkTemplate>
DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>();
static {
DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
- new VcnCellUnderlyingNetworkPriority.Builder()
+ new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setAllowRoaming(true /* allowRoaming */)
@@ -175,13 +175,13 @@
.build());
DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.build());
DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
- new VcnCellUnderlyingNetworkPriority.Builder()
+ new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setAllowRoaming(true /* allowRoaming */)
@@ -202,7 +202,7 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
- @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities;
+ @NonNull private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities;
private static final String MAX_MTU_KEY = "mMaxMtu";
private final int mMaxMtu;
@@ -215,7 +215,7 @@
@NonNull String gatewayConnectionName,
@NonNull IkeTunnelConnectionParams tunnelConnectionParams,
@NonNull Set<Integer> exposedCapabilities,
- @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+ @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
mGatewayConnectionName = gatewayConnectionName;
@@ -265,7 +265,7 @@
new LinkedHashSet<>(
PersistableBundleUtils.toList(
networkPrioritiesBundle,
- VcnUnderlyingNetworkPriority::fromPersistableBundle));
+ VcnUnderlyingNetworkTemplate::fromPersistableBundle));
}
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
@@ -368,14 +368,14 @@
}
/**
- * Retrieve the configured VcnUnderlyingNetworkPriority list, or a default list if it is not
+ * Retrieve the configured VcnUnderlyingNetworkTemplate list, or a default list if it is not
* configured.
*
- * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkPriority>)
+ * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkTemplate>)
* @hide
*/
@NonNull
- public LinkedHashSet<VcnUnderlyingNetworkPriority> getVcnUnderlyingNetworkPriorities() {
+ public LinkedHashSet<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
return new LinkedHashSet<>(mUnderlyingNetworkPriorities);
}
@@ -418,7 +418,7 @@
final PersistableBundle networkPrioritiesBundle =
PersistableBundleUtils.fromList(
new ArrayList<>(mUnderlyingNetworkPriorities),
- VcnUnderlyingNetworkPriority::toPersistableBundle);
+ VcnUnderlyingNetworkTemplate::toPersistableBundle);
result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
@@ -465,7 +465,7 @@
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull
- private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities =
+ private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities =
new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -539,7 +539,7 @@
}
/**
- * Set the VcnUnderlyingNetworkPriority list.
+ * Set the VcnUnderlyingNetworkTemplate list.
*
* @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that
* are ordered from most to least preferred, or an empty list to use the default
@@ -550,7 +550,7 @@
/** @hide */
@NonNull
public Builder setVcnUnderlyingNetworkPriorities(
- @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities) {
+ @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities) {
Objects.requireNonNull(
mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
similarity index 93%
rename from core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java
rename to core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 551f757..d306d5c 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -33,7 +33,7 @@
// TODO: Add documents
/** @hide */
-public abstract class VcnUnderlyingNetworkPriority {
+public abstract class VcnUnderlyingNetworkTemplate {
/** @hide */
protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
/** @hide */
@@ -68,7 +68,7 @@
private final boolean mAllowMetered;
/** @hide */
- protected VcnUnderlyingNetworkPriority(
+ protected VcnUnderlyingNetworkTemplate(
int networkPriorityType, int networkQuality, boolean allowMetered) {
mNetworkPriorityType = networkPriorityType;
mNetworkQuality = networkQuality;
@@ -89,16 +89,16 @@
/** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
- public static VcnUnderlyingNetworkPriority fromPersistableBundle(
+ public static VcnUnderlyingNetworkTemplate fromPersistableBundle(
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
final int networkPriorityType = in.getInt(NETWORK_PRIORITY_TYPE_KEY);
switch (networkPriorityType) {
case NETWORK_PRIORITY_TYPE_WIFI:
- return VcnWifiUnderlyingNetworkPriority.fromPersistableBundle(in);
+ return VcnWifiUnderlyingNetworkTemplate.fromPersistableBundle(in);
case NETWORK_PRIORITY_TYPE_CELL:
- return VcnCellUnderlyingNetworkPriority.fromPersistableBundle(in);
+ return VcnCellUnderlyingNetworkTemplate.fromPersistableBundle(in);
default:
throw new IllegalArgumentException(
"Invalid networkPriorityType:" + networkPriorityType);
@@ -124,11 +124,11 @@
@Override
public boolean equals(@Nullable Object other) {
- if (!(other instanceof VcnUnderlyingNetworkPriority)) {
+ if (!(other instanceof VcnUnderlyingNetworkTemplate)) {
return false;
}
- final VcnUnderlyingNetworkPriority rhs = (VcnUnderlyingNetworkPriority) other;
+ final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
return mNetworkPriorityType == rhs.mNetworkPriorityType
&& mNetworkQuality == rhs.mNetworkQuality
&& mAllowMetered == rhs.mAllowMetered;
@@ -168,7 +168,7 @@
}
/**
- * This class is used to incrementally build VcnUnderlyingNetworkPriority objects.
+ * This class is used to incrementally build VcnUnderlyingNetworkTemplate objects.
*
* @param <T> The subclass to be built.
*/
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
similarity index 81%
rename from core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
rename to core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 85eb100..6bbb2bf 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -28,11 +28,11 @@
// TODO: Add documents
/** @hide */
-public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority {
+public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
private static final String SSID_KEY = "mSsid";
@Nullable private final String mSsid;
- private VcnWifiUnderlyingNetworkPriority(
+ private VcnWifiUnderlyingNetworkTemplate(
int networkQuality, boolean allowMetered, String ssid) {
super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered);
mSsid = ssid;
@@ -43,14 +43,14 @@
/** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
- public static VcnWifiUnderlyingNetworkPriority fromPersistableBundle(
+ public static VcnWifiUnderlyingNetworkTemplate fromPersistableBundle(
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
final String ssid = in.getString(SSID_KEY);
- return new VcnWifiUnderlyingNetworkPriority(networkQuality, allowMetered, ssid);
+ return new VcnWifiUnderlyingNetworkTemplate(networkQuality, allowMetered, ssid);
}
/** @hide */
@@ -74,11 +74,11 @@
return false;
}
- if (!(other instanceof VcnWifiUnderlyingNetworkPriority)) {
+ if (!(other instanceof VcnWifiUnderlyingNetworkTemplate)) {
return false;
}
- final VcnWifiUnderlyingNetworkPriority rhs = (VcnWifiUnderlyingNetworkPriority) other;
+ final VcnWifiUnderlyingNetworkTemplate rhs = (VcnWifiUnderlyingNetworkTemplate) other;
return mSsid.equals(rhs.mSsid);
}
@@ -94,8 +94,8 @@
return mSsid;
}
- /** This class is used to incrementally build VcnWifiUnderlyingNetworkPriority objects. */
- public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
+ /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
+ public static class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
@Nullable private String mSsid;
/** Construct a Builder object. */
@@ -112,10 +112,10 @@
return this;
}
- /** Build the VcnWifiUnderlyingNetworkPriority. */
+ /** Build the VcnWifiUnderlyingNetworkTemplate. */
@NonNull
- public VcnWifiUnderlyingNetworkPriority build() {
- return new VcnWifiUnderlyingNetworkPriority(mNetworkQuality, mAllowMetered, mSsid);
+ public VcnWifiUnderlyingNetworkTemplate build() {
+ return new VcnWifiUnderlyingNetworkTemplate(mNetworkQuality, mAllowMetered, mSsid);
}
/** @hide */
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 74fffd0..92d652d 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1011,7 +1011,7 @@
new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
CACHE_KEY_IS_POWER_SAVE_MODE_PROPERTY) {
@Override
- protected Boolean recompute(Void query) {
+ public Boolean recompute(Void query) {
try {
return mService.isPowerSaveMode();
} catch (RemoteException e) {
@@ -1024,7 +1024,7 @@
new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
CACHE_KEY_IS_INTERACTIVE_PROPERTY) {
@Override
- protected Boolean recompute(Void query) {
+ public Boolean recompute(Void query) {
try {
return mService.isInteractive();
} catch (RemoteException e) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b3639e4..b4f3fa0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2754,7 +2754,7 @@
new PropertyInvalidatedCache<Integer, Boolean>(
32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
@Override
- protected Boolean recompute(Integer query) {
+ public Boolean recompute(Integer query) {
try {
return mService.isUserUnlocked(query);
} catch (RemoteException re) {
@@ -2762,7 +2762,7 @@
}
}
@Override
- protected boolean bypass(Integer query) {
+ public boolean bypass(Integer query) {
return query < 0;
}
};
@@ -2772,7 +2772,7 @@
new PropertyInvalidatedCache<Integer, Boolean>(
32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
@Override
- protected Boolean recompute(Integer query) {
+ public Boolean recompute(Integer query) {
try {
return mService.isUserUnlockingOrUnlocked(query);
} catch (RemoteException re) {
@@ -2780,7 +2780,7 @@
}
}
@Override
- protected boolean bypass(Integer query) {
+ public boolean bypass(Integer query) {
return query < 0;
}
};
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 61e48c5..3ea50e9 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1447,7 +1447,7 @@
new PropertyInvalidatedCache<PermissionQuery, Integer>(
2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
@Override
- protected Integer recompute(PermissionQuery query) {
+ public Integer recompute(PermissionQuery query) {
return checkPermissionUncached(query.permission, query.pid, query.uid);
}
};
@@ -1530,12 +1530,12 @@
new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") {
@Override
- protected Integer recompute(PackageNamePermissionQuery query) {
+ public Integer recompute(PackageNamePermissionQuery query) {
return checkPackageNamePermissionUncached(
query.permName, query.pkgName, query.userId);
}
@Override
- protected boolean bypass(PackageNamePermissionQuery query) {
+ public boolean bypass(PackageNamePermissionQuery query) {
return query.userId < 0;
}
};
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 55ffdab..4447679 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10248,6 +10248,13 @@
public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 0x3;
/**
+ * Whether the following typing focus feature for magnification is enabled.
+ * @hide
+ */
+ public static final String ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED =
+ "accessibility_magnification_follow_typing_enabled";
+
+ /**
* Controls magnification capability. Accessibility magnification is capable of at least one
* of the magnification modes.
*
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index 6411314..50efbac 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -80,6 +80,7 @@
colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
}
wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
+ contextImage.close();
}
}
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index af76cb9..1ed12f7 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -66,7 +66,12 @@
* @hide
*/
public void setBatchingEnabled(boolean batchingEnabled) {
+ if (mBatchingEnabled == batchingEnabled) {
+ return;
+ }
+
mBatchingEnabled = batchingEnabled;
+ mHandler.removeCallbacks(mConsumeBatchedInputEvents);
if (!batchingEnabled) {
unscheduleBatchedInput();
mHandler.post(mConsumeBatchedInputEvents);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ab33fea..b7f9be7 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -231,6 +231,7 @@
float shadowRadius);
private static native void nativeSetGlobalShadowSettings(@Size(4) float[] ambientColor,
@Size(4) float[] spotColor, float lightPosY, float lightPosZ, float lightRadius);
+ private static native boolean nativeGetDisplayDecorationSupport(IBinder displayToken);
private static native void nativeSetFrameRate(long transactionObj, long nativeObject,
float frameRate, int compatibility, int changeFrameRateStrategy);
@@ -2651,6 +2652,20 @@
}
/**
+ * Returns whether a display supports DISPLAY_DECORATION.
+ *
+ * @param displayToken
+ * The token for the display.
+ *
+ * @return Whether the display supports DISPLAY_DECORATION.
+ *
+ * @hide
+ */
+ public static boolean getDisplayDecorationSupport(IBinder displayToken) {
+ return nativeGetDisplayDecorationSupport(displayToken);
+ }
+
+ /**
* Adds a callback to be informed about SF's jank classification for a specific surface.
* @hide
*/
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
index 1cb6825..722546e 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -67,4 +67,13 @@
*/
void onAccessibilityActionPerformed(int displayId);
+ /**
+ * Called when the user is performing dragging gesture. It is started after the offset
+ * between the down location and the move event location exceed
+ * {@link ViewConfiguration#getScaledTouchSlop()}.
+ *
+ * @param displayId The logical display id.
+ */
+ void onDrag(int displayId);
+
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 0d4ad38..a33b2f1 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -41,6 +41,8 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
@@ -181,6 +183,8 @@
public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39;
public static final int CUJ_SCREEN_OFF = 40;
public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41;
+ public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42;
+ public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43;
private static final int NO_STATSD_LOGGING = -1;
@@ -231,6 +235,8 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION,
};
private static volatile InteractionJankMonitor sInstance;
@@ -293,6 +299,8 @@
CUJ_SPLASHSCREEN_EXIT_ANIM,
CUJ_SCREEN_OFF,
CUJ_SCREEN_OFF_SHOW_AOD,
+ CUJ_ONE_HANDED_ENTER_TRANSITION,
+ CUJ_ONE_HANDED_EXIT_TRANSITION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -712,6 +720,10 @@
return "SCREEN_OFF";
case CUJ_SCREEN_OFF_SHOW_AOD:
return "SCREEN_OFF_SHOW_AOD";
+ case CUJ_ONE_HANDED_ENTER_TRANSITION:
+ return "ONE_HANDED_ENTER_TRANSITION";
+ case CUJ_ONE_HANDED_EXIT_TRANSITION:
+ return "ONE_HANDED_EXIT_TRANSITION";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f3cdf82..a5cf7ce 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -207,8 +207,10 @@
*
* @param displayId the ID of the display to notify.
* @param types the internal insets types of the bars are about to show transiently.
+ * @param isGestureOnSystemBar whether the gesture to show the transient bar was a gesture on
+ * one of the bars itself.
*/
- void showTransient(int displayId, in int[] types);
+ void showTransient(int displayId, in int[] types, boolean isGestureOnSystemBar);
/**
* Notifies System UI to abort the transient state of system bars, which prevents the bars being
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 04e4819..d3c3917 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -178,7 +178,7 @@
public ScreenshotHelper(Context context) {
mContext = context;
IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
}
/**
diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS
index 554e278..1c2d19d 100644
--- a/core/java/com/android/server/OWNERS
+++ b/core/java/com/android/server/OWNERS
@@ -1 +1 @@
-per-file SystemConfig.java = toddke@google.com,patb@google.com
+per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 67d0c52..dd5af04 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1768,6 +1768,15 @@
client->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius);
}
+static jboolean nativeGetDisplayDecorationSupport(JNIEnv* env, jclass clazz,
+ jobject displayTokenObject) {
+ sp<IBinder> displayToken(ibinderForJavaObject(env, displayTokenObject));
+ if (displayToken == nullptr) {
+ return JNI_FALSE;
+ }
+ return static_cast<jboolean>(SurfaceComposerClient::getDisplayDecorationSupport(displayToken));
+}
+
static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
SurfaceControl *surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
@@ -2092,6 +2101,8 @@
(void*)nativeMirrorSurface },
{"nativeSetGlobalShadowSettings", "([F[FFFF)V",
(void*)nativeSetGlobalShadowSettings },
+ {"nativeGetDisplayDecorationSupport", "(Landroid/os/IBinder;)Z",
+ (void*)nativeGetDisplayDecorationSupport},
{"nativeGetHandle", "(J)J",
(void*)nativeGetHandle },
{"nativeSetFixedTransformHint", "(JJI)V",
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 00b7fd7..4c0ba8c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -86,6 +86,8 @@
optional SettingProto accessibility_floating_menu_opacity = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_floating_menu_fade_enabled = 41 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto odi_captions_volume_ui_enabled = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // Setting for accessibility magnification for following typing.
+ optional SettingProto accessibility_magnification_follow_typing_enabled = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 7a2c63f..fd3079f 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -77,11 +77,11 @@
PropertyInvalidatedCache<Integer, Boolean> testCache =
new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
@Override
- protected boolean bypass(Integer x) {
+ public boolean bypass(Integer x) {
return x % 13 == 0;
}
};
@@ -131,21 +131,21 @@
PropertyInvalidatedCache<Integer, Boolean> cache1 =
new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
};
PropertyInvalidatedCache<Integer, Boolean> cache2 =
new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
};
PropertyInvalidatedCache<Integer, Boolean> cache3 =
new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
};
@@ -171,7 +171,7 @@
// Create a new cache1. Verify that the new instance is disabled.
cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
};
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 62d0b2e..02e5942 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -120,10 +120,18 @@
return null;
}
+ public Region getCurrentMagnificationRegion(int displayId) {
+ return null;
+ }
+
public boolean resetMagnification(int displayId, boolean animate) {
return false;
}
+ public boolean resetCurrentMagnification(int displayId, boolean animate) {
+ return false;
+ }
+
public boolean setMagnificationConfig(int displayId,
@NonNull MagnificationConfig config, boolean animate) {
return false;
diff --git a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
index c4080e8..182bf6d 100644
--- a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
+++ b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
@@ -35,7 +35,7 @@
}
@Override
- protected String recompute(Integer qv) {
+ public String recompute(Integer qv) {
mRecomputeCount += 1;
return "foo" + qv.toString();
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index a6aa4f2..54bab4a 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -276,7 +276,7 @@
* "HMACSHA256", sharedSecret, salt, info.toByteArray(), 32));
* byte[] associatedData = {};
* return key.decrypt(ciphertext, associatedData);
- * }
+ * }</pre>
*/
public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
private static final X500Principal DEFAULT_ATTESTATION_CERT_SUBJECT =
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
index 4ac972c..44b2f45 100644
--- a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -21,7 +21,7 @@
android:orientation="vertical"
android:clipToPadding="false"
android:paddingEnd="@dimen/compat_hint_padding_end"
- android:paddingBottom="5dp"
+ android:paddingBottom="8dp"
android:clickable="true">
<TextView
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index c99f3fe..dfaeeeb 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -33,8 +33,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipToPadding="false"
- android:layout_marginEnd="16dp"
- android:layout_marginBottom="16dp"
+ android:layout_marginEnd="@dimen/compat_button_margin"
+ android:layout_marginBottom="@dimen/compat_button_margin"
android:orientation="vertical">
<ImageButton
@@ -62,8 +62,10 @@
<ImageButton
android:id="@+id/size_compat_restart_button"
android:visibility="gone"
- android:layout_width="@dimen/size_compat_button_width"
- android:layout_height="@dimen/size_compat_button_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/compat_button_margin"
+ android:layout_marginBottom="@dimen/compat_button_margin"
android:src="@drawable/size_compat_restart_button_ripple"
android:background="@android:color/transparent"
android:contentDescription="@string/restart_button_description"/>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index d338e3b..1c19a10 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -200,11 +200,8 @@
<!-- Size of user education views on large screens (phone is just match parent). -->
<dimen name="bubbles_user_education_width_large_screen">400dp</dimen>
- <!-- The width of the size compat restart button including padding. -->
- <dimen name="size_compat_button_width">80dp</dimen>
-
- <!-- The height of the size compat restart button including padding. -->
- <dimen name="size_compat_button_height">64dp</dimen>
+ <!-- Bottom and end margin for compat buttons. -->
+ <dimen name="compat_button_margin">16dp</dimen>
<!-- The radius of the corners of the compat hint bubble. -->
<dimen name="compat_hint_corner_radius">28dp</dimen>
@@ -212,8 +209,8 @@
<!-- The width of the compat hint point. -->
<dimen name="compat_hint_point_width">10dp</dimen>
- <!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2
- - compat_hint_corner_radius - compat_hint_point_width /2). -->
+ <!-- The end padding for the compat hint. Computed as (compat button width (=48) / 2
+ + compat_button_margin - compat_hint_corner_radius - compat_hint_point_width / 2). -->
<dimen name="compat_hint_padding_end">7dp</dimen>
<!-- The width of the size compat hint. -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index ce1f870..7903a51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -284,17 +284,21 @@
mSyncQueue = syncQueue;
}
- private static void registerOneHandedState(OneHandedController oneHanded) {
+ private void registerOneHandedState(OneHandedController oneHanded) {
oneHanded.registerTransitionCallback(
new OneHandedTransitionCallback() {
@Override
public void onStartFinished(Rect bounds) {
- // TODO(b/198403767) mStackView.offSetY(int bounds.top)
+ if (mStackView != null) {
+ mStackView.onVerticalOffsetChanged(bounds.top);
+ }
}
@Override
public void onStopFinished(Rect bounds) {
- // TODO(b/198403767) mStackView.offSetY(int bounds.top)
+ if (mStackView != null) {
+ mStackView.onVerticalOffsetChanged(bounds.top);
+ }
}
});
}
@@ -423,7 +427,7 @@
}
});
- mOneHandedOptional.ifPresent(BubbleController::registerOneHandedState);
+ mOneHandedOptional.ifPresent(this::registerOneHandedState);
}
@VisibleForTesting
@@ -1315,6 +1319,7 @@
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides.
* Updates stack description for TalkBack focus.
+ * Updates bubbles' icon views clickable states
*/
public void updateStack() {
if (mStackView == null) {
@@ -1332,6 +1337,8 @@
}
mStackView.updateContentDescription();
+
+ mStackView.updateBubblesClickableStates();
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index df3780e..7bf4439 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1485,6 +1485,25 @@
}
}
+ /**
+ * Update bubbles' icon views clickable states.
+ */
+ public void updateBubblesClickableStates() {
+ for (int i = 0; i < mBubbleData.getBubbles().size(); i++) {
+ final Bubble bubble = mBubbleData.getBubbles().get(i);
+ if (bubble.getIconView() != null) {
+ if (mIsExpanded) {
+ // when stack is expanded all bubbles are clickable
+ bubble.getIconView().setClickable(true);
+ } else {
+ // when stack is collapsed, only the top bubble needs to be clickable,
+ // so that a11y ignores all the inaccessible bubbles in the stack
+ bubble.getIconView().setClickable(i == 0);
+ }
+ }
+ }
+ }
+
private void updateSystemGestureExcludeRects() {
// Exclude the region occupied by the first BubbleView in the stack
Rect excludeZone = mSystemGestureExclusionRects.get(0);
@@ -3015,6 +3034,16 @@
}
/**
+ * Handles vertical offset changes, e.g. when one handed mode is switched on/off.
+ *
+ * @param offset new vertical offset.
+ */
+ void onVerticalOffsetChanged(int offset) {
+ // adjust dismiss view vertical position, so that it is still visible to the user
+ mDismissView.setPadding(/* left = */ 0, /* top = */ 0, /* right = */ 0, offset);
+ }
+
+ /**
* Holds some commonly queried information about the stack.
*/
public static class StackViewState {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 4f01dc6..cc37cae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import android.view.WindowManager;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
@@ -143,10 +144,10 @@
static OneHandedController provideOneHandedController(Context context,
WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger, @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
+ UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor,
+ @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
return OneHandedController.create(context, windowManager, displayController, displayLayout,
- taskStackListener, uiEventLogger, mainExecutor, mainHandler);
+ taskStackListener, jankMonitor, uiEventLogger, mainExecutor, mainHandler);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index c9c73fd..96f82fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -45,6 +45,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
@@ -194,7 +195,8 @@
public static OneHandedController create(
Context context, WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) {
+ InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
+ ShellExecutor mainExecutor, Handler mainHandler) {
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
@@ -210,13 +212,13 @@
mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
context, displayLayout, settingsUtil, animationController, tutorialHandler,
- oneHandedBackgroundPanelOrganizer, mainExecutor);
+ oneHandedBackgroundPanelOrganizer, jankMonitor, mainExecutor);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
- settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
+ settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState, jankMonitor,
oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
mainHandler);
}
@@ -232,6 +234,7 @@
OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
OneHandedTimeoutHandler timeoutHandler,
OneHandedState state,
+ InteractionJankMonitor jankMonitor,
OneHandedUiEventLogger uiEventsLogger,
IOverlayManager overlayManager,
TaskStackListenerImpl taskStackListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index ec3ef5a..87eb40c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -16,12 +16,15 @@
package com.android.wm.shell.onehanded;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_ENTER_TRANSITION;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_EXIT_TRANSITION;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
import android.content.Context;
import android.graphics.Rect;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.window.DisplayAreaAppearedInfo;
@@ -34,6 +37,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -41,6 +45,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Manages OneHanded display areas such as offset.
@@ -62,6 +67,8 @@
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
+ private final InteractionJankMonitor mJankMonitor;
+ private final Context mContext;
private boolean mIsReady;
private float mLastVisualOffset = 0;
@@ -95,7 +102,11 @@
public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
+ final boolean isEntering = animator.getTransitionDirection()
+ == TRANSITION_DIRECTION_TRIGGER;
if (mAnimationController.isAnimatorsConsumed()) {
+ endCUJTracing(isEntering ? CUJ_ONE_HANDED_ENTER_TRANSITION
+ : CUJ_ONE_HANDED_EXIT_TRANSITION);
finishOffset((int) animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -105,7 +116,11 @@
public void onOneHandedAnimationCancel(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
+ final boolean isEntering = animator.getTransitionDirection()
+ == TRANSITION_DIRECTION_TRIGGER;
if (mAnimationController.isAnimatorsConsumed()) {
+ cancelCUJTracing(isEntering ? CUJ_ONE_HANDED_ENTER_TRANSITION
+ : CUJ_ONE_HANDED_EXIT_TRANSITION);
finishOffset((int) animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -121,11 +136,14 @@
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler,
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
+ InteractionJankMonitor jankMonitor,
ShellExecutor mainExecutor) {
super(mainExecutor);
+ mContext = context;
setDisplayLayout(displayLayout);
mOneHandedSettingsUtil = oneHandedSettingsUtil;
mAnimationController = animationController;
+ mJankMonitor = jankMonitor;
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
mEnterExitAnimationDurationMs =
@@ -197,6 +215,11 @@
final int direction = yOffset > 0
? TRANSITION_DIRECTION_TRIGGER
: TRANSITION_DIRECTION_EXIT;
+ if (direction == TRANSITION_DIRECTION_TRIGGER) {
+ beginCUJTracing(CUJ_ONE_HANDED_ENTER_TRANSITION, "enterOneHanded");
+ } else {
+ beginCUJTracing(CUJ_ONE_HANDED_EXIT_TRANSITION, "stopOneHanded");
+ }
mDisplayAreaTokenMap.forEach(
(token, leash) -> {
animateWindows(token, leash, fromPos, yOffset, direction,
@@ -302,6 +325,26 @@
mTransitionCallbacks.add(callback);
}
+ void beginCUJTracing(@InteractionJankMonitor.CujType int cujType, @Nullable String tag) {
+ final Map.Entry<WindowContainerToken, SurfaceControl> firstEntry =
+ getDisplayAreaTokenMap().entrySet().iterator().next();
+ final InteractionJankMonitor.Configuration.Builder builder =
+ InteractionJankMonitor.Configuration.Builder.withSurface(
+ cujType, mContext, firstEntry.getValue());
+ if (!TextUtils.isEmpty(tag)) {
+ builder.setTag(tag);
+ }
+ mJankMonitor.begin(builder);
+ }
+
+ void endCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+ mJankMonitor.end(cujType);
+ }
+
+ void cancelCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+ mJankMonitor.cancel(cujType);
+ }
+
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index c6794b8..1716380 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -28,7 +28,6 @@
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
-import static android.window.TransitionInfo.isIndependent;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
@@ -48,7 +47,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
-import android.util.ArrayMap;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -62,8 +60,8 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.CounterRotatorHelper;
import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.util.CounterRotator;
import java.util.Optional;
@@ -175,14 +173,25 @@
return true;
}
+ // The previous PIP Task is no longer in PIP, but this is not an exit transition (This can
+ // happen when a new activity requests enter PIP). In this case, we just show this Task in
+ // its end state, and play other animation as normal.
+ final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
+ if (currentPipChange != null
+ && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) {
+ resetPrevPip(currentPipChange, startTransaction);
+ }
+
// Entering PIP.
if (isEnteringPip(info, mCurrentPipTaskToken)) {
return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
}
- // For transition that we don't animate, we may need to update the PIP surface, otherwise it
- // will be reset after the transition.
- updatePipForUnhandledTransition(info, startTransaction, finishTransaction);
+ // For transition that we don't animate, but contains the PIP leash, we need to update the
+ // PIP surface, otherwise it will be reset after the transition.
+ if (currentPipChange != null) {
+ updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction);
+ }
return false;
}
@@ -322,35 +331,11 @@
final int displayH = displayRotationChange.getEndAbsBounds().height();
// Counter-rotate all "going-away" things since they are still in the old orientation.
- final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (!Transitions.isClosingType(change.getMode())
- || !isIndependent(change, info)
- || change.getParent() == null) {
- continue;
- }
- CounterRotator crot = counterRotators.get(change.getParent());
- if (crot == null) {
- crot = new CounterRotator();
- crot.setup(startTransaction,
- info.getChange(change.getParent()).getLeash(),
- rotateDelta, displayW, displayH);
- if (crot.getSurface() != null) {
- // Wallpaper should be placed at the bottom.
- final int layer = (change.getFlags() & FLAG_IS_WALLPAPER) == 0
- ? info.getChanges().size() - i
- : -1;
- startTransaction.setLayer(crot.getSurface(), layer);
- }
- counterRotators.put(change.getParent(), crot);
- }
- crot.addChild(startTransaction, change.getLeash());
- }
+ final CounterRotatorHelper rotator = new CounterRotatorHelper();
+ rotator.handleClosingChanges(info, startTransaction, rotateDelta, displayW, displayH);
+
mFinishCallback = (wct, wctCB) -> {
- for (int i = 0; i < counterRotators.size(); ++i) {
- counterRotators.valueAt(i).cleanUp(info.getRootLeash());
- }
+ rotator.cleanUp();
mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
finishCallback.onTransitionFinished(wct, wctCB);
};
@@ -597,30 +582,36 @@
finishCallback.onTransitionFinished(null, null);
}
- private void updatePipForUnhandledTransition(@NonNull TransitionInfo info,
+ private void resetPrevPip(@NonNull TransitionInfo.Change prevPipChange,
+ @NonNull SurfaceControl.Transaction startTransaction) {
+ final SurfaceControl leash = prevPipChange.getLeash();
+ final Rect bounds = prevPipChange.getEndAbsBounds();
+ final Point offset = prevPipChange.getEndRelOffset();
+ bounds.offset(-offset.x, -offset.y);
+
+ startTransaction.setWindowCrop(leash, null);
+ startTransaction.setMatrix(leash, 1, 0, 0, 1);
+ startTransaction.setCornerRadius(leash, 0);
+ startTransaction.setPosition(leash, bounds.left, bounds.top);
+
+ mCurrentPipTaskToken = null;
+ mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo());
+ }
+
+ private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction) {
- if (mCurrentPipTaskToken == null) {
- return;
- }
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (!mCurrentPipTaskToken.equals(change.getContainer())) {
- continue;
- }
- // When the PIP window is visible and being a part of the transition, such as display
- // rotation, we need to update its bounds and rounded corner.
- final SurfaceControl leash = change.getLeash();
- final Rect destBounds = mPipBoundsState.getBounds();
- final boolean isInPip = mPipTransitionState.isInPip();
- mSurfaceTransactionHelper
- .crop(startTransaction, leash, destBounds)
- .round(startTransaction, leash, isInPip);
- mSurfaceTransactionHelper
- .crop(finishTransaction, leash, destBounds)
- .round(finishTransaction, leash, isInPip);
- break;
- }
+ // When the PIP window is visible and being a part of the transition, such as display
+ // rotation, we need to update its bounds and rounded corner.
+ final SurfaceControl leash = pipChange.getLeash();
+ final Rect destBounds = mPipBoundsState.getBounds();
+ final boolean isInPip = mPipTransitionState.isInPip();
+ mSurfaceTransactionHelper
+ .crop(startTransaction, leash, destBounds)
+ .round(startTransaction, leash, isInPip);
+ mSurfaceTransactionHelper
+ .crop(finishTransaction, leash, destBounds)
+ .round(finishTransaction, leash, isInPip);
}
private void finishResizeForMenu(Rect destinationBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
new file mode 100644
index 0000000..08c99b2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.transition;
+
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+
+import android.util.ArrayMap;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.util.CounterRotator;
+
+import java.util.List;
+
+/**
+ * The helper class that performs counter-rotate for all "going-away" window containers if they are
+ * still in the old rotation in a transition.
+ */
+public class CounterRotatorHelper {
+ private final ArrayMap<WindowContainerToken, CounterRotator> mRotatorMap = new ArrayMap<>();
+ private SurfaceControl mRootLeash;
+
+ /** Puts the surface controls of closing changes to counter-rotated surfaces. */
+ public void handleClosingChanges(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ int rotateDelta, int displayW, int displayH) {
+ mRootLeash = info.getRootLeash();
+ final List<TransitionInfo.Change> changes = info.getChanges();
+ final int numChanges = changes.size();
+ for (int i = numChanges - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = changes.get(i);
+ final WindowContainerToken parent = change.getParent();
+ if (!Transitions.isClosingType(change.getMode())
+ || !TransitionInfo.isIndependent(change, info) || parent == null) {
+ continue;
+ }
+
+ CounterRotator crot = mRotatorMap.get(parent);
+ if (crot == null) {
+ crot = new CounterRotator();
+ crot.setup(startTransaction, info.getChange(parent).getLeash(), rotateDelta,
+ displayW, displayH);
+ final SurfaceControl rotatorSc = crot.getSurface();
+ if (rotatorSc != null) {
+ // Wallpaper should be placed at the bottom.
+ final int layer = (change.getFlags() & FLAG_IS_WALLPAPER) == 0
+ ? numChanges - i
+ : -1;
+ startTransaction.setLayer(rotatorSc, layer);
+ }
+ mRotatorMap.put(parent, crot);
+ }
+ crot.addChild(startTransaction, change.getLeash());
+ }
+ }
+
+ /** Restores to the original state, i.e. reparent back to transition root. */
+ public void cleanUp() {
+ for (int i = mRotatorMap.size() - 1; i >= 0; --i) {
+ mRotatorMap.valueAt(i).cleanUp(mRootLeash);
+ }
+ mRotatorMap.clear();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 072b925..5833ca8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -43,7 +43,6 @@
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
-import static android.window.TransitionInfo.isIndependent;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
@@ -78,7 +77,6 @@
import android.window.TransitionInfo;
import android.window.TransitionMetrics;
import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.R;
@@ -92,7 +90,6 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.util.CounterRotator;
import java.util.ArrayList;
@@ -280,16 +277,12 @@
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
- final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
+ final CounterRotatorHelper rotator = new CounterRotatorHelper();
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
- for (int i = 0; i < counterRotators.size(); ++i) {
- counterRotators.valueAt(i).cleanUp(info.getRootLeash());
- }
- counterRotators.clear();
-
+ rotator.cleanUp();
if (mRotationAnimation != null) {
mRotationAnimation.kill();
mRotationAnimation = null;
@@ -322,29 +315,9 @@
continue;
}
} else {
- // opening/closing an app into a new orientation. Counter-rotate all
- // "going-away" things since they are still in the old orientation.
- for (int j = info.getChanges().size() - 1; j >= 0; --j) {
- final TransitionInfo.Change innerChange = info.getChanges().get(j);
- if (!Transitions.isClosingType(innerChange.getMode())
- || !isIndependent(innerChange, info)
- || innerChange.getParent() == null) {
- continue;
- }
- CounterRotator crot = counterRotators.get(innerChange.getParent());
- if (crot == null) {
- crot = new CounterRotator();
- crot.setup(startTransaction,
- info.getChange(innerChange.getParent()).getLeash(),
- rotateDelta, displayW, displayH);
- if (crot.getSurface() != null) {
- int layer = info.getChanges().size() - j;
- startTransaction.setLayer(crot.getSurface(), layer);
- }
- counterRotators.put(innerChange.getParent(), crot);
- }
- crot.addChild(startTransaction, innerChange.getLeash());
- }
+ // Opening/closing an app into a new orientation.
+ rotator.handleClosingChanges(info, startTransaction, rotateDelta,
+ displayW, displayH);
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index dbd3d8c..8dd9104 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -26,11 +26,9 @@
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,7 +37,7 @@
/**
* Test Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -84,8 +82,6 @@
@Presubmit
@Test
fun displayEndsAt90Degrees() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmEnd {
hasRotation(Surface.ROTATION_90)
}
@@ -93,41 +89,23 @@
@Presubmit
@Test
- override fun navBarLayerIsVisible() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
- super.navBarLayerIsVisible()
- }
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Presubmit
@Test
- override fun statusBarLayerIsVisible() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerIsVisible()
- }
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
- super.navBarLayerRotatesAndScales()
- }
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@Presubmit
@Test
fun pipWindowInsideDisplay() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmStart {
frameRegion(pipApp.component).coversAtMost(startingBounds)
}
@@ -136,8 +114,6 @@
@Presubmit
@Test
fun pipAppShowsOnTop() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmEnd {
isAppWindowOnTop(pipApp.component)
}
@@ -146,8 +122,6 @@
@Presubmit
@Test
fun pipLayerInsideDisplay() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersStart {
visibleRegion(pipApp.component).coversAtMost(startingBounds)
}
@@ -156,8 +130,6 @@
@Presubmit
@Test
fun pipAlwaysVisible() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.isAppWindowVisible(pipApp.component)
}
@@ -166,8 +138,6 @@
@Presubmit
@Test
fun pipAppLayerCoversFullScreen() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersEnd {
visibleRegion(pipApp.component).coversExactly(endingBounds)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index 2cdbffa..f40aa66 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -25,6 +25,7 @@
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:launchMode="singleTop"
+ android:theme="@style/CutoutShortEdges"
android:label="FixedApp"
android:exported="true">
<intent-filter>
@@ -37,6 +38,7 @@
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:taskAffinity="com.android.wm.shell.flicker.testapp.PipActivity"
+ android:theme="@style/CutoutShortEdges"
android:launchMode="singleTop"
android:label="PipApp"
android:exported="true">
@@ -52,6 +54,7 @@
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="ImeApp"
android:launchMode="singleTop"
android:exported="true">
@@ -68,6 +71,7 @@
<activity android:name=".SplitScreenActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SplitScreenPrimaryApp"
android:exported="true">
<intent-filter>
@@ -79,6 +83,7 @@
<activity android:name=".SplitScreenSecondaryActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenSecondaryActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SplitScreenSecondaryApp"
android:exported="true">
<intent-filter>
@@ -90,6 +95,7 @@
<activity android:name=".NonResizeableActivity"
android:resizeableActivity="false"
android:taskAffinity="com.android.wm.shell.flicker.testapp.NonResizeableActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="NonResizeableApp"
android:exported="true">
<intent-filter>
@@ -100,6 +106,7 @@
<activity android:name=".SimpleActivity"
android:taskAffinity="com.android.wm.shell.flicker.testapp.SimpleActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SimpleApp"
android:exported="true">
<intent-filter>
@@ -111,6 +118,7 @@
android:name=".LaunchBubbleActivity"
android:label="LaunchBubbleApp"
android:exported="true"
+ android:theme="@style/CutoutShortEdges"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -121,6 +129,7 @@
android:name=".BubbleActivity"
android:label="BubbleApp"
android:exported="false"
+ android:theme="@style/CutoutShortEdges"
android:resizeableActivity="true" />
</application>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
new file mode 100644
index 0000000..87a61a8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <style name="CutoutDefault">
+ <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ </style>
+
+ <style name="CutoutShortEdges">
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+
+ <style name="CutoutNever">
+ <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 16bc5075..636e875 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -46,6 +46,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -86,6 +87,8 @@
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
+ InteractionJankMonitor mMockJankMonitor;
+ @Mock
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@@ -139,6 +142,7 @@
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
mSpiedTransitionState,
+ mMockJankMonitor,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 1d92a48..df21163 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -50,6 +50,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -99,6 +100,8 @@
ShellExecutor mMockShellMainExecutor;
@Mock
OneHandedSettingsUtil mMockSettingsUitl;
+ @Mock
+ InteractionJankMonitor mJankMonitor;
List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>();
@@ -141,6 +144,7 @@
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
+ mJankMonitor,
mMockShellMainExecutor));
for (int i = 0; i < DISPLAYAREA_INFO_COUNT; i++) {
@@ -428,6 +432,7 @@
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
+ mJankMonitor,
mMockShellMainExecutor));
assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index bea69c5..58399b6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -40,6 +40,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -78,6 +79,8 @@
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
+ InteractionJankMonitor mMockJankMonitor;
+ @Mock
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@@ -128,6 +131,7 @@
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
mSpiedState,
+ mMockJankMonitor,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 61caa0b..9109a18 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -3640,7 +3640,7 @@
}
@Override
- protected Boolean recompute(Integer userId) {
+ public Boolean recompute(Integer userId) {
Preconditions.checkArgument(userId >= 0);
if (mManager == null) {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index f9fc17f..45c2a5a 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -283,9 +283,21 @@
synchronized (mCallbackLock) {
if (mCallback != null) {
mCallback.onFilterEvent(this, events);
+ } else {
+ for (FilterEvent event : events) {
+ if (event instanceof MediaEvent) {
+ ((MediaEvent)event).release();
+ }
+ }
}
}
});
+ } else {
+ for (FilterEvent event : events) {
+ if (event instanceof MediaEvent) {
+ ((MediaEvent)event).release();
+ }
+ }
}
}
}
@@ -558,6 +570,8 @@
if (res != Tuner.RESULT_SUCCESS) {
TunerUtils.throwExceptionForResult(res, "Failed to close filter.");
} else {
+ mCallback = null;
+ mExecutor = null;
mIsStarted = false;
mIsClosed = true;
}
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 38bac1c..d3d8bba 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -25,15 +25,21 @@
name: "framework-connectivity-netstats-internal-sources",
srcs: [
"src/android/app/usage/*.java",
- "src/android/net/DataUsage*.*",
- "src/android/net/INetworkStats*.*",
- "src/android/net/NetworkIdentity*.java",
+ "src/android/net/DataUsageRequest.*",
+ "src/android/net/INetworkStatsService.aidl",
+ "src/android/net/INetworkStatsSession.aidl",
+ "src/android/net/NetworkIdentity.java",
+ "src/android/net/NetworkIdentitySet.java",
"src/android/net/NetworkStateSnapshot.*",
- "src/android/net/NetworkStats*.*",
+ "src/android/net/NetworkStats.*",
+ "src/android/net/NetworkStatsAccess.*",
+ "src/android/net/NetworkStatsCollection.*",
+ "src/android/net/NetworkStatsHistory.*",
"src/android/net/NetworkTemplate.*",
"src/android/net/TrafficStats.java",
"src/android/net/UnderlyingNetworkInfo.*",
"src/android/net/netstats/**/*.*",
+ "src/com/android/server/NetworkManagementSocketTagger.java",
],
path: "src",
visibility: [
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
index a84e7a9..10a22ac 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
@@ -343,7 +343,7 @@
// Load and validate the optional algorithm resource. Undefined or duplicate algorithms in
// the resource are not allowed.
final String[] resourceAlgos = systemResources.getStringArray(
- com.android.internal.R.array.config_optionalIpSecAlgorithms);
+ android.R.array.config_optionalIpSecAlgorithms);
for (String str : resourceAlgos) {
if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) {
// This error should be caught by CTS and never be thrown to API callers
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
similarity index 97%
rename from core/java/com/android/server/NetworkManagementSocketTagger.java
rename to packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
index d89566c9..e35f6a6 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
@@ -18,7 +18,6 @@
import android.os.StrictMode;
import android.util.Log;
-import android.util.Slog;
import dalvik.system.SocketTagger;
@@ -122,7 +121,7 @@
public static void resetKernelUidStats(int uid) {
int errno = native_deleteTagData(0, uid);
if (errno < 0) {
- Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
+ Log.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
}
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index ced2e22..97281ed 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -448,7 +448,7 @@
handlerThread.start();
mHandler = new NetworkStatsHandler(handlerThread.getLooper());
mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
- mHandler.getLooper(), new HandlerExecutor(mHandler), this);
+ new HandlerExecutor(mHandler), this);
mContentResolver = mContext.getContentResolver();
mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
mNetworkStatsSubscriptionsMonitor);
@@ -474,11 +474,10 @@
*/
@NonNull
public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(@NonNull Context context,
- @NonNull Looper looper, @NonNull Executor executor,
- @NonNull NetworkStatsService service) {
+ @NonNull Executor executor, @NonNull NetworkStatsService service) {
// TODO: Update RatType passively in NSS, instead of querying into the monitor
// when notifyNetworkStatus.
- return new NetworkStatsSubscriptionsMonitor(context, looper, executor,
+ return new NetworkStatsSubscriptionsMonitor(context, executor,
(subscriberId, type) -> service.handleOnCollapsedRatTypeChanged());
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 9bb7bb8..6df6de3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.os.Looper;
import android.telephony.Annotation;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
@@ -79,9 +78,9 @@
@NonNull
private final Executor mExecutor;
- NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Looper looper,
+ NetworkStatsSubscriptionsMonitor(@NonNull Context context,
@NonNull Executor executor, @NonNull Delegate delegate) {
- super(looper);
+ super();
mSubscriptionManager = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 2b357c5..1e8cb9f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -38,6 +38,7 @@
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
+import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
@@ -54,6 +55,7 @@
public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
private static final String LOG_TAG = "RestrictedLockUtils";
+ private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
/**
* @return drawables for displaying with settings that are locked by a device admin.
@@ -92,14 +94,25 @@
}
final UserManager um = UserManager.get(context);
+ final UserHandle userHandle = UserHandle.of(userId);
final List<UserManager.EnforcingUser> enforcingUsers =
- um.getUserRestrictionSources(userRestriction, UserHandle.of(userId));
+ um.getUserRestrictionSources(userRestriction, userHandle);
if (enforcingUsers.isEmpty()) {
// Restriction is not enforced.
return null;
- } else if (enforcingUsers.size() > 1) {
- return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
+ }
+ final int size = enforcingUsers.size();
+ if (size > 1) {
+ final EnforcedAdmin enforcedAdmin = EnforcedAdmin
+ .createDefaultEnforcedAdminWithRestriction(userRestriction);
+ enforcedAdmin.user = userHandle;
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Multiple (" + size + ") enforcing users for restriction '"
+ + userRestriction + "' on user " + userHandle + "; returning default admin "
+ + "(" + enforcedAdmin + ")");
+ }
+ return enforcedAdmin;
}
final int restrictionSource = enforcingUsers.get(0).getUserRestrictionSource();
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 16cece9..8e35ee96 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -173,6 +173,7 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
Settings.Secure.ONE_HANDED_MODE_ENABLED,
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 13c1e51..2312525 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -267,6 +267,7 @@
new InclusiveIntegerRangeValidator(
Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL));
+ VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index cd6447f..00cdc9b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1813,6 +1813,10 @@
dumpSetting(s, p,
Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
SecureSettingsProto.Accessibility.ODI_CAPTIONS_VOLUME_UI_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+ SecureSettingsProto.Accessibility
+ .ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
p.end(accessibilityToken);
final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b19ef3a..c805e2d 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -138,6 +138,7 @@
"tests/src/com/android/systemui/statusbar/RankingBuilder.java",
"tests/src/com/android/systemui/statusbar/SbnBuilder.java",
"tests/src/com/android/systemui/SysuiTestableContext.java",
+ "tests/src/com/android/systemui/util/**/*Fake.java",
"tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java",
"tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java",
"tests/src/com/android/systemui/**/Fake*.java",
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 0b3eccf..29221aa 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -28,18 +28,88 @@
const val TAG = "ColorScheme"
const val ACCENT1_CHROMA = 48.0f
-const val ACCENT2_CHROMA = 16.0f
-const val ACCENT3_CHROMA = 32.0f
-const val ACCENT3_HUE_SHIFT = 60.0f
-
-const val NEUTRAL1_CHROMA = 4.0f
-const val NEUTRAL2_CHROMA = 8.0f
-
const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
-
const val MIN_CHROMA = 5
-public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
+internal enum class ChromaStrategy {
+ EQ, GTE
+}
+
+internal enum class HueStrategy {
+ SOURCE, ADD, SUBTRACT
+}
+
+internal class Chroma(val strategy: ChromaStrategy, val value: Double) {
+ fun get(sourceChroma: Double): Double {
+ return when (strategy) {
+ ChromaStrategy.EQ -> value
+ ChromaStrategy.GTE -> sourceChroma.coerceAtLeast(value)
+ }
+ }
+}
+
+internal class Hue(val strategy: HueStrategy = HueStrategy.SOURCE, val value: Double = 0.0) {
+ fun get(sourceHue: Double): Double {
+ return when (strategy) {
+ HueStrategy.SOURCE -> sourceHue
+ HueStrategy.ADD -> ColorScheme.wrapDegreesDouble(sourceHue + value)
+ HueStrategy.SUBTRACT -> ColorScheme.wrapDegreesDouble(sourceHue - value)
+ }
+ }
+}
+
+internal class TonalSpec(val hue: Hue = Hue(), val chroma: Chroma) {
+ fun shades(sourceColor: Cam): List<Int> {
+ val hue = hue.get(sourceColor.hue.toDouble())
+ val chroma = chroma.get(sourceColor.chroma.toDouble())
+ return Shades.of(hue.toFloat(), chroma.toFloat()).toList()
+ }
+}
+
+internal class CoreSpec(
+ val a1: TonalSpec,
+ val a2: TonalSpec,
+ val a3: TonalSpec,
+ val n1: TonalSpec,
+ val n2: TonalSpec
+)
+
+enum class Style(internal val coreSpec: CoreSpec) {
+ SPRITZ(CoreSpec(
+ a1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0))
+ )),
+ TONAL_SPOT(CoreSpec(
+ a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+ a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
+ a3 = TonalSpec(Hue(HueStrategy.ADD, 60.0), Chroma(ChromaStrategy.EQ, 24.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0))
+ )),
+ VIBRANT(CoreSpec(
+ a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+ a2 = TonalSpec(Hue(HueStrategy.ADD, 10.0), Chroma(ChromaStrategy.EQ, 24.0)),
+ a3 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.GTE, 32.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
+ )),
+ EXPRESSIVE(CoreSpec(
+ a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 40.0), Chroma(ChromaStrategy.GTE, 64.0)),
+ a2 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.EQ, 24.0)),
+ a3 = TonalSpec(Hue(HueStrategy.SUBTRACT, 80.0), Chroma(ChromaStrategy.GTE, 64.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 32.0))
+ )),
+}
+
+class ColorScheme(
+ @ColorInt seed: Int,
+ val darkTheme: Boolean,
+ val style: Style = Style.TONAL_SPOT
+) {
val accent1: List<Int>
val accent2: List<Int>
@@ -47,6 +117,9 @@
val neutral1: List<Int>
val neutral2: List<Int>
+ constructor(@ColorInt seed: Int, darkTheme: Boolean):
+ this(seed, darkTheme, Style.TONAL_SPOT)
+
constructor(wallpaperColors: WallpaperColors, darkTheme: Boolean):
this(getSeedColor(wallpaperColors), darkTheme)
@@ -83,14 +156,11 @@
seed
}
val camSeed = Cam.fromInt(seedArgb)
- val hue = camSeed.hue
- val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
- val tertiaryHue = wrapDegrees((hue + ACCENT3_HUE_SHIFT).toInt())
- accent1 = Shades.of(hue, chroma).toList()
- accent2 = Shades.of(hue, ACCENT2_CHROMA).toList()
- accent3 = Shades.of(tertiaryHue.toFloat(), ACCENT3_CHROMA).toList()
- neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList()
- neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList()
+ accent1 = style.coreSpec.a1.shades(camSeed)
+ accent2 = style.coreSpec.a2.shades(camSeed)
+ accent3 = style.coreSpec.a3.shades(camSeed)
+ neutral1 = style.coreSpec.n1.shades(camSeed)
+ neutral2 = style.coreSpec.n2.shades(camSeed)
}
override fun toString(): String {
@@ -100,6 +170,7 @@
" accent1: ${humanReadable(accent1)}\n" +
" accent2: ${humanReadable(accent2)}\n" +
" accent3: ${humanReadable(accent3)}\n" +
+ " style: $style\n" +
"}"
}
@@ -225,6 +296,20 @@
}
}
+ public fun wrapDegreesDouble(degrees: Double): Double {
+ return when {
+ degrees < 0 -> {
+ (degrees % 360) + 360
+ }
+ degrees >= 360 -> {
+ degrees % 360
+ }
+ else -> {
+ degrees
+ }
+ }
+ }
+
private fun hueDiff(a: Float, b: Float): Float {
return 180f - ((a - b).absoluteValue - 180f).absoluteValue
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index ffac26b..1ef5324 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -159,9 +159,9 @@
public Supplier<Icon> iconSupplier;
public int state = DEFAULT_STATE;
public CharSequence label;
- public CharSequence secondaryLabel;
+ @Nullable public CharSequence secondaryLabel;
public CharSequence contentDescription;
- public CharSequence stateDescription;
+ @Nullable public CharSequence stateDescription;
public CharSequence dualLabelContentDescription;
public boolean disabledByPolicy;
public boolean dualTarget = false;
@@ -170,6 +170,7 @@
public SlashState slash;
public boolean handlesLongClick = true;
public boolean showRippleEffect = true;
+ @Nullable
public Drawable sideViewCustomDrawable;
public String spec;
diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
new file mode 100644
index 0000000..f898ef6
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/dream_overlay_complications_layer"
+ android:padding="20dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextClock
+ android:id="@+id/time_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-thin"
+ android:format12Hour="h:mm"
+ android:format24Hour="kk:mm"
+ android:shadowColor="#B2000000"
+ android:shadowRadius="2.0"
+ android:singleLine="true"
+ android:textSize="72sp"
+ app:layout_constraintBottom_toTopOf="@+id/date_view"
+ app:layout_constraintStart_toStartOf="parent" />
+ <TextClock
+ android:id="@+id/date_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:shadowColor="#B2000000"
+ android:shadowRadius="2.0"
+ android:format12Hour="EEE, MMM d"
+ android:format24Hour="EEE, MMM d"
+ android:singleLine="true"
+ android:textSize="18sp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@+id/time_view"
+ app:layout_constraintStart_toStartOf="@+id/time_view" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 4929f50..c6b502e 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -28,6 +28,8 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
+ <include layout="@layout/dream_overlay_complications_layer" />
+
<com.android.systemui.dreams.DreamOverlayStatusBarView
android:id="@+id/dream_overlay_status_bar"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_container.xml
similarity index 93%
rename from packages/SystemUI/res/layout/keyguard_media_header.xml
rename to packages/SystemUI/res/layout/keyguard_media_container.xml
index 63a878f..c717e37 100644
--- a/packages/SystemUI/res/layout/keyguard_media_header.xml
+++ b/packages/SystemUI/res/layout/keyguard_media_container.xml
@@ -16,7 +16,7 @@
-->
<!-- Layout for media controls on the lockscreen -->
-<com.android.systemui.statusbar.notification.stack.MediaHeaderView
+<com.android.systemui.statusbar.notification.stack.MediaContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2bf121d2e..2916c1c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1165,10 +1165,10 @@
<string name="wallet_lockscreen_settings_label">Lock screen settings</string>
<!-- QR Code Scanner label, title [CHAR LIMIT=32] -->
- <string name="qr_code_scanner_title">Scan QR</string>
+ <string name="qr_code_scanner_title">QR Code</string>
<!-- QR Code Scanner description [CHAR LIMIT=NONE] -->
- <string name="qr_code_scanner_description">Click to scan a QR code</string>
+ <string name="qr_code_scanner_description">Tap to scan</string>
<!-- Name of the work status bar icon. -->
<string name="status_bar_work">Work profile</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 24e93ef..953b0e0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -21,24 +21,24 @@
import android.hardware.SensorManager
import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
-import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
-import java.lang.IllegalStateException
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
import java.util.concurrent.Executor
/**
* Factory for [UnfoldTransitionProgressProvider].
*
- * This is needed as Launcher has to create the object manually.
- * Sysui create it using dagger (see [UnfoldTransitionModule]).
+ * This is needed as Launcher has to create the object manually. Sysui create it using dagger (see
+ * [UnfoldTransitionModule]).
*/
fun createUnfoldTransitionProgressProvider(
context: Context,
@@ -52,10 +52,45 @@
): UnfoldTransitionProgressProvider {
if (!config.isEnabled) {
- throw IllegalStateException("Trying to create " +
- "UnfoldTransitionProgressProvider when the transition is disabled")
+ throw IllegalStateException(
+ "Trying to create " +
+ "UnfoldTransitionProgressProvider when the transition is disabled")
}
+ val foldStateProvider =
+ createFoldStateProvider(
+ context,
+ config,
+ screenStatusProvider,
+ deviceStateManager,
+ sensorManager,
+ mainHandler,
+ mainExecutor)
+
+ val unfoldTransitionProgressProvider =
+ if (config.isHingeAngleEnabled) {
+ PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
+ } else {
+ FixedTimingTransitionProgressProvider(foldStateProvider)
+ }
+
+ return ScaleAwareTransitionProgressProvider(
+ unfoldTransitionProgressProvider, context.contentResolver)
+ .apply {
+ // Always present callback that logs animation beginning and end.
+ addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
+ }
+}
+
+fun createFoldStateProvider(
+ context: Context,
+ config: UnfoldTransitionConfig,
+ screenStatusProvider: ScreenStatusProvider,
+ deviceStateManager: DeviceStateManager,
+ sensorManager: SensorManager,
+ mainHandler: Handler,
+ mainExecutor: Executor
+): FoldStateProvider {
val hingeAngleProvider =
if (config.isHingeAngleEnabled) {
HingeSensorAngleProvider(sensorManager)
@@ -63,28 +98,13 @@
EmptyHingeAngleProvider()
}
- val foldStateProvider = DeviceFoldStateProvider(
+ return DeviceFoldStateProvider(
context,
hingeAngleProvider,
screenStatusProvider,
deviceStateManager,
mainExecutor,
- mainHandler
- )
-
- val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
- PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
- } else {
- FixedTimingTransitionProgressProvider(foldStateProvider)
- }
- return ScaleAwareTransitionProgressProvider(
- unfoldTransitionProgressProvider,
- context.contentResolver
- ).apply {
- // Always present callback that logs animation beginning and end.
- addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
- }
+ mainHandler)
}
-fun createConfig(context: Context): UnfoldTransitionConfig =
- ResourceUnfoldTransitionConfig(context)
+fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context)
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index a10efa9..4784bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -225,6 +225,13 @@
}
@Override
+ public void onDrag(int displayId) {
+ if (mWindowMagnificationConnectionImpl != null) {
+ mWindowMagnificationConnectionImpl.onDrag(displayId);
+ }
+ }
+
+ @Override
public void requestWindowMagnificationConnection(boolean connect) {
if (connect) {
setWindowMagnificationConnection();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index 2133da2..1d22633 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -142,4 +142,14 @@
}
}
}
+
+ void onDrag(int displayId) {
+ if (mConnectionCallback != null) {
+ try {
+ mConnectionCallback.onDrag(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to inform taking control by a user", e);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index b064ba9..aa1a433 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -252,7 +252,12 @@
mMagnificationFrame.height());
mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect,
Surface.ROTATION_0).apply();
- mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, mSourceBounds);
+
+ // Notify source bounds change when the magnifier is not animating.
+ if (!mAnimationController.isAnimating()) {
+ mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId,
+ mSourceBounds);
+ }
}
};
mUpdateStateDescriptionRunnable = () -> {
@@ -596,7 +601,6 @@
private void modifyWindowMagnification(SurfaceControl.Transaction t) {
mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
updateMirrorViewLayout();
-
}
/**
@@ -800,7 +804,7 @@
* are as same as current values, or the transition is interrupted
* due to the new transition request.
*/
- void enableWindowMagnification(float scale, float centerX, float centerY,
+ public void enableWindowMagnification(float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
mAnimationController.enableWindowMagnification(scale, centerX, centerY,
@@ -960,6 +964,7 @@
@Override
public boolean onDrag(float offsetX, float offsetY) {
moveWindowMagnifier(offsetX, offsetY);
+ mWindowMagnifierCallback.onDrag(mDisplayId);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
index 628a5e8..bdded10 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility;
import android.graphics.Rect;
+import android.view.ViewConfiguration;
/**
* A callback to inform {@link com.android.server.accessibility.AccessibilityManagerService} about
@@ -53,4 +54,13 @@
* @param displayId The logical display id.
*/
void onAccessibilityActionPerformed(int displayId);
+
+ /**
+ * Called when the user is performing dragging gesture. It is started after the offset
+ * between the down location and the move event location exceed
+ * {@link ViewConfiguration#getScaledTouchSlop()}.
+ *
+ * @param displayId The logical display id.
+ */
+ void onDrag(int displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java
new file mode 100644
index 0000000..08d969b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for lockscreen to shade transition events. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface LSShadeTransitionLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 1f953d7..b323586 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -61,6 +61,14 @@
return factory.create("NotifHeadsUpLog", 1000);
}
+ /** Provides a logging buffer for all logs for lockscreen to shade transition events. */
+ @Provides
+ @SysUISingleton
+ @LSShadeTransitionLog
+ public static LogBuffer provideLSShadeTransitionControllerBuffer(LogBufferFactory factory) {
+ return factory.create("LSShadeTransitionLog", 50);
+ }
+
/** Provides a logging buffer for all logs related to managing notification sections. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 5ff624d..44727f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -27,7 +27,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
@@ -96,14 +96,14 @@
/**
* single pane media container placed at the top of the notifications list
*/
- var singlePaneContainer: MediaHeaderView? = null
+ var singlePaneContainer: MediaContainerView? = null
private set
private var splitShadeContainer: ViewGroup? = null
/**
* Attaches media container in single pane mode, situated at the top of the notifications list
*/
- fun attachSinglePaneContainer(mediaView: MediaHeaderView?) {
+ fun attachSinglePaneContainer(mediaView: MediaContainerView?) {
val needsListener = singlePaneContainer == null
singlePaneContainer = mediaView
if (needsListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index ce3b443..29321b4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -865,6 +865,10 @@
println("playerKeys: ${MediaPlayerData.playerKeys()}")
println("smartspaceMediaData: ${MediaPlayerData.smartspaceMediaData}")
println("shouldPrioritizeSs: ${MediaPlayerData.shouldPrioritizeSs}")
+ println("current size: $currentCarouselWidth x $currentCarouselHeight")
+ println("location: $desiredLocation")
+ println("state: ${desiredHostState?.expansion}, " +
+ "only active ${desiredHostState?.showsOnlyActiveMedia}")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9c4227e..d190dcb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -227,6 +227,7 @@
private @Behavior int mBehavior;
private boolean mTransientShown;
+ private boolean mTransientShownFromGestureOnSystemBar;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private LightBarController mLightBarController;
private final LightBarController mMainLightBarController;
@@ -871,6 +872,9 @@
+ windowStateToString(mNavigationBarWindowState));
pw.println(" mNavigationBarMode="
+ BarTransitions.modeToString(mNavigationBarMode));
+ pw.println(" mTransientShown=" + mTransientShown);
+ pw.println(" mTransientShownFromGestureOnSystemBar="
+ + mTransientShownFromGestureOnSystemBar);
dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
mNavigationBarView.dump(pw);
}
@@ -989,7 +993,8 @@
}
@Override
- public void showTransient(int displayId, @InternalInsetsType int[] types) {
+ public void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar) {
if (displayId != mDisplayId) {
return;
}
@@ -998,6 +1003,7 @@
}
if (!mTransientShown) {
mTransientShown = true;
+ mTransientShownFromGestureOnSystemBar = isGestureOnSystemBar;
handleTransientChanged();
}
}
@@ -1016,12 +1022,14 @@
private void clearTransient() {
if (mTransientShown) {
mTransientShown = false;
+ mTransientShownFromGestureOnSystemBar = false;
handleTransientChanged();
}
}
private void handleTransientChanged() {
- mNavigationBarView.onTransientStateChanged(mTransientShown);
+ mNavigationBarView.onTransientStateChanged(mTransientShown,
+ mTransientShownFromGestureOnSystemBar);
final int barMode = barMode(mTransientShown, mAppearance);
if (updateBarMode(barMode) && mLightBarController != null) {
mLightBarController.onNavigationBarModeChanged(barMode);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 7adb7ac..5fbdd88 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -447,12 +447,17 @@
mRegionSamplingHelper.setWindowHasBlurs(hasBlurs);
}
- void onTransientStateChanged(boolean isTransient) {
+ void onTransientStateChanged(boolean isTransient, boolean isGestureOnSystemBar) {
mEdgeBackGestureHandler.onNavBarTransientStateChanged(isTransient);
// The visibility of the navigation bar buttons is dependent on the transient state of
// the navigation bar.
if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ // Always allow the overlay if in non-gestural nav mode, otherwise, only allow showing
+ // the overlay if the user is swiping directly over a system bar
+ boolean allowNavBarOverlay = !QuickStepContract.isGesturalMode(mNavBarMode)
+ || isGestureOnSystemBar;
+ isTransient = isTransient && allowNavBarOverlay;
mNavBarOverlayController.setButtonState(isTransient, /* force */ false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index feda99f..002dd10 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -19,7 +19,7 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -86,6 +86,7 @@
private static final String TAG = TaskbarDelegate.class.getSimpleName();
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
+ private final NavigationBarOverlayController mNavBarOverlayController;
private boolean mInitialized;
private CommandQueue mCommandQueue;
private OverviewProxyService mOverviewProxyService;
@@ -140,6 +141,13 @@
@Override
public void hide() {
+ clearTransient();
+ }
+ };
+
+ private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
+ if (visible) {
+ mAutoHideController.touchAutoHide();
}
};
@@ -147,6 +155,11 @@
public TaskbarDelegate(Context context) {
mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
.create(context);
+ mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.init(mNavbarOverlayVisibilityChangeCallback,
+ mEdgeBackGestureHandler::updateNavigationBarOverlayExcludeRegion);
+ }
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mPipListener = mEdgeBackGestureHandler::setPipStashExclusionBounds;
@@ -206,6 +219,9 @@
mNavigationModeController.addListener(this));
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.init();
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.registerListeners();
+ }
mEdgeBackGestureHandler.onNavBarAttached();
// Initialize component callback
Display display = mDisplayManager.getDisplay(displayId);
@@ -229,6 +245,9 @@
mNavigationModeController.removeListener(this);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.destroy();
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.unregisterListeners();
+ }
mEdgeBackGestureHandler.onNavBarDetached();
mScreenPinningNotify = null;
if (mWindowContext != null) {
@@ -350,14 +369,17 @@
}
@Override
- public void showTransient(int displayId, int[] types) {
+ public void showTransient(int displayId, int[] types, boolean isGestureOnSystemBar) {
if (displayId != mDisplayId) {
return;
}
- if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
+ if (!containsType(types, ITYPE_EXTRA_NAVIGATION_BAR)) {
return;
}
- mTaskbarTransientShowing = true;
+ if (!mTaskbarTransientShowing) {
+ mTaskbarTransientShowing = true;
+ onTransientStateChanged();
+ }
}
@Override
@@ -365,15 +387,14 @@
if (displayId != mDisplayId) {
return;
}
- if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
+ if (!containsType(types, ITYPE_EXTRA_NAVIGATION_BAR)) {
return;
}
- mTaskbarTransientShowing = false;
+ clearTransient();
}
@Override
public void onTaskbarAutohideSuspend(boolean suspend) {
- mTaskbarTransientShowing = suspend;
if (suspend) {
mAutoHideController.suspendAutoHide();
} else {
@@ -381,6 +402,30 @@
}
}
+ private void clearTransient() {
+ if (mTaskbarTransientShowing) {
+ mTaskbarTransientShowing = false;
+ onTransientStateChanged();
+ }
+ }
+
+ private void onTransientStateChanged() {
+ mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTaskbarTransientShowing);
+
+ // The visibility of the navigation bar buttons is dependent on the transient state of
+ // the navigation bar.
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.setButtonState(mTaskbarTransientShowing, /* force */ false);
+ }
+ }
+
+ @Override
+ public void onRecentsAnimationStateChanged(boolean running) {
+ if (running) {
+ mNavBarOverlayController.setButtonState(/* visible */false, /* force */true);
+ }
+ }
+
@Override
public void onNavigationModeChanged(int mode) {
mNavigationMode = mode;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
index 1195184..18d28bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
@@ -36,6 +36,7 @@
private final int mItemSize;
private final Handler mHandler;
+ @Nullable
private ListAdapter mAdapter;
private int mCount;
private boolean mEnableAutoSizing;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 6d1f8f7..d20141b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -19,6 +19,7 @@
import android.view.animation.OvershootInterpolator;
import android.widget.Scroller;
+import androidx.annotation.Nullable;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
@@ -51,14 +52,17 @@
private final ArrayList<TileRecord> mTiles = new ArrayList<>();
private final ArrayList<TileLayout> mPages = new ArrayList<>();
+ @Nullable
private PageIndicator mPageIndicator;
private float mPageIndicatorPosition;
+ @Nullable
private PageListener mPageListener;
private boolean mListening;
private Scroller mScroller;
+ @Nullable
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
private boolean mDistributeTiles = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 6c7d6e0..e06b768 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -26,6 +26,8 @@
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnLayoutChangeListener;
+import androidx.annotation.Nullable;
+
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSTile;
@@ -88,6 +90,7 @@
private final View mQSFooterActions;
private final View mQQSFooterActions;
+ @Nullable
private PagedTileLayout mPagedLayout;
private boolean mOnFirstPage = true;
@@ -95,6 +98,7 @@
private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
// Animator for elements in the first page, including secondary labels and qqs brightness
// slider, as well as animating the alpha of the QS tile layout (as we are tracking QQS tiles)
+ @Nullable
private TouchAnimator mFirstPageAnimator;
// TranslationX animator for QQS/QS tiles
private TouchAnimator mTranslationXAnimator;
@@ -109,13 +113,16 @@
// This animates fading of SecurityFooter and media divider
private TouchAnimator mAllPagesDelayedAnimator;
// Animator for brightness slider(s)
+ @Nullable
private TouchAnimator mBrightnessAnimator;
// Animator for Footer actions in QQS
private TouchAnimator mQQSFooterActionsAnimator;
// Height animator for QQS tiles (height changing from QQS size to QS size)
+ @Nullable
private HeightExpansionAnimator mQQSTileHeightAnimator;
// Height animator for QS tile in first page but not in QQS, to present the illusion that they
// are expanding alongside the QQS tiles
+ @Nullable
private HeightExpansionAnimator mOtherFirstPageTilesHeightAnimator;
// Pair of animators for each non first page. The creation is delayed until the user first
// scrolls to that page, in order to get the proper measures and layout.
@@ -215,7 +222,7 @@
}
@Override
- public void onViewAttachedToWindow(View v) {
+ public void onViewAttachedToWindow(@Nullable View v) {
mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
MOVE_FULL_ROWS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index d43404b..04e2252 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -64,15 +64,18 @@
protected TextView mDetailDoneButton;
@VisibleForTesting
QSDetailClipper mClipper;
+ @Nullable
private DetailAdapter mDetailAdapter;
private QSPanelController mQsPanelController;
protected View mQsDetailHeader;
protected TextView mQsDetailHeaderTitle;
private ViewStub mQsDetailHeaderSwitchStub;
+ @Nullable
private Switch mQsDetailHeaderSwitch;
protected ImageView mQsDetailHeaderProgress;
+ @Nullable
protected QSTileHost mHost;
private boolean mScanState;
@@ -87,6 +90,7 @@
private boolean mSwitchState;
private QSFooter mFooter;
+ @Nullable
private QSContainerController mQsContainerController;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
@@ -183,12 +187,14 @@
}
public interface Callback {
- void onShowingDetail(DetailAdapter detail, int x, int y);
+ /** Handle an event of showing detail. */
+ void onShowingDetail(@Nullable DetailAdapter detail, int x, int y);
void onToggleStateChanged(boolean state);
void onScanStateChanged(boolean state);
}
- public void handleShowingDetail(final DetailAdapter adapter, int x, int y,
+ /** Handle an event of showing detail. */
+ public void handleShowingDetail(final @Nullable DetailAdapter adapter, int x, int y,
boolean toggleQs) {
final boolean showingDetail = adapter != null;
final boolean wasShowingDetail = mDetailAdapter != null;
@@ -378,7 +384,8 @@
}
@Override
- public void onShowingDetail(final DetailAdapter detail, final int x, final int y) {
+ public void onShowingDetail(
+ final @Nullable DetailAdapter detail, final int x, final int y) {
post(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index 63cedd0..43136d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -23,12 +23,15 @@
import android.view.View;
import android.view.ViewAnimationUtils;
+import androidx.annotation.Nullable;
+
/** Helper for quick settings detail panel clip animations. **/
public class QSDetailClipper {
private final View mDetail;
private final TransitionDrawable mBackground;
+ @Nullable
private Animator mAnimator;
public QSDetailClipper(View detail) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
index b50af00..afd4f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs;
+import androidx.annotation.Nullable;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.qs.DetailAdapter;
@@ -26,13 +28,14 @@
*/
@SysUISingleton
public class QSDetailDisplayer {
+ @Nullable
private QSPanelController mQsPanelController;
@Inject
public QSDetailDisplayer() {
}
- public void setQsPanelController(QSPanelController qsPanelController) {
+ public void setQsPanelController(@Nullable QSPanelController qsPanelController) {
mQsPanelController = qsPanelController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index e93c349..eb3247b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -33,6 +33,8 @@
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile;
@@ -50,6 +52,7 @@
private final Adapter mAdapter = new Adapter();
private String mTag;
+ @Nullable
private Callback mCallback;
private boolean mItemsVisible = true;
private AutoSizingList mItemList;
@@ -130,7 +133,8 @@
mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
}
- public void setItems(Item[] items) {
+ /** Set items. */
+ public void setItems(@Nullable Item[] items) {
mHandler.removeMessages(H.SET_ITEMS);
mHandler.obtainMessage(H.SET_ITEMS, items).sendToTarget();
}
@@ -266,9 +270,12 @@
}
public int iconResId;
+ @Nullable
public QSTile.Icon icon;
+ @Nullable
public Drawable overlay;
public CharSequence line1;
+ @Nullable
public CharSequence line2;
public Object tag;
public boolean canDisconnect;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 4d23958..066a286 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -48,6 +48,7 @@
private TextView mBuildText;
private View mActionsContainer;
+ @Nullable
protected TouchAnimator mFooterAnimator;
private boolean mQsDisabled;
@@ -56,6 +57,7 @@
private boolean mShouldShowBuildText;
+ @Nullable
private OnClickListener mExpandClickListener;
private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index bb8a1e8..41dced6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -117,6 +117,7 @@
private QSPanelController mQSPanelController;
private QuickQSPanelController mQuickQSPanelController;
private QSCustomizerController mQSCustomizerController;
+ @Nullable
private ScrollListener mScrollListener;
/**
* When true, QS will translate from outside the screen. It will be clipped with parallax
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ff9790c..d4d6da8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -401,6 +401,9 @@
pw.print(" "); pw.println(record.tileView.toString());
}
}
+ if (mMediaHost != null) {
+ pw.println(" media bounds: " + mMediaHost.getCurrentBounds());
+ }
}
public QSPanel.QSTileLayout getTileLayout() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 7f19d0e..878f753 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -48,6 +48,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
@@ -85,8 +86,10 @@
protected H mHandler;
private boolean mIsVisible;
+ @Nullable
private CharSequence mFooterTextContent = null;
private int mFooterIconId;
+ @Nullable
private Drawable mPrimaryFooterIconDrawable;
@Inject
@@ -240,6 +243,7 @@
mMainHandler.post(mUpdateDisplayState);
}
+ @Nullable
protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile,
boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
@@ -497,6 +501,7 @@
return mContext.getString(R.string.ok);
}
+ @Nullable
private String getNegativeButton() {
if (mSecurityController.isParentalControlsEnabled()) {
return mContext.getString(R.string.monitoring_button_view_controls);
@@ -504,6 +509,7 @@
return null;
}
+ @Nullable
protected CharSequence getManagementMessage(boolean isDeviceManaged,
CharSequence organizationName) {
if (!isDeviceManaged) {
@@ -521,6 +527,7 @@
return mContext.getString(R.string.monitoring_description_management);
}
+ @Nullable
protected CharSequence getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts,
boolean hasCACertsInWorkProfile) {
if (!(hasCACerts || hasCACertsInWorkProfile)) return null;
@@ -534,6 +541,7 @@
return mContext.getString(R.string.monitoring_description_ca_certificate);
}
+ @Nullable
protected CharSequence getNetworkLoggingMessage(boolean isDeviceManaged,
boolean isNetworkLoggingEnabled) {
if (!isNetworkLoggingEnabled) return null;
@@ -545,6 +553,7 @@
}
}
+ @Nullable
protected CharSequence getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile,
String vpnName, String vpnNameWorkProfile) {
if (vpnName == null && vpnNameWorkProfile == null) return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 1030c21..cca4913 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -29,6 +29,8 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.UiEventLogger;
@@ -98,6 +100,7 @@
private final CustomTileStatePersister mCustomTileStatePersister;
private final List<Callback> mCallbacks = new ArrayList<>();
+ @Nullable
private AutoTileManager mAutoTiles;
private final StatusBarIconController mIconController;
private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
@@ -472,6 +475,8 @@
saveTilesToSettings(newTiles);
}
+ /** Create a {@link QSTile} of a {@code tileSpec} type. */
+ @Nullable
public QSTile createTile(String tileSpec) {
for (int i = 0; i < mQsFactories.size(); i++) {
QSTile t = mQsFactories.get(i).createTile(tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 8b94983..866b1b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -33,6 +33,7 @@
import android.widget.Space;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.policy.SystemBarUtils;
import com.android.settingslib.Utils;
@@ -44,7 +45,6 @@
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.VariableDateView;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import java.util.List;
@@ -57,8 +57,11 @@
private boolean mExpanded;
private boolean mQsDisabled;
+ @Nullable
private TouchAnimator mAlphaAnimator;
+ @Nullable
private TouchAnimator mTranslationAnimator;
+ @Nullable
private TouchAnimator mIconsAlphaAnimator;
private TouchAnimator mIconsAlphaAnimatorFixed;
@@ -85,7 +88,9 @@
private StatusIconContainer mIconContainer;
private View mPrivacyChip;
+ @Nullable
private TintedIconManager mTintedIconManager;
+ @Nullable
private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private StatusBarContentInsetsProvider mInsetsProvider;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
index bb2340c..130bcab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
@@ -7,13 +7,15 @@
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import androidx.annotation.Nullable;
+
public class QuickTileLayout extends LinearLayout {
public QuickTileLayout(Context context) {
this(context, null);
}
- public QuickTileLayout(Context context, AttributeSet attrs) {
+ public QuickTileLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setGravity(Gravity.CENTER);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
index a9b2376..9011853 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
@@ -60,7 +60,9 @@
private final RectF mSlashRect = new RectF(0, 0, 0, 0);
private float mRotation;
private boolean mSlashed;
+ @Nullable
private Mode mTintMode;
+ @Nullable
private ColorStateList mTintList;
private boolean mAnimationEnabled = true;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index bff318a..da82d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -9,6 +9,8 @@
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -49,7 +51,7 @@
this(context, null);
}
- public TileLayout(Context context, AttributeSet attrs) {
+ public TileLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setFocusableInTouchMode(true);
mLessRows = ((Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0)
@@ -67,7 +69,7 @@
}
@Override
- public void setListening(boolean listening, UiEventLogger uiEventLogger) {
+ public void setListening(boolean listening, @Nullable UiEventLogger uiEventLogger) {
if (mListening == listening) return;
mListening = listening;
for (TileRecord record : mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index ca8f681..bc62416 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -20,6 +20,8 @@
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.List;
@@ -37,12 +39,19 @@
private final float mStartDelay;
private final float mEndDelay;
private final float mSpan;
+ @Nullable
private final Interpolator mInterpolator;
+ @Nullable
private final Listener mListener;
private float mLastT = -1;
- private TouchAnimator(Object[] targets, KeyframeSet[] keyframeSets,
- float startDelay, float endDelay, Interpolator interpolator, Listener listener) {
+ private TouchAnimator(
+ Object[] targets,
+ KeyframeSet[] keyframeSets,
+ float startDelay,
+ float endDelay,
+ @Nullable Interpolator interpolator,
+ @Nullable Listener listener) {
mTargets = targets;
mKeyframeSets = keyframeSets;
mStartDelay = startDelay;
@@ -126,7 +135,9 @@
private float mStartDelay;
private float mEndDelay;
+ @Nullable
private Interpolator mInterpolator;
+ @Nullable
private Listener mListener;
public Builder addFloat(Object target, String property, float... values) {
@@ -183,7 +194,8 @@
return this;
}
- public Builder setInterpolator(Interpolator intepolator) {
+ /** Sets interpolator. */
+ public Builder setInterpolator(@Nullable Interpolator intepolator) {
mInterpolator = intepolator;
return this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 32ac733..2959c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -25,6 +25,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.Utils;
@@ -40,6 +41,7 @@
private ImageView mMobileSignal;
private ImageView mMobileRoaming;
private View mSpacer;
+ @Nullable
private CellSignalState mLastSignalState;
private boolean mProviderModelInitialized = false;
private boolean mIsSingleCarrier;
@@ -125,7 +127,7 @@
return true;
}
- private boolean hasValidTypeContentDescription(String typeContentDescription) {
+ private boolean hasValidTypeContentDescription(@Nullable String typeContentDescription) {
return TextUtils.equals(typeContentDescription,
mContext.getString(R.string.data_connection_no_internet))
|| TextUtils.equals(typeContentDescription,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 9ebdb1c..b59c0cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -28,6 +28,7 @@
import android.widget.LinearLayout;
import android.widget.Toolbar;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
@@ -107,7 +108,7 @@
mQsContainerController = controller;
}
- public void setQs(QS qs) {
+ public void setQs(@Nullable QS qs) {
mQs = qs;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 618a429..9739011 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -28,6 +28,7 @@
import android.widget.Toolbar;
import android.widget.Toolbar.OnMenuItemClickListener;
+import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -198,7 +199,7 @@
}
/** */
- public void setQs(QSFragment qsFragment) {
+ public void setQs(@Nullable QSFragment qsFragment) {
mView.setQs(qsFragment);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index eae2565..b29687f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -31,6 +31,8 @@
import android.util.ArraySet;
import android.widget.Button;
+import androidx.annotation.Nullable;
+
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -79,7 +81,7 @@
mUserTracker = userTracker;
}
- public void setListener(TileStateListener listener) {
+ public void setListener(@Nullable TileStateListener listener) {
mListener = listener;
}
@@ -269,6 +271,7 @@
});
}
+ @Nullable
private State getState(Collection<QSTile> tiles, String spec) {
for (QSTile tile : tiles) {
if (spec.equals(tile.getTileSpec())) {
@@ -278,7 +281,8 @@
return null;
}
- private void addTile(String spec, CharSequence appLabel, State state, boolean isSystem) {
+ private void addTile(
+ String spec, @Nullable CharSequence appLabel, State state, boolean isSystem) {
if (mSpecs.contains(spec)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 10efec3..4f15351 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -89,7 +89,9 @@
private final TileServiceManager mServiceManager;
private final int mUser;
private final CustomTileStatePersister mCustomTileStatePersister;
+ @Nullable
private android.graphics.drawable.Icon mDefaultIcon;
+ @Nullable
private CharSequence mDefaultLabel;
private final Context mUserContext;
@@ -197,8 +199,8 @@
/**
* Compare two icons, only works for resources.
*/
- private boolean iconEquals(android.graphics.drawable.Icon icon1,
- android.graphics.drawable.Icon icon2) {
+ private boolean iconEquals(@Nullable android.graphics.drawable.Icon icon1,
+ @Nullable android.graphics.drawable.Icon icon2) {
if (icon1 == icon2) {
return true;
}
@@ -372,6 +374,7 @@
Uri.fromParts("package", mComponent.getPackageName(), null));
}
+ @Nullable
private Intent resolveIntent(Intent i) {
ResolveInfo result = mContext.getPackageManager().resolveActivityAsUser(i, 0, mUser);
return result != null ? new Intent(TileService.ACTION_QS_TILE_PREFERENCES)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index e6612fe..17ae7ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -36,6 +36,7 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -85,6 +86,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private Set<Integer> mQueuedMessages = new ArraySet<>();
+ @Nullable
private QSTileServiceWrapper mWrapper;
private boolean mListening;
private IBinder mClickBinder;
@@ -95,6 +97,7 @@
private AtomicBoolean mPackageReceiverRegistered = new AtomicBoolean(false);
private AtomicBoolean mUserReceiverRegistered = new AtomicBoolean(false);
private boolean mUnbindImmediate;
+ @Nullable
private TileChangeListener mChangeListener;
// Return value from bindServiceAsUser, determines whether safe to call unbind.
private boolean mIsBound;
@@ -466,6 +469,7 @@
}
}
+ @Nullable
@Override
public IBinder asBinder() {
return mWrapper != null ? mWrapper.asBinder() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index bfa2aaa4..0a3c17c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -35,6 +35,8 @@
import android.util.ArrayMap;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Dependency;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -297,6 +299,7 @@
}
}
+ @Nullable
@Override
public Tile getTile(IBinder token) {
CustomTile customTile = getTileForToken(token);
@@ -330,12 +333,14 @@
return keyguardStateController.isMethodSecure() && keyguardStateController.isShowing();
}
+ @Nullable
private CustomTile getTileForToken(IBinder token) {
synchronized (mServices) {
return mTokenMap.get(token);
}
}
+ @Nullable
private CustomTile getTileForComponent(ComponentName component) {
synchronized (mServices) {
return mTiles.get(component);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 76950d1..5e68f61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -18,6 +18,8 @@
import android.os.Build;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSIconView;
@@ -173,6 +175,8 @@
mColorCorrectionTileProvider = colorCorrectionTileProvider;
}
+ /** Creates a tile with a type based on {@code tileSpec} */
+ @Nullable
public final QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
@@ -182,6 +186,7 @@
return tile;
}
+ @Nullable
protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
switch (tileSpec) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index b5f07d1..6d9d5b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -116,6 +116,7 @@
private boolean mAnnounceNextStateChange;
private String mTileSpec;
+ @Nullable
private EnforcedAdmin mEnforcedAdmin;
private boolean mShowingDetail;
private int mIsFullQs;
@@ -260,6 +261,8 @@
return new QSIconViewImpl(context);
}
+ /** Returns corresponding DetailAdapter. */
+ @Nullable
public DetailAdapter getDetailAdapter() {
return null; // optional
}
@@ -342,7 +345,7 @@
refreshState(null);
}
- protected final void refreshState(Object arg) {
+ protected final void refreshState(@Nullable Object arg) {
mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
}
@@ -432,9 +435,10 @@
*
* @return the intent to launch
*/
+ @Nullable
public abstract Intent getLongClickIntent();
- protected void handleRefreshState(Object arg) {
+ protected void handleRefreshState(@Nullable Object arg) {
handleUpdateState(mTmpState, arg);
boolean changed = mTmpState.copyTo(mState);
if (mReadyState == READY_STATE_READYING) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
index 72c68ce..f1e82b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
@@ -27,6 +27,7 @@
public class SlashImageView extends ImageView {
+ @Nullable
@VisibleForTesting
protected SlashDrawable mSlash;
private boolean mAnimationEnabled = true;
@@ -35,6 +36,7 @@
super(context);
}
+ @Nullable
protected SlashDrawable getSlash() {
return mSlash;
}
@@ -52,7 +54,7 @@
}
@Override
- public void setImageDrawable(Drawable drawable) {
+ public void setImageDrawable(@Nullable Drawable drawable) {
if (drawable == null) {
mSlash = null;
super.setImageDrawable(null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 5552105..754f8e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -320,6 +320,7 @@
// We probably won't ever have space in the UI for more than 20 devices, so don't
// get info for them.
private static final int MAX_DEVICES = 20;
+ @Nullable
private QSDetailItems mItems;
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index e5601f2..698a253 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -257,7 +257,9 @@
private static final class CallbackInfo {
boolean airplaneModeEnabled;
+ @Nullable
CharSequence dataSubscriptionName;
+ @Nullable
CharSequence dataContentDescription;
boolean activityIn;
boolean activityOut;
@@ -320,6 +322,7 @@
return mContext.getString(R.string.quick_settings_cellular_detail_title);
}
+ @Nullable
@Override
public Boolean getToggleState() {
return mDataController.isMobileDataSupported()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 49c548d..a06dc8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -360,6 +360,7 @@
private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
+ @Nullable
private ZenModePanel mZenPanel;
private boolean mAuto;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index cd81b4a..9df942d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -145,13 +145,15 @@
&& mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM);
}
- private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
+ @Nullable
+ private CharSequence getSecondaryLabel(boolean isTransient, @Nullable String statusLabel) {
return isTransient
? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
: statusLabel;
}
- private static String removeDoubleQuotes(String string) {
+ @Nullable
+ private static String removeDoubleQuotes(@Nullable String string) {
if (string == null) return null;
final int length = string.length();
if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
@@ -163,6 +165,7 @@
private static final class EthernetCallbackInfo {
boolean mConnected;
int mEthernetSignalIconId;
+ @Nullable
String mEthernetContentDescription;
@Override
@@ -180,11 +183,14 @@
boolean mEnabled;
boolean mConnected;
int mWifiSignalIconId;
+ @Nullable
String mSsid;
boolean mActivityIn;
boolean mActivityOut;
+ @Nullable
String mWifiSignalContentDescription;
boolean mIsTransient;
+ @Nullable
public String mStatusLabel;
boolean mNoDefaultNetwork;
boolean mNoValidatedNetwork;
@@ -211,7 +217,9 @@
private static final class CellularCallbackInfo {
boolean mAirplaneModeEnabled;
+ @Nullable
CharSequence mDataSubscriptionName;
+ @Nullable
CharSequence mDataContentDescription;
int mMobileSignalIconId;
int mQsTypeIcon;
@@ -540,7 +548,8 @@
}
}
- private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
+ private CharSequence appendMobileDataType(
+ @Nullable CharSequence current, @Nullable CharSequence dataType) {
if (TextUtils.isEmpty(dataType)) {
return Html.fromHtml((current == null ? "" : current.toString()), 0);
}
@@ -551,6 +560,7 @@
return Html.fromHtml(concat, 0);
}
+ @Nullable
private CharSequence getMobileDataContentName(CellularCallbackInfo cb) {
if (cb.mRoaming && !TextUtils.isEmpty(cb.mDataContentDescription)) {
String roaming = mContext.getString(R.string.data_connection_roaming);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 0886b46..a61f0ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -54,6 +54,7 @@
private static final String NFC = "nfc";
private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc);
+ @Nullable
private NfcAdapter mAdapter;
private BroadcastDispatcher mBroadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 9996ecd..b658025 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -131,6 +131,7 @@
return mQRCodeScannerController.isCameraAvailable();
}
+ @Nullable
@Override
public Intent getLongClickIntent() {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index d9919bd..247f02b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -72,8 +72,10 @@
private final SecureSettings mSecureSettings;
private final QuickAccessWalletController mController;
+ @Nullable
private WalletCard mSelectedCard;
private boolean mIsWalletUpdating = true;
+ @Nullable
@VisibleForTesting Drawable mCardViewDrawable;
@Inject
@@ -200,6 +202,7 @@
&& mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT) != null;
}
+ @Nullable
@Override
public Intent getLongClickIntent() {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 8ff75cb..45e43ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -136,6 +136,7 @@
return 0;
}
+ @Nullable
@Override
public Intent getLongClickIntent() {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index b0a1b18..0be0619 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -134,6 +134,7 @@
return new Intent(Settings.ACTION_PRIVACY_SETTINGS);
}
+ @Nullable
@Override
public DetailAdapter getDetailAdapter() {
return super.getDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 6ca550c..076ef35 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -28,6 +28,8 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.util.ArrayUtils;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -50,15 +52,15 @@
this(context, null);
}
- public UserDetailItemView(Context context, AttributeSet attrs) {
+ public UserDetailItemView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
- public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public UserDetailItemView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr,
+ public UserDetailItemView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
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 f793a50..ce6aaae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -77,6 +77,7 @@
private final Context mContext;
protected UserSwitcherController mController;
+ @Nullable
private View mCurrentUserView;
private final UiEventLogger mUiEventLogger;
private final FalsingManager mFalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index e110a64..db1b6e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -48,6 +48,7 @@
private final UserSwitcherController mUserSwitcherController;
private final UserInfoController mUserInfoController;
+ @Nullable
private Pair<String, Drawable> mLastUpdate;
@Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 1608248..c82ff34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -258,6 +258,7 @@
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
}
+ @Nullable
private static String removeDoubleQuotes(String string) {
if (string == null) return null;
final int length = string.length();
@@ -271,11 +272,14 @@
boolean enabled;
boolean connected;
int wifiSignalIconId;
+ @Nullable
String ssid;
boolean activityIn;
boolean activityOut;
+ @Nullable
String wifiSignalContentDescription;
boolean isTransient;
+ @Nullable
public String statusLabel;
@Override
@@ -321,7 +325,9 @@
protected class WifiDetailAdapter implements DetailAdapter,
AccessPointController.AccessPointCallback, QSDetailItems.Callback {
+ @Nullable
private QSDetailItems mItems;
+ @Nullable
private WifiEntry[] mAccessPoints;
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 544246e..4fe155c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -52,6 +52,7 @@
private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
private final InternetDialogController mInternetDialogController;
+ @Nullable
private List<WifiEntry> mWifiEntries;
@VisibleForTesting
protected int mWifiEntriesCount;
@@ -189,6 +190,7 @@
mWifiSummaryText.setText(summary);
}
+ @Nullable
Drawable getWifiDrawable(int level, boolean hasNoInternet) {
// If the Wi-Fi level is equal to WIFI_LEVEL_UNREACHABLE(-1), then a null drawable
// will be returned.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index e7982bf..8e01942 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -42,7 +42,6 @@
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
@@ -98,6 +97,7 @@
private InternetDialogFactory mInternetDialogFactory;
private SubscriptionManager mSubscriptionManager;
private TelephonyManager mTelephonyManager;
+ @Nullable
private AlertDialog mAlertDialog;
private UiEventLogger mUiEventLogger;
private Context mContext;
@@ -130,12 +130,14 @@
private Button mDoneButton;
private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
+ @Nullable
private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mCanConfigMobileData;
// Wi-Fi entries
private int mWifiNetworkHeight;
+ @Nullable
@VisibleForTesting
protected WifiEntry mConnectedWifiEntry;
@VisibleForTesting
@@ -536,6 +538,7 @@
return mInternetDialogController.getDialogTitleText();
}
+ @Nullable
CharSequence getSubtitleText() {
return mInternetDialogController.getSubtitleText(
mIsProgressBarVisible && !mIsSearchingHidden);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 3189d2f..f89b7a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -303,6 +303,7 @@
return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
+ @Nullable
protected Intent getWifiDetailsSettingsIntent(String key) {
if (TextUtils.isEmpty(key)) {
if (DEBUG) {
@@ -320,6 +321,7 @@
return mContext.getText(R.string.quick_settings_internet_label);
}
+ @Nullable
CharSequence getSubtitleText(boolean isProgressBarVisible) {
if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
// When Wi-Fi is disabled.
@@ -391,6 +393,7 @@
return null;
}
+ @Nullable
Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) {
if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f380911..84e21e4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -113,7 +113,8 @@
@Override
public IBinder onBind(@NonNull Intent intent) {
- registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS));
+ registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS),
+ Context.RECEIVER_EXPORTED);
final Messenger m = new Messenger(mHandler);
if (DEBUG_SERVICE) {
Log.d(TAG, "onBind: returning connection: " + m);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3cecbb7..597e424 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -348,11 +348,19 @@
String packageName) { }
/**
- * @see IStatusBar#showTransient(int, int[]).
+ * @see IStatusBar#showTransient(int, int[], boolean).
*/
default void showTransient(int displayId, @InternalInsetsType int[] types) { }
/**
+ * @see IStatusBar#showTransient(int, int[], boolean).
+ */
+ default void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar) {
+ showTransient(displayId, types);
+ }
+
+ /**
* @see IStatusBar#abortTransient(int, int[]).
*/
default void abortTransient(int displayId, @InternalInsetsType int[] types) { }
@@ -1038,9 +1046,10 @@
}
@Override
- public void showTransient(int displayId, int[] types) {
+ public void showTransient(int displayId, int[] types, boolean isGestureOnSystemBar) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, 0, types).sendToTarget();
+ mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, isGestureOnSystemBar ? 1 : 0,
+ types).sendToTarget();
}
}
@@ -1444,8 +1453,9 @@
case MSG_SHOW_TRANSIENT: {
final int displayId = msg.arg1;
final int[] types = (int[]) msg.obj;
+ final boolean isGestureOnSystemBar = msg.arg2 != 0;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showTransient(displayId, types);
+ mCallbacks.get(i).showTransient(displayId, types, isGestureOnSystemBar);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
index 4272bb1..66591b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
@@ -74,9 +74,12 @@
* Returns a string representing the, old, new, and new-after-modification disable flag states,
* as well as the differences between each of the states.
*
- * Example:
- * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (hB.iGR) | New after local modification:
- * EnaihBcRso.qInGR (.n)
+ * Example if [old], [new], and [newAfterLocalModification] are all different:
+ * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (changed: hB.iGR) | New after local
+ * modification: EnaihBcRso.qInGR (changed: .n)
+ *
+ * Example if [old] and [new] are the same:
+ * EnaihBcRso.qiNGR (unchanged)
*
* A capital character signifies the flag is set and a lowercase character signifies that the
* flag isn't set. The flag states will be logged in the same order as the passed-in lists.
@@ -96,54 +99,51 @@
new: DisableState,
newAfterLocalModification: DisableState? = null
): String {
- val builder = StringBuilder("Received new disable state. ")
+ val builder = StringBuilder("Received new disable state: ")
- old?.let {
+ // This if/else has slightly repetitive code but is easier to read.
+ if (old != null && old != new) {
builder.append("Old: ")
builder.append(getFlagsString(old))
builder.append(" | ")
- }
-
- builder.append("New: ")
- if (old != null && old != new) {
- builder.append(getFlagsStringWithDiff(old, new))
- } else {
+ builder.append("New: ")
builder.append(getFlagsString(new))
+ builder.append(" ")
+ builder.append(getDiffString(old, new))
+ } else if (old != null && old == new) {
+ // If old and new are the same, we only need to print one of them.
+ builder.append(getFlagsString(new))
+ builder.append(" ")
+ builder.append(getDiffString(old, new))
+ } else { // old == null
+ builder.append(getFlagsString(new))
+ // Don't get a diff string because we have no [old] to compare with.
}
if (newAfterLocalModification != null && new != newAfterLocalModification) {
builder.append(" | New after local modification: ")
- builder.append(getFlagsStringWithDiff(new, newAfterLocalModification))
+ builder.append(getFlagsString(newAfterLocalModification))
+ builder.append(" ")
+ builder.append(getDiffString(new, newAfterLocalModification))
}
return builder.toString()
}
/**
- * Returns a string representing [new] state, as well as the difference from [old] to [new]
- * (if there is one).
- */
- private fun getFlagsStringWithDiff(old: DisableState, new: DisableState): String {
- val builder = StringBuilder()
- builder.append(getFlagsString(new))
- builder.append(" ")
- builder.append(getDiffString(old, new))
- return builder.toString()
- }
-
- /**
- * Returns a string representing the difference between [old] and [new], or an empty string if
- * there is no difference.
+ * Returns a string representing the difference between [old] and [new].
*
- * For example, if old was "abc.DE" and new was "aBC.De", the difference returned would be
- * "(BC.e)".
+ * - If [old] was "abc.DE" and [new] was "aBC.De", the difference returned would be
+ * "(changed: BC.e)".
+ * - If [old] and [new] are the same, the difference returned would be "(unchanged)".
*/
private fun getDiffString(old: DisableState, new: DisableState): String {
if (old == new) {
- return ""
+ return "(unchanged)"
}
val builder = StringBuilder("(")
+ builder.append("changed: ")
disable1FlagsList.forEach {
val newSymbol = it.getFlagStatus(new.disable1)
if (it.getFlagStatus(old.disable1) != newSymbol) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 8ce73d7..cd4b745 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -133,7 +133,7 @@
private final DockManager mDockManager;
private final DevicePolicyManager mDevicePolicyManager;
private final UserManager mUserManager;
- private final @Main DelayableExecutor mExecutor;
+ protected final @Main DelayableExecutor mExecutor;
private final LockPatternUtils mLockPatternUtils;
private final IActivityManager mIActivityManager;
private final FalsingManager mFalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 491a175..648e14c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -8,12 +8,13 @@
import android.content.res.Configuration
import android.os.SystemClock
import android.util.DisplayMetrics
+import android.util.IndentingPrintWriter
import android.util.MathUtils
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import androidx.annotation.VisibleForTesting
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent
+import com.android.systemui.Dumpable
import com.android.systemui.ExpandHelper
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
@@ -22,23 +23,26 @@
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.media.MediaHierarchyManager
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
+import java.io.FileDescriptor
+import java.io.PrintWriter
import javax.inject.Inject
private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375L
@@ -51,19 +55,19 @@
@SysUISingleton
class LockscreenShadeTransitionController @Inject constructor(
private val statusBarStateController: SysuiStatusBarStateController,
- private val lockscreenGestureLogger: LockscreenGestureLogger,
+ private val logger: LSShadeTransitionLogger,
private val keyguardBypassController: KeyguardBypassController,
private val lockScreenUserManager: NotificationLockscreenUserManager,
private val falsingCollector: FalsingCollector,
private val ambientState: AmbientState,
- private val displayMetrics: DisplayMetrics,
private val mediaHierarchyManager: MediaHierarchyManager,
private val scrimController: ScrimController,
private val depthController: NotificationShadeDepthController,
private val context: Context,
configurationController: ConfigurationController,
- falsingManager: FalsingManager
-) {
+ falsingManager: FalsingManager,
+ dumpManager: DumpManager,
+) : Dumpable {
private var pulseHeight: Float = 0f
private var useSplitShade: Boolean = false
private lateinit var nsslController: NotificationStackScrollLayoutController
@@ -139,6 +143,23 @@
touchHelper.updateResources(context)
}
})
+ dumpManager.registerDumpable(this)
+ statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
+ override fun onExpandedChanged(isExpanded: Boolean) {
+ // safeguard: When the panel is fully collapsed, let's make sure to reset.
+ // See b/198098523
+ if (!isExpanded) {
+ if (dragDownAmount != 0f && dragDownAnimator?.isRunning != true) {
+ logger.logDragDownAmountResetWhenFullyCollapsed()
+ dragDownAmount = 0f
+ }
+ if (pulseHeight != 0f && pulseHeightAnimator?.isRunning != true) {
+ logger.logPulseHeightNotResetWhenFullyCollapsed()
+ setPulseHeight(0f, animate = false)
+ }
+ }
+ }
+ })
}
private fun updateResources() {
@@ -182,19 +203,19 @@
*/
internal fun onDraggedDown(startingChild: View?, dragLengthY: Int) {
if (canDragDown()) {
+ val cancelRunnable = Runnable {
+ logger.logGoingToLockedShadeAborted()
+ setDragDownAmountAnimated(0f)
+ }
if (nsslController.isInLockedDownShade()) {
+ logger.logDraggedDownLockDownShade(startingChild)
statusBarStateController.setLeaveOpenOnKeyguardHide(true)
statusbar.dismissKeyguardThenExecute(OnDismissAction {
nextHideKeyguardNeedsNoAnimation = true
false
- },
- null /* cancelRunnable */, false /* afterKeyguardGone */)
+ }, cancelRunnable, false /* afterKeyguardGone */)
} else {
- lockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_SHADE,
- (dragLengthY / displayMetrics.density).toInt(),
- 0 /* velocityDp */)
- lockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
+ logger.logDraggedDown(startingChild, dragLengthY)
if (!ambientState.isDozing() || startingChild != null) {
// go to locked shade while animating the drag down amount from its current
// value
@@ -216,11 +237,11 @@
dragDownAmount = 0f
forceApplyAmount = false
}
- val cancelRunnable = Runnable { setDragDownAmountAnimated(0f) }
goToLockedShadeInternal(startingChild, animationHandler, cancelRunnable)
}
}
} else {
+ logger.logUnSuccessfulDragDown(startingChild)
setDragDownAmountAnimated(0f)
}
}
@@ -229,6 +250,7 @@
* Called by the touch helper when the drag down was aborted and should be reset.
*/
internal fun onDragDownReset() {
+ logger.logDragDownAborted()
nsslController.setDimmed(true /* dimmed */, true /* animated */)
nsslController.resetScrollPosition()
nsslController.resetCheckSnoozeLeavebehind()
@@ -246,10 +268,16 @@
/**
* Called by the touch helper when the drag down was started
*/
- internal fun onDragDownStarted() {
+ internal fun onDragDownStarted(startingChild: ExpandableView?) {
+ logger.logDragDownStarted(startingChild)
nsslController.cancelLongPress()
nsslController.checkSnoozeLeavebehind()
- dragDownAnimator?.cancel()
+ dragDownAnimator?.apply {
+ if (isRunning) {
+ logger.logAnimationCancelled(isPulse = false)
+ cancel()
+ }
+ }
}
/**
@@ -294,12 +322,12 @@
set(value) {
if (field != value || forceApplyAmount) {
field = value
- if (!nsslController.isInLockedDownShade() || forceApplyAmount) {
+ if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
nsslController.setTransitionToFullShadeAmount(field)
notificationPanelController.setTransitionToFullShadeAmount(field,
false /* animate */, 0 /* delay */)
- dragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
- qS.setTransitionToFullShadeAmount(field, dragProgress)
+ qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
+ qS.setTransitionToFullShadeAmount(field, qSDragProgress)
// TODO: appear media also in split shade
val mediaAmount = if (useSplitShade) 0f else field
mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
@@ -308,7 +336,10 @@
}
}
- var dragProgress = 0f
+ /**
+ * The drag progress of the quick settings drag down amount
+ */
+ var qSDragProgress = 0f
private set
private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
@@ -325,6 +356,7 @@
delay: Long = 0,
endlistener: (() -> Unit)? = null
) {
+ logger.logDragDownAnimation(target)
val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target)
dragDownAnimator.interpolator = Interpolators.FAST_OUT_SLOW_IN
dragDownAnimator.duration = SPRING_BACK_ANIMATION_LENGTH_MS
@@ -380,7 +412,9 @@
*/
@JvmOverloads
fun goToLockedShade(expandedView: View?, needsQSAnimation: Boolean = true) {
- if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ val isKeyguard = statusBarStateController.state == StatusBarState.KEYGUARD
+ logger.logTryGoToLockedShade(isKeyguard)
+ if (isKeyguard) {
val animationHandler: ((Long) -> Unit)?
if (needsQSAnimation) {
// Let's use the default animation
@@ -416,6 +450,7 @@
) {
if (statusbar.isShadeDisabled) {
cancelAction?.run()
+ logger.logShadeDisabledOnGoToLockedShade()
return
}
var userId: Int = lockScreenUserManager.getCurrentUserId()
@@ -454,9 +489,11 @@
}
cancelAction?.run()
}
+ logger.logShowBouncerOnGoToLockedShade()
statusbar.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
draggedDownEntry = entry
} else {
+ logger.logGoingToLockedShade(animationHandler != null)
statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
// This call needs to be after updating the shade state since otherwise
// the scrimstate resets too early
@@ -476,6 +513,7 @@
* @param previousState which state were we in when we hid the keyguard?
*/
fun onHideKeyguard(delay: Long, previousState: Int) {
+ logger.logOnHideKeyguard()
if (animationHandlerOnKeyguardDismiss != null) {
animationHandlerOnKeyguardDismiss!!.invoke(delay)
animationHandlerOnKeyguardDismiss = null
@@ -498,6 +536,7 @@
* not triggered by gestures, e.g. when clicking on the shelf or expand button.
*/
private fun performDefaultGoToFullShadeAnimation(delay: Long) {
+ logger.logDefaultGoToFullShadeAnimation(delay)
notificationPanelController.animateToFullShade(delay)
animateAppear(delay)
}
@@ -534,6 +573,7 @@
* @param cancelled was the interaction cancelled and this is a reset?
*/
fun finishPulseAnimation(cancelled: Boolean) {
+ logger.logPulseExpansionFinished(cancelled)
if (cancelled) {
setPulseHeight(0f, animate = true)
} else {
@@ -546,7 +586,28 @@
* Notify this class that a pulse expansion is starting
*/
fun onPulseExpansionStarted() {
- pulseHeightAnimator?.cancel()
+ logger.logPulseExpansionStarted()
+ pulseHeightAnimator?.apply {
+ if (isRunning) {
+ logger.logAnimationCancelled(isPulse = true)
+ cancel()
+ }
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ IndentingPrintWriter(pw, " ").let {
+ it.println("LSShadeTransitionController:")
+ it.increaseIndent()
+ it.println("pulseHeight: $pulseHeight")
+ it.println("useSplitShade: $useSplitShade")
+ it.println("dragDownAmount: $dragDownAmount")
+ it.println("qSDragProgress: $qSDragProgress")
+ it.println("isDragDownAnywhereEnabled: $isDragDownAnywhereEnabled")
+ it.println("isFalsingCheckNeeded: $isFalsingCheckNeeded")
+ it.println("hasPendingHandlerOnKeyguardDismiss: " +
+ "${animationHandlerOnKeyguardDismiss != null}")
+ }
}
}
@@ -626,7 +687,7 @@
captureStartingChild(initialTouchX, initialTouchY)
initialTouchY = y
initialTouchX = x
- dragDownCallback.onDragDownStarted()
+ dragDownCallback.onDragDownStarted(startingChild)
dragDownAmountOnStart = dragDownCallback.dragDownAmount
return startingChild != null || dragDownCallback.isDragDownAnywhereEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 761a203..ea51bd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -24,15 +24,18 @@
import android.os.PowerManager
import android.os.PowerManager.WAKE_REASON_GESTURE
import android.os.SystemClock
+import android.util.IndentingPrintWriter
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.ViewConfiguration
+import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
@@ -43,6 +46,8 @@
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
+import java.io.FileDescriptor
+import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.max
@@ -61,8 +66,9 @@
private val statusBarStateController: StatusBarStateController,
private val falsingManager: FalsingManager,
private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
- private val falsingCollector: FalsingCollector
-) : Gefingerpoken {
+ private val falsingCollector: FalsingCollector,
+ dumpManager: DumpManager
+) : Gefingerpoken, Dumpable {
companion object {
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
}
@@ -120,6 +126,7 @@
}
})
mPowerManager = context.getSystemService(PowerManager::class.java)
+ dumpManager.registerDumpable(this)
}
private fun initResources(context: Context) {
@@ -329,4 +336,17 @@
fun onStartedWakingUp() {
isWakingToShadeLocked = false
}
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ IndentingPrintWriter(pw, " ").let {
+ it.println("PulseExpansionHandler:")
+ it.increaseIndent()
+ it.println("isExpanding: $isExpanding")
+ it.println("leavingLockscreen: $leavingLockscreen")
+ it.println("mPulsing: $mPulsing")
+ it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
+ it.println("qsExpanded: $qsExpanded")
+ it.println("bouncerShowing: $bouncerShowing")
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 4717b3a..09c608d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -626,7 +626,6 @@
entry = new NotificationEntry(
notification,
ranking,
- mFgsFeatureController.isForegroundServiceDismissalEnabled(),
SystemClock.uptimeMillis());
mAllNotifications.add(entry);
mLeakDetector.trackInstance(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index cd8897e..bd9383d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
@@ -37,6 +38,7 @@
/**
* Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config.
*/
+@SysUISingleton
class NotificationSectionsFeatureManager @Inject constructor(
val proxy: DeviceConfigProxy,
val context: Context
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index b6b9c3f..bf81ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -56,10 +56,12 @@
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Pair;
+import android.util.Slog;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
@@ -240,6 +242,10 @@
Assert.isMainThread();
checkForReentrantCall();
+ // TODO (b/206842750): This method is called from (silent) clear all and non-clear all
+ // contexts and should be checking the NO_CLEAR flag, rather than depending on NSSL
+ // to pass in a properly filtered list of notifications
+
final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>();
for (int i = 0; i < entriesToDismiss.size(); i++) {
NotificationEntry entry = entriesToDismiss.get(i).first;
@@ -742,12 +748,13 @@
*
* See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code.
*/
- private static boolean shouldAutoDismissChildren(
+ @VisibleForTesting
+ static boolean shouldAutoDismissChildren(
NotificationEntry entry,
String dismissedGroupKey) {
return entry.getSbn().getGroupKey().equals(dismissedGroupKey)
&& !entry.getSbn().getNotification().isGroupSummary()
- && !hasFlag(entry, Notification.FLAG_FOREGROUND_SERVICE)
+ && !hasFlag(entry, Notification.FLAG_ONGOING_EVENT)
&& !hasFlag(entry, Notification.FLAG_BUBBLE)
&& entry.getDismissState() != DISMISSED;
}
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 d56938a..f22acb7 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
@@ -174,7 +174,6 @@
private boolean mAutoHeadsUp;
private boolean mPulseSupressed;
- private boolean mAllowFgsDismissal;
private int mBucket = BUCKET_ALERTING;
@Nullable private Long mPendingAnimationDuration;
private boolean mIsMarkedForUserTriggeredMovement;
@@ -192,14 +191,6 @@
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@NonNull Ranking ranking,
- long creationTime) {
- this(sbn, ranking, false, creationTime);
- }
-
- public NotificationEntry(
- @NonNull StatusBarNotification sbn,
- @NonNull Ranking ranking,
- boolean allowFgsDismissal,
long creationTime
) {
super(requireNonNull(requireNonNull(sbn).getKey()), creationTime);
@@ -209,8 +200,6 @@
mKey = sbn.getKey();
setSbn(sbn);
setRanking(ranking);
-
- mAllowFgsDismissal = allowFgsDismissal;
}
@Override
@@ -743,13 +732,11 @@
/**
* @return Can the underlying notification be cleared? This can be different from whether the
* notification can be dismissed in case notifications are sensitive on the lockscreen.
- * @see #canViewBeDismissed()
*/
- // TOOD: This logic doesn't belong on NotificationEntry. It should be moved to the
- // ForegroundsServiceDismissalFeatureController or some other controller that can be added
- // as a dependency to any class that needs to answer this question.
+ // TODO: This logic doesn't belong on NotificationEntry. It should be moved to a controller
+ // that can be added as a dependency to any class that needs to answer this question.
public boolean isClearable() {
- if (!isDismissable()) {
+ if (!mSbn.isClearable()) {
return false;
}
@@ -757,7 +744,7 @@
if (children != null && children.size() > 0) {
for (int i = 0; i < children.size(); i++) {
NotificationEntry child = children.get(i);
- if (!child.isDismissable()) {
+ if (!child.getSbn().isClearable()) {
return false;
}
}
@@ -766,28 +753,25 @@
}
/**
- * Notifications might have any combination of flags:
- * - FLAG_ONGOING_EVENT
- * - FLAG_NO_CLEAR
- * - FLAG_FOREGROUND_SERVICE
- *
- * We want to allow dismissal of notifications that represent foreground services, which may
- * have all 3 flags set. If we only find NO_CLEAR though, we don't want to allow dismissal
+ * @return Can the underlying notification be individually dismissed?
+ * @see #canViewBeDismissed()
*/
- private boolean isDismissable() {
- boolean ongoing = ((mSbn.getNotification().flags & Notification.FLAG_ONGOING_EVENT) != 0);
- boolean noclear = ((mSbn.getNotification().flags & Notification.FLAG_NO_CLEAR) != 0);
- boolean fgs = ((mSbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0);
-
- if (mAllowFgsDismissal) {
- if (noclear && !ongoing && !fgs) {
- return false;
- }
- return true;
- } else {
- return mSbn.isClearable();
+ // TODO: This logic doesn't belong on NotificationEntry. It should be moved to a controller
+ // that can be added as a dependency to any class that needs to answer this question.
+ public boolean isDismissable() {
+ if (mSbn.isOngoing()) {
+ return false;
}
-
+ List<NotificationEntry> children = getAttachedNotifChildren();
+ if (children != null && children.size() > 0) {
+ for (int i = 0; i < children.size(); i++) {
+ NotificationEntry child = children.get(i);
+ if (child.getSbn().isOngoing()) {
+ return false;
+ }
+ }
+ }
+ return true;
}
public boolean canViewBeDismissed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index f50038c..3bd91b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -111,7 +111,7 @@
String group = entry.getSbn().getGroup();
if (mNotifCollection.isOnlyChildInGroup(entry)) {
NotificationEntry summary = mNotifCollection.getGroupSummary(group);
- if (summary != null && summary.isClearable()) return summary;
+ if (summary != null && summary.isDismissable()) return summary;
}
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
index 3b114bb..8daf8be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
@@ -111,7 +111,7 @@
public NotificationEntry getGroupSummaryToDismiss(NotificationEntry entry) {
if (mGroupMembershipManager.isOnlyChildInGroup(entry)) {
NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(entry);
- return groupSummary.isClearable() ? groupSummary : null;
+ return groupSummary.isDismissable() ? groupSummary : null;
}
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
new file mode 100644
index 0000000..f949af0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
+import javax.inject.Inject
+
+@SysUISingleton
+class MediaContainerController @Inject constructor(
+ private val layoutInflater: LayoutInflater
+) : NodeController {
+
+ override val nodeLabel = "MediaContainer"
+ var mediaContainerView: MediaContainerView? = null
+ private set
+
+ fun reinflateView(parent: ViewGroup) {
+ var oldPos = -1
+ mediaContainerView?.let { _view ->
+ _view.removeFromTransientContainer()
+ if (_view.parent === parent) {
+ oldPos = parent.indexOfChild(_view)
+ parent.removeView(_view)
+ }
+ }
+ val inflated = layoutInflater.inflate(
+ R.layout.keyguard_media_container,
+ parent,
+ false /* attachToRoot */)
+ as MediaContainerView
+ if (oldPos != -1) {
+ parent.addView(inflated, oldPos)
+ }
+ mediaContainerView = inflated
+ }
+
+ override val view: View
+ get() = mediaContainerView!!
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index f59e4ab..f13470e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.render
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,6 +33,8 @@
* need to present in the shade, notably the section headers.
*/
class NodeSpecBuilder(
+ private val mediaContainerController: MediaContainerController,
+ private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val viewBarn: NotifViewBarn
) {
fun buildNodeSpec(
@@ -39,6 +42,13 @@
notifList: List<ListEntry>
): NodeSpec = traceSection("NodeSpecBuilder.buildNodeSpec") {
val root = NodeSpecImpl(null, rootController)
+
+ // The media container should be added as the first child of the root node
+ // TODO: Perhaps the node spec building process should be more of a pipeline of its own?
+ if (sectionsFeatureManager.isMediaControlsEnabled()) {
+ root.children.add(NodeSpecImpl(root, mediaContainerController))
+ }
+
var currentSection: NotifSection? = null
val prevSections = mutableSetOf<NotifSection?>()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
index 8c15647..4e9017e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -60,7 +60,7 @@
override fun reinflateView(parent: ViewGroup) {
var oldPos = -1
_view?.let { _view ->
- _view.transientContainer?.removeView(_view)
+ _view.removeFromTransientContainer()
if (_view.parent === parent) {
oldPos = parent.indexOfChild(_view)
parent.removeView(_view)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 1a8d720..43a75a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.view.View
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -33,13 +34,15 @@
context: Context,
listContainer: NotificationListContainer,
private val stackController: NotifStackController,
+ mediaContainerController: MediaContainerController,
+ featureManager: NotificationSectionsFeatureManager,
logger: ShadeViewDifferLogger,
private val viewBarn: NotifViewBarn
) {
// We pass a shim view here because the listContainer may not actually have a view associated
// with it and the differ never actually cares about the root node's view.
private val rootController = RootNodeController(listContainer, View(context))
- private val specBuilder = NodeSpecBuilder(viewBarn)
+ private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager, viewBarn)
private val viewDiffer = ShadeViewDiffer(rootController, logger)
/** Method for attaching this manager to the pipeline. */
@@ -68,6 +71,8 @@
class ShadeViewManagerFactory @Inject constructor(
private val context: Context,
private val logger: ShadeViewDifferLogger,
+ private val mediaContainerController: MediaContainerController,
+ private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val viewBarn: NotifViewBarn
) {
fun create(listContainer: NotificationListContainer, stackController: NotifStackController) =
@@ -75,6 +80,8 @@
context,
listContainer,
stackController,
+ mediaContainerController,
+ sectionsFeatureManager,
logger,
viewBarn)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index f898470..08a230b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1462,7 +1462,7 @@
public void performDismiss(boolean fromAccessibility) {
Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
dismiss(fromAccessibility);
- if (mEntry.isClearable()) {
+ if (mEntry.isDismissable()) {
if (mOnUserInteractionCallback != null) {
mOnUserInteractionCallback.onDismiss(mEntry, REASON_CANCEL,
mOnUserInteractionCallback.getGroupSummaryToDismiss(mEntry));
@@ -2673,9 +2673,18 @@
/**
* @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
* otherwise some state might not be updated. To request about the general clearability
- * see {@link NotificationEntry#isClearable()}.
+ * see {@link NotificationEntry#isDismissable()}.
*/
public boolean canViewBeDismissed() {
+ return mEntry.isDismissable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ }
+
+ /**
+ * @return Whether this view is allowed to be cleared with clear all. Only valid for visible
+ * notifications as otherwise some state might not be updated. To request about the general
+ * clearability see {@link NotificationEntry#isClearable()}.
+ */
+ public boolean canViewBeCleared() {
return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index e8e6e31..0b6d759 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -32,7 +32,7 @@
import java.io.PrintWriter;
public class FooterView extends StackScrollerDecorView {
- private FooterViewButton mDismissButton;
+ private FooterViewButton mClearAllButton;
private FooterViewButton mManageButton;
private boolean mShowHistory;
@@ -57,16 +57,16 @@
pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
pw.println("manageButton showHistory: " + mShowHistory);
pw.println("manageButton visibility: "
- + DumpUtilsKt.visibilityString(mDismissButton.getVisibility()));
+ + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
pw.println("dismissButton visibility: "
- + DumpUtilsKt.visibilityString(mDismissButton.getVisibility()));
+ + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
});
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mDismissButton = (FooterViewButton) findSecondaryView();
+ mClearAllButton = (FooterViewButton) findSecondaryView();
mManageButton = findViewById(R.id.manage_text);
}
@@ -74,8 +74,8 @@
mManageButton.setOnClickListener(listener);
}
- public void setDismissButtonClickListener(OnClickListener listener) {
- mDismissButton.setOnClickListener(listener);
+ public void setClearAllButtonClickListener(OnClickListener listener) {
+ mClearAllButton.setOnClickListener(listener);
}
public boolean isOnEmptySpace(float touchX, float touchY) {
@@ -106,8 +106,8 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateColors();
- mDismissButton.setText(R.string.clear_all_notifications_text);
- mDismissButton.setContentDescription(
+ mClearAllButton.setText(R.string.clear_all_notifications_text);
+ mClearAllButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
showHistory(mShowHistory);
}
@@ -118,8 +118,8 @@
public void updateColors() {
Resources.Theme theme = mContext.getTheme();
int textColor = getResources().getColor(R.color.notif_pill_text, theme);
- mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
- mDismissButton.setTextColor(textColor);
+ mClearAllButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+ mClearAllButton.setTextColor(textColor);
mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
mManageButton.setTextColor(textColor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index e658468..7dc2e19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -57,7 +57,7 @@
private int mTopPadding;
private boolean mShadeExpanded;
private float mMaxHeadsUpTranslation;
- private boolean mDismissAllInProgress;
+ private boolean mClearAllInProgress;
private int mLayoutMinHeight;
private int mLayoutMaxHeight;
private NotificationShelf mShelf;
@@ -384,12 +384,12 @@
return mMaxHeadsUpTranslation;
}
- public void setDismissAllInProgress(boolean dismissAllInProgress) {
- mDismissAllInProgress = dismissAllInProgress;
+ public void setClearAllInProgress(boolean clearAllInProgress) {
+ mClearAllInProgress = clearAllInProgress;
}
- public boolean isDismissAllInProgress() {
- return mDismissAllInProgress;
+ public boolean isClearAllInProgress() {
+ return mClearAllInProgress;
}
public void setLayoutMinHeight(int layoutMinHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
index 0247a99..c9a0f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
@@ -25,9 +25,9 @@
/**
* Root view to insert Lock screen media controls into the notification stack.
*/
-public class MediaHeaderView extends ExpandableView {
+public class MediaContainerView extends ExpandableView {
- public MediaHeaderView(Context context, AttributeSet attrs) {
+ public MediaContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 464fd06..b589d9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -45,7 +45,7 @@
private ExpandableNotificationRow mTrackedHeadsUp;
private float mAppearFraction;
private boolean mRoundForPulsingViews;
- private boolean mIsDismissAllInProgress;
+ private boolean mIsClearAllInProgress;
private ExpandableView mSwipedView = null;
private ExpandableView mViewBeforeSwipedView = null;
@@ -156,8 +156,8 @@
}
}
- void setDismissAllInProgress(boolean isClearingAll) {
- mIsDismissAllInProgress = isClearingAll;
+ void setClearAllInProgress(boolean isClearingAll) {
+ mIsClearAllInProgress = isClearingAll;
}
private float getRoundnessFraction(ExpandableView view, boolean top) {
@@ -170,8 +170,8 @@
return 1f;
}
if (view instanceof ExpandableNotificationRow
- && ((ExpandableNotificationRow) view).canViewBeDismissed()
- && mIsDismissAllInProgress) {
+ && ((ExpandableNotificationRow) view).canViewBeCleared()
+ && mIsClearAllInProgress) {
return 1.0f;
}
if ((view.isPinned()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 1d90780..b02dc0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -16,17 +16,15 @@
package com.android.systemui.statusbar.notification.stack
import android.annotation.ColorInt
-import android.annotation.LayoutRes
import android.util.Log
-import android.view.LayoutInflater
import android.view.View
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
import com.android.systemui.media.KeyguardMediaController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager
import com.android.systemui.statusbar.notification.dagger.AlertingHeader
@@ -60,6 +58,7 @@
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val logger: NotificationSectionsLogger,
private val notifPipelineFlags: NotifPipelineFlags,
+ private val mediaContainerController: MediaContainerController,
@IncomingHeader private val incomingHeaderController: SectionHeaderController,
@PeopleHeader private val peopleHeaderController: SectionHeaderController,
@AlertingHeader private val alertingHeaderController: SectionHeaderController,
@@ -68,7 +67,7 @@
private val configurationListener = object : ConfigurationController.ConfigurationListener {
override fun onLocaleListChanged() {
- reinflateViews(LayoutInflater.from(parent.context))
+ reinflateViews()
}
}
@@ -91,39 +90,19 @@
val peopleHeaderView: SectionHeaderView?
get() = peopleHeaderController.headerView
- @get:VisibleForTesting
- var mediaControlsView: MediaHeaderView? = null
- private set
+ @VisibleForTesting
+ val mediaControlsView: MediaContainerView?
+ get() = mediaContainerController.mediaContainerView
/** Must be called before use. */
- fun initialize(parent: NotificationStackScrollLayout, layoutInflater: LayoutInflater) {
+ fun initialize(parent: NotificationStackScrollLayout) {
check(!initialized) { "NotificationSectionsManager already initialized" }
initialized = true
this.parent = parent
- reinflateViews(layoutInflater)
+ reinflateViews()
configurationController.addCallback(configurationListener)
}
- private fun <T : ExpandableView> reinflateView(
- view: T?,
- layoutInflater: LayoutInflater,
- @LayoutRes layoutResId: Int
- ): T {
- var oldPos = -1
- view?.let {
- view.transientContainer?.removeView(view)
- if (view.parent === parent) {
- oldPos = parent.indexOfChild(view)
- parent.removeView(view)
- }
- }
- val inflated = layoutInflater.inflate(layoutResId, parent, false) as T
- if (oldPos != -1) {
- parent.addView(inflated, oldPos)
- }
- return inflated
- }
-
fun createSectionsForBuckets(): Array<NotificationSection> =
sectionsFeatureManager.getNotificationBuckets()
.map { NotificationSection(parent, it) }
@@ -132,13 +111,12 @@
/**
* Reinflates the entire notification header, including all decoration views.
*/
- fun reinflateViews(layoutInflater: LayoutInflater) {
+ fun reinflateViews() {
silentHeaderController.reinflateView(parent)
alertingHeaderController.reinflateView(parent)
peopleHeaderController.reinflateView(parent)
incomingHeaderController.reinflateView(parent)
- mediaControlsView =
- reinflateView(mediaControlsView, layoutInflater, R.layout.keyguard_media_header)
+ mediaContainerController.reinflateView(parent)
keyguardMediaController.attachSinglePaneContainer(mediaControlsView)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 943f05f..915a85d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -255,8 +255,8 @@
private boolean mIsCurrentUserSetup;
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
- private boolean mDismissAllInProgress;
- private FooterDismissListener mFooterDismissListener;
+ private boolean mClearAllInProgress;
+ private FooterClearAllListener mFooterClearAllListener;
private boolean mFlingAfterUpEvent;
/**
@@ -439,8 +439,8 @@
private int mQsScrollBoundaryPosition;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final Rect mTmpRect = new Rect();
- private DismissListener mDismissListener;
- private DismissAllAnimationListener mDismissAllAnimationListener;
+ private ClearAllListener mClearAllListener;
+ private ClearAllAnimationListener mClearAllAnimationListener;
private ShadeController mShadeController;
private Consumer<Boolean> mOnStackYChanged;
@@ -574,7 +574,7 @@
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
updateSplitNotificationShade();
- mSectionsManager.initialize(this, LayoutInflater.from(context));
+ mSectionsManager.initialize(this);
mSections = mSectionsManager.createSectionsForBuckets();
mAmbientState = Dependency.get(AmbientState.class);
@@ -665,7 +665,7 @@
inflateFooterView();
inflateEmptyShadeView();
updateFooter();
- mSectionsManager.reinflateViews(LayoutInflater.from(mContext));
+ mSectionsManager.reinflateViews();
}
public void setIsRemoteInputActive(boolean isActive) {
@@ -1207,7 +1207,7 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void clampScrollPosition() {
int scrollRange = getScrollRange();
- if (scrollRange < mOwnScrollY && !mAmbientState.isDismissAllInProgress()) {
+ if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) {
boolean animateStackY = false;
if (scrollRange < getScrollAmountToScrollBoundary()
&& mAnimateStackYForContentHeightChange) {
@@ -1729,7 +1729,7 @@
return;
}
mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
- true /* isDismissAll */);
+ true /* isClearAll */);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2228,7 +2228,7 @@
height += viewHeight;
numShownItems++;
- if (viewHeight > 0 || !(expandableView instanceof MediaHeaderView)) {
+ if (viewHeight > 0 || !(expandableView instanceof MediaContainerView)) {
// Only count the media as a notification if it has a positive height.
numShownNotifs++;
}
@@ -3202,7 +3202,7 @@
ignoreChildren = false;
}
childWasSwipedOut |= isFullySwipedOut(row);
- } else if (child instanceof MediaHeaderView) {
+ } else if (child instanceof MediaContainerView) {
childWasSwipedOut = true;
}
if (!childWasSwipedOut) {
@@ -4071,8 +4071,8 @@
clearTransient();
clearHeadsUpDisappearRunning();
- if (mAmbientState.isDismissAllInProgress()) {
- setDismissAllInProgress(false);
+ if (mAmbientState.isClearAllInProgress()) {
+ setClearAllInProgress(false);
if (mShadeNeedsToClose) {
mShadeNeedsToClose = false;
postDelayed(
@@ -4446,19 +4446,19 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setDismissAllInProgress(boolean dismissAllInProgress) {
- mDismissAllInProgress = dismissAllInProgress;
- mAmbientState.setDismissAllInProgress(dismissAllInProgress);
- mController.getNoticationRoundessManager().setDismissAllInProgress(dismissAllInProgress);
- handleDismissAllClipping();
+ public void setClearAllInProgress(boolean clearAllInProgress) {
+ mClearAllInProgress = clearAllInProgress;
+ mAmbientState.setClearAllInProgress(clearAllInProgress);
+ mController.getNoticationRoundessManager().setClearAllInProgress(clearAllInProgress);
+ handleClearAllClipping();
}
- boolean getDismissAllInProgress() {
- return mDismissAllInProgress;
+ boolean getClearAllInProgress() {
+ return mClearAllInProgress;
}
@ShadeViewRefactor(RefactorComponent.ADAPTER)
- private void handleDismissAllClipping() {
+ private void handleClearAllClipping() {
final int count = getChildCount();
boolean previousChildWillBeDismissed = false;
for (int i = 0; i < count; i++) {
@@ -4466,12 +4466,12 @@
if (child.getVisibility() == GONE) {
continue;
}
- if (mDismissAllInProgress && previousChildWillBeDismissed) {
+ if (mClearAllInProgress && previousChildWillBeDismissed) {
child.setMinClipTopAmount(child.getClipTopAmount());
} else {
child.setMinClipTopAmount(0);
}
- previousChildWillBeDismissed = canChildBeDismissed(child);
+ previousChildWillBeDismissed = canChildBeCleared(child);
}
}
@@ -5022,7 +5022,7 @@
}
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- if (isVisible(row) && includeChildInDismissAll(row, selection)) {
+ if (isVisible(row) && includeChildInClearAll(row, selection)) {
return true;
}
}
@@ -5052,7 +5052,7 @@
if (isChildrenVisible(parent)) {
for (ExpandableNotificationRow child : parent.getAttachedChildren()) {
- if (isVisible(child) && includeChildInDismissAll(child, selection)) {
+ if (isVisible(child) && includeChildInClearAll(child, selection)) {
viewsToHide.add(child);
}
}
@@ -5073,13 +5073,13 @@
continue;
}
ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
- if (includeChildInDismissAll(parent, selection)) {
+ if (includeChildInClearAll(parent, selection)) {
viewsToRemove.add(parent);
}
List<ExpandableNotificationRow> children = parent.getAttachedChildren();
if (isVisible(parent) && children != null) {
for (ExpandableNotificationRow child : children) {
- if (includeChildInDismissAll(parent, selection)) {
+ if (includeChildInClearAll(parent, selection)) {
viewsToRemove.add(child);
}
}
@@ -5090,7 +5090,7 @@
/**
* Collects a list of visible rows, and animates them away in a staggered fashion as if they
- * were dismissed. Notifications are dismissed in the backend via onDismissAllAnimationsEnd.
+ * were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd.
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@VisibleForTesting
@@ -5099,18 +5099,18 @@
final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection);
final ArrayList<ExpandableNotificationRow> rowsToDismissInBackend =
getRowsToDismissInBackend(selection);
- if (mDismissListener != null) {
- mDismissListener.onDismiss(selection);
+ if (mClearAllListener != null) {
+ mClearAllListener.onClearAll(selection);
}
final Runnable dismissInBackend = () -> {
- onDismissAllAnimationsEnd(rowsToDismissInBackend, selection);
+ onClearAllAnimationsEnd(rowsToDismissInBackend, selection);
};
if (viewsToAnimateAway.isEmpty()) {
dismissInBackend.run();
return;
}
// Disable normal animations
- setDismissAllInProgress(true);
+ setClearAllInProgress(true);
mShadeNeedsToClose = closeShade;
// Decrease the delay for every row we animate to give the sense of
@@ -5131,10 +5131,10 @@
}
}
- private boolean includeChildInDismissAll(
+ private boolean includeChildInClearAll(
ExpandableNotificationRow row,
@SelectedRows int selection) {
- return canChildBeDismissed(row) && matchesSelection(row, selection);
+ return canChildBeCleared(row) && matchesSelection(row, selection);
}
/** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
@@ -5150,9 +5150,9 @@
protected void inflateFooterView() {
FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_footer, this, false);
- footerView.setDismissButtonClickListener(v -> {
- if (mFooterDismissListener != null) {
- mFooterDismissListener.onDismiss();
+ footerView.setClearAllButtonClickListener(v -> {
+ if (mFooterClearAllListener != null) {
+ mFooterClearAllListener.onClearAll();
}
clearNotifications(ROWS_ALL, true /* closeShade */);
footerView.setSecondaryVisible(false /* visible */, true /* animate */);
@@ -5389,20 +5389,20 @@
return mCheckForLeavebehind;
}
- void setDismissListener (DismissListener listener) {
- mDismissListener = listener;
+ void setClearAllListener(ClearAllListener listener) {
+ mClearAllListener = listener;
}
- void setDismissAllAnimationListener(DismissAllAnimationListener dismissAllAnimationListener) {
- mDismissAllAnimationListener = dismissAllAnimationListener;
+ void setClearAllAnimationListener(ClearAllAnimationListener clearAllAnimationListener) {
+ mClearAllAnimationListener = clearAllAnimationListener;
}
public void setHighPriorityBeforeSpeedBump(boolean highPriorityBeforeSpeedBump) {
mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump;
}
- void setFooterDismissListener(FooterDismissListener listener) {
- mFooterDismissListener = listener;
+ void setFooterClearAllListener(FooterClearAllListener listener) {
+ mFooterClearAllListener = listener;
}
void setShadeController(ShadeController shadeController) {
@@ -5976,9 +5976,6 @@
static boolean canChildBeDismissed(View v) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- if (row.isBlockingHelperShowingAndTranslationFinished()) {
- return true;
- }
if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
return false;
}
@@ -5990,6 +5987,20 @@
return false;
}
+ static boolean canChildBeCleared(View v) {
+ if (v instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
+ return false;
+ }
+ return row.canViewBeCleared();
+ }
+ if (v instanceof PeopleHubView) {
+ return ((PeopleHubView) v).getCanSwipe();
+ }
+ return false;
+ }
+
// --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
void onEntryUpdated(NotificationEntry entry) {
@@ -6003,11 +6014,11 @@
/**
* Called after the animations for a "clear all notifications" action has ended.
*/
- private void onDismissAllAnimationsEnd(
+ private void onClearAllAnimationsEnd(
List<ExpandableNotificationRow> viewsToRemove,
@SelectedRows int selectedRows) {
- if (mDismissAllAnimationListener != null) {
- mDismissAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
+ if (mClearAllAnimationListener != null) {
+ mClearAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
}
}
@@ -6149,15 +6160,15 @@
/** Only rows where entry.isHighPriority() is false. */
public static final int ROWS_GENTLE = 2;
- interface DismissListener {
- void onDismiss(@SelectedRows int selectedRows);
+ interface ClearAllListener {
+ void onClearAll(@SelectedRows int selectedRows);
}
- interface FooterDismissListener {
- void onDismiss();
+ interface FooterClearAllListener {
+ void onClearAll();
}
- interface DismissAllAnimationListener {
+ interface ClearAllAnimationListener {
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index fc612a9..ff75eef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -28,7 +28,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeCleared;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
import android.content.res.Configuration;
@@ -466,7 +466,7 @@
*/
public void handleChildViewDismissed(View view) {
- if (mView.getDismissAllInProgress()) {
+ if (mView.getClearAllInProgress()) {
return;
}
mView.onSwipeEnd();
@@ -510,7 +510,7 @@
&& (parent.areGutsExposed()
|| mSwipeHelper.getExposedMenuView() == parent
|| (parent.getAttachedChildren().size() == 1
- && parent.getEntry().isClearable()))) {
+ && parent.getEntry().isDismissable()))) {
// In this case the group is expanded and showing the menu for the
// group, further interaction should apply to the group, not any
// child notifications so we use the parent of the child. We also do the
@@ -720,10 +720,10 @@
mView.setController(this);
mView.setTouchHandler(new TouchHandler());
mView.setStatusBar(mStatusBar);
- mView.setDismissAllAnimationListener(this::onAnimationEnd);
- mView.setDismissListener((selection) -> mUiEventLogger.log(
+ mView.setClearAllAnimationListener(this::onAnimationEnd);
+ mView.setClearAllListener((selection) -> mUiEventLogger.log(
NotificationPanelEvent.fromSelection(selection)));
- mView.setFooterDismissListener(() ->
+ mView.setFooterClearAllListener(() ->
mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
@@ -1452,7 +1452,7 @@
}
} else {
for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
- if (canChildBeDismissed(rowToRemove)) {
+ if (canChildBeCleared(rowToRemove)) {
mNotificationEntryManager.performRemoveNotification(
rowToRemove.getEntry().getSbn(),
getDismissedByUserStats(rowToRemove.getEntry()),
@@ -1503,7 +1503,7 @@
* from the keyguard host to the quick settings one.
*/
public int getFullShadeTransitionInset() {
- MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
+ MediaContainerView view = mKeyguardMediaController.getSinglePaneContainer();
if (view == null || view.getHeight() == 0
|| mStatusBarStateController.getState() != KEYGUARD) {
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 2c70a5f..8f0579c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -458,7 +458,7 @@
final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
((FooterView.FooterViewState) viewState).hideContent =
isShelfShowing || noSpaceForFooter
- || (ambientState.isDismissAllInProgress()
+ || (ambientState.isClearAllInProgress()
&& !hasOngoingNotifs(algorithmState));
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
new file mode 100644
index 0000000..4de78f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.util.DisplayMetrics
+import android.view.View
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.LSShadeTransitionLog
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import javax.inject.Inject
+
+private const val TAG = "LockscreenShadeTransitionController"
+
+class LSShadeTransitionLogger @Inject constructor(
+ @LSShadeTransitionLog private val buffer: LogBuffer,
+ private val lockscreenGestureLogger: LockscreenGestureLogger,
+ private val displayMetrics: DisplayMetrics
+) {
+ fun logUnSuccessfulDragDown(startingChild: View?) {
+ val entry = (startingChild as ExpandableNotificationRow?)?.entry
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = entry?.key ?: "no entry"
+ }, {
+ "Tried to drag down but can't drag down on $str1"
+ })
+ }
+
+ fun logDragDownAborted() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "The drag down was reset"
+ })
+ }
+
+ fun logDragDownStarted(startingChild: ExpandableView?) {
+ val entry = (startingChild as ExpandableNotificationRow?)?.entry
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = entry?.key ?: "no entry"
+ }, {
+ "The drag down has started on $str1"
+ })
+ }
+
+ fun logDraggedDownLockDownShade(startingChild: View?) {
+ val entry = (startingChild as ExpandableNotificationRow?)?.entry
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = entry?.key ?: "no entry"
+ }, {
+ "Dragged down in locked down shade on $str1"
+ })
+ }
+
+ fun logDraggedDown(startingChild: View?, dragLengthY: Int) {
+ val entry = (startingChild as ExpandableNotificationRow?)?.entry
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = entry?.key ?: "no entry"
+ }, {
+ "Drag down succeeded on $str1"
+ })
+ // Do logging to event log not just our own buffer
+ lockscreenGestureLogger.write(
+ MetricsEvent.ACTION_LS_SHADE,
+ (dragLengthY / displayMetrics.density).toInt(),
+ 0 /* velocityDp */)
+ lockscreenGestureLogger.log(
+ LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
+ }
+
+ fun logDefaultGoToFullShadeAnimation(delay: Long) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ long1 = delay
+ }, {
+ "Default animation started to full shade with delay $long1"
+ })
+ }
+
+ fun logTryGoToLockedShade(keyguard: Boolean) {
+ buffer.log(TAG, LogLevel.INFO, {
+ bool1 = keyguard
+ }, {
+ "Trying to go to locked shade " + if (bool1) "from keyguard" else "not from keyguard"
+ })
+ }
+
+ fun logShadeDisabledOnGoToLockedShade() {
+ buffer.log(TAG, LogLevel.WARNING, {}, {
+ "The shade was disabled when trying to go to the locked shade"
+ })
+ }
+
+ fun logShowBouncerOnGoToLockedShade() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Showing bouncer when trying to go to the locked shade"
+ })
+ }
+
+ fun logGoingToLockedShade(customAnimationHandler: Boolean) {
+ buffer.log(TAG, LogLevel.INFO, {
+ bool1 = customAnimationHandler
+ }, {
+ "Going to locked shade " + if (customAnimationHandler) "with" else "without" +
+ " a custom handler"
+ })
+ }
+
+ fun logOnHideKeyguard() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Notified that the keyguard is being hidden"
+ })
+ }
+
+ fun logPulseExpansionStarted() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Pulse Expansion has started"
+ })
+ }
+
+ fun logPulseExpansionFinished(cancelled: Boolean) {
+ if (cancelled) {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Pulse Expansion is requested to cancel"
+ })
+ } else {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Pulse Expansion is requested to finish"
+ })
+ }
+ }
+
+ fun logDragDownAnimation(target: Float) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ double1 = target.toDouble()
+ }, {
+ "Drag down amount animating to " + double1
+ })
+ }
+
+ fun logAnimationCancelled(isPulse: Boolean) {
+ if (isPulse) {
+ buffer.log(TAG, LogLevel.DEBUG, {}, {
+ "Pulse animation cancelled"
+ })
+ } else {
+ buffer.log(TAG, LogLevel.DEBUG, {}, {
+ "drag down animation cancelled"
+ })
+ }
+ }
+
+ fun logDragDownAmountResetWhenFullyCollapsed() {
+ buffer.log(TAG, LogLevel.WARNING, {}, {
+ "Drag down amount stuck and reset after shade was fully collapsed"
+ })
+ }
+
+ fun logPulseHeightNotResetWhenFullyCollapsed() {
+ buffer.log(TAG, LogLevel.WARNING, {}, {
+ "Pulse height stuck and reset after shade was fully collapsed"
+ })
+ }
+
+ fun logGoingToLockedShadeAborted() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Going to the Locked Shade has been aborted"
+ })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 03b1627..4d625cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -183,7 +183,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
+import com.android.systemui.statusbar.notification.stack.MediaContainerView;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -1581,7 +1581,7 @@
if (row.isRemoved()) {
continue;
}
- } else if (child instanceof MediaHeaderView) {
+ } else if (child instanceof MediaContainerView) {
if (child.getVisibility() == GONE) {
continue;
}
@@ -2433,7 +2433,7 @@
// mLockscreenShadeTransitionController.getDragProgress change.
// When in lockscreen, getDragProgress indicates the true expanded fraction of QS
float shadeExpandedFraction = mTransitioningToFullShadeProgress > 0
- ? mLockscreenShadeTransitionController.getDragProgress()
+ ? mLockscreenShadeTransitionController.getQSDragProgress()
: getExpandedFraction();
mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
@@ -4766,7 +4766,7 @@
@Override
public float getLockscreenShadeDragProgress() {
- return mLockscreenShadeTransitionController.getDragProgress();
+ return mLockscreenShadeTransitionController.getQSDragProgress();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index 51f2e67..cf9a5db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -490,7 +490,8 @@
}
@Override
- public void showTransient(int displayId, @InternalInsetsType int[] types) {
+ public void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar) {
if (displayId != mDisplayId) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index e2d0bb9..31407b1 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -66,6 +66,8 @@
"android.theme.customization.accent_color";
static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
"android.theme.customization.system_palette";
+ static final String OVERLAY_CATEGORY_THEME_STYLE =
+ "android.theme.customization.theme_style";
static final String OVERLAY_COLOR_SOURCE = "android.theme.customization.color_source";
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c86d77b..fb80551 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -66,6 +66,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.monet.ColorScheme;
+import com.android.systemui.monet.Style;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -121,8 +122,8 @@
private boolean mNeedsOverlayCreation;
// Dominant color extracted from wallpaper, NOT the color used on the overlay
protected int mMainWallpaperColor = Color.TRANSPARENT;
- // Accent color extracted from wallpaper, NOT the color used on the overlay
- protected int mWallpaperAccentColor = Color.TRANSPARENT;
+ // Theme variant: Vibrant, Tonal, Expressive, etc
+ private Style mThemeStyle = Style.TONAL_SPOT;
// Accent colors overlay
private FabricatedOverlay mSecondaryOverlay;
// Neutral system colors overlay
@@ -453,26 +454,20 @@
private void reevaluateSystemTheme(boolean forceReload) {
final WallpaperColors currentColors = mCurrentColors.get(mUserTracker.getUserId());
final int mainColor;
- final int accentCandidate;
if (currentColors == null) {
mainColor = Color.TRANSPARENT;
- accentCandidate = Color.TRANSPARENT;
} else {
mainColor = getNeutralColor(currentColors);
- accentCandidate = getAccentColor(currentColors);
}
- if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate
- && !forceReload) {
+ if (mMainWallpaperColor == mainColor && !forceReload) {
return;
}
-
mMainWallpaperColor = mainColor;
- mWallpaperAccentColor = accentCandidate;
if (mIsMonetEnabled) {
- mSecondaryOverlay = getOverlay(mWallpaperAccentColor, ACCENT);
- mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL);
+ mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
+ mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
mNeedsOverlayCreation = true;
if (DEBUG) {
Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay
@@ -497,11 +492,11 @@
/**
* Given a color candidate, return an overlay definition.
*/
- protected @Nullable FabricatedOverlay getOverlay(int color, int type) {
+ protected @Nullable FabricatedOverlay getOverlay(int color, int type, Style style) {
boolean nightMode = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
- mColorScheme = new ColorScheme(color, nightMode);
+ mColorScheme = new ColorScheme(color, nightMode, style);
List<Integer> colorShades = type == ACCENT
? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors();
String name = type == ACCENT ? "accent" : "neutral";
@@ -537,6 +532,7 @@
currentUser);
if (DEBUG) Log.d(TAG, "updateThemeOverlays. Setting: " + overlayPackageJson);
final Map<String, OverlayIdentifier> categoryToPackage = new ArrayMap<>();
+ Style newStyle = mThemeStyle;
if (!TextUtils.isEmpty(overlayPackageJson)) {
try {
JSONObject object = new JSONObject(overlayPackageJson);
@@ -547,11 +543,25 @@
categoryToPackage.put(category, identifier);
}
}
+
+ try {
+ newStyle = Style.valueOf(
+ object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE));
+ } catch (IllegalArgumentException e) {
+ newStyle = Style.TONAL_SPOT;
+ }
} catch (JSONException e) {
Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
}
}
+ if (mIsMonetEnabled && newStyle != mThemeStyle) {
+ mThemeStyle = newStyle;
+ mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
+ mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
+ mNeedsOverlayCreation = true;
+ }
+
// Let's generate system overlay if the style picker decided to override it.
OverlayIdentifier systemPalette = categoryToPackage.get(OVERLAY_CATEGORY_SYSTEM_PALETTE);
if (mIsMonetEnabled && systemPalette != null && systemPalette.getPackageName() != null) {
@@ -561,9 +571,11 @@
colorString = "#" + colorString;
}
int color = Color.parseColor(colorString);
- mNeutralOverlay = getOverlay(color, NEUTRAL);
+ mNeutralOverlay = getOverlay(color, NEUTRAL, mThemeStyle);
+ mSecondaryOverlay = getOverlay(color, ACCENT, mThemeStyle);
mNeedsOverlayCreation = true;
categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
} catch (Exception e) {
// Color.parseColor doesn't catch any exceptions from the calls it makes
Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName(), e);
@@ -574,30 +586,6 @@
// setting. We need to sanitize the input, otherwise the overlay transaction will
// fail.
categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
- } catch (NumberFormatException e) {
- // This is a package name. All good, let's continue
- }
- }
-
- // Same for accent color.
- OverlayIdentifier accentPalette = categoryToPackage.get(OVERLAY_CATEGORY_ACCENT_COLOR);
- if (mIsMonetEnabled && accentPalette != null && accentPalette.getPackageName() != null) {
- try {
- String colorString = accentPalette.getPackageName().toLowerCase();
- if (!colorString.startsWith("#")) {
- colorString = "#" + colorString;
- }
- int color = Color.parseColor(colorString);
- mSecondaryOverlay = getOverlay(color, ACCENT);
- mNeedsOverlayCreation = true;
- categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
- } catch (Exception e) {
- // Color.parseColor doesn't catch any exceptions from the calls it makes
- Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName(), e);
- }
- } else if (!mIsMonetEnabled && accentPalette != null) {
- try {
- Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16);
categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
} catch (NumberFormatException e) {
// This is a package name. All good, let's continue
@@ -642,7 +630,6 @@
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("mSystemColors=" + mCurrentColors);
pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
- pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor));
pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
pw.println("mNeutralOverlay=" + mNeutralOverlay);
pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
@@ -650,5 +637,6 @@
pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
pw.println("mAcceptColorEvents=" + mAcceptColorEvents);
pw.println("mDeferredThemeEvaluation=" + mDeferredThemeEvaluation);
+ pw.println("mThemeStyle=" + mThemeStyle);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt
new file mode 100644
index 0000000..1f5959e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.annotation.IntDef
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates
+
+/** Reports device fold states for logging purposes. */
+// TODO(b/198305865): Log state changes.
+interface FoldStateLoggingProvider : CallbackController<FoldStateLoggingListener> {
+
+ fun init()
+ fun uninit()
+
+ interface FoldStateLoggingListener {
+ fun onFoldUpdate(foldStateUpdate: FoldStateChange)
+ }
+
+ @IntDef(prefix = ["LOGGED_FOLD_STATE_"], value = [FULLY_OPENED, FULLY_CLOSED, HALF_OPENED])
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class LoggedFoldedStates
+}
+
+data class FoldStateChange(
+ @LoggedFoldedStates val previous: Int,
+ @LoggedFoldedStates val current: Int,
+ val dtMillis: Long
+)
+
+const val FULLY_OPENED = 1
+const val FULLY_CLOSED = 2
+const val HALF_OPENED = 3
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt
new file mode 100644
index 0000000..2683971
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.util.Log
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.util.time.SystemClock
+
+/**
+ * Reports device fold states for logging purposes.
+ *
+ * Wraps the state provided by [FoldStateProvider] to output only [FULLY_OPENED], [FULLY_CLOSED] and
+ * [HALF_OPENED] for logging purposes.
+ *
+ * Note that [HALF_OPENED] state is only emitted after the device angle is stable for some timeout.
+ * Check [FoldStateProvider] impl for it.
+ *
+ * This doesn't log the following transitions:
+ * - [HALF_OPENED] -> [FULLY_OPENED]: not interesting, as there is no transition going on
+ * - [HALF_OPENED] -> [HALF_OPENED]: not meaningful.
+ */
+class FoldStateLoggingProviderImpl(
+ private val foldStateProvider: FoldStateProvider,
+ private val clock: SystemClock
+) : FoldStateLoggingProvider, FoldStateProvider.FoldUpdatesListener {
+
+ private val outputListeners: MutableList<FoldStateLoggingListener> = mutableListOf()
+
+ @LoggedFoldedStates private var lastState: Int? = null
+ private var actionStartMillis: Long? = null
+
+ override fun init() {
+ foldStateProvider.addCallback(this)
+ foldStateProvider.start()
+ }
+
+ override fun uninit() {
+ foldStateProvider.removeCallback(this)
+ foldStateProvider.stop()
+ }
+
+ override fun onHingeAngleUpdate(angle: Float) {}
+
+ override fun onFoldUpdate(@FoldUpdate update: Int) {
+ val now = clock.elapsedRealtime()
+ when (update) {
+ FOLD_UPDATE_START_OPENING -> {
+ lastState = FULLY_CLOSED
+ actionStartMillis = now
+ }
+ FOLD_UPDATE_START_CLOSING -> actionStartMillis = now
+ FOLD_UPDATE_FINISH_HALF_OPEN -> dispatchState(HALF_OPENED)
+ FOLD_UPDATE_FINISH_FULL_OPEN -> dispatchState(FULLY_OPENED)
+ FOLD_UPDATE_FINISH_CLOSED -> dispatchState(FULLY_CLOSED)
+ }
+ }
+
+ private fun dispatchState(@LoggedFoldedStates current: Int) {
+ val now = clock.elapsedRealtime()
+ val previous = lastState
+ val lastActionStart = actionStartMillis
+
+ if (previous != null && previous != current && lastActionStart != null) {
+ val time = now - lastActionStart
+ val foldStateChange = FoldStateChange(previous, current, time)
+ outputListeners.forEach { it.onFoldUpdate(foldStateChange) }
+ if (DEBUG) {
+ Log.d(TAG, "From $previous to $current in $time")
+ }
+ }
+
+ actionStartMillis = null
+ lastState = current
+ }
+
+ override fun addCallback(listener: FoldStateLoggingListener) {
+ outputListeners.add(listener)
+ }
+
+ override fun removeCallback(listener: FoldStateLoggingListener) {
+ outputListeners.remove(listener)
+ }
+}
+
+private const val DEBUG = false
+private const val TAG = "FoldStateLoggingProviderImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index ccde316..07f9c54 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -17,10 +17,11 @@
package com.android.systemui.unfold
import com.android.keyguard.KeyguardUnfoldTransition
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
-import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.util.kotlin.getOrNull
import dagger.BindsInstance
import dagger.Module
import dagger.Provides
@@ -36,15 +37,17 @@
/**
* Creates an injectable [SysUIUnfoldComponent] that provides objects that have been scoped with
- * [@SysUIUnfoldScope]. Since [SysUIUnfoldComponent] depends upon:
+ * [@SysUIUnfoldScope].
+ *
+ * Since [SysUIUnfoldComponent] depends upon:
* * [Optional<UnfoldTransitionProgressProvider>]
* * [Optional<ScopedUnfoldTransitionProgressProvider>]
* * [Optional<NaturalRotationProgressProvider>]
+ *
* no objects will get constructed if these parameters are empty.
*/
@Module(subcomponents = [SysUIUnfoldComponent::class])
class SysUIUnfoldModule {
- constructor() {}
@Provides
@SysUISingleton
@@ -53,12 +56,16 @@
rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
@Named(UNFOLD_STATUS_BAR) scopedProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
factory: SysUIUnfoldComponent.Factory
- ) =
- provider.flatMap { p1 ->
- rotationProvider.flatMap { p2 ->
- scopedProvider.map { p3 -> factory.create(p1, p2, p3) }
- }
+ ): Optional<SysUIUnfoldComponent> {
+ val p1 = provider.getOrNull()
+ val p2 = rotationProvider.getOrNull()
+ val p3 = scopedProvider.getOrNull()
+ return if (p1 == null || p2 == null || p3 == null) {
+ Optional.empty()
+ } else {
+ Optional.of(factory.create(p1, p2, p3))
}
+ }
}
@SysUIUnfoldScope
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 75dfd48..f2c1561 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -24,8 +24,10 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.util.time.SystemClockImpl
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
import dagger.Lazy
import dagger.Module
@@ -48,7 +50,7 @@
sensorManager: SensorManager,
@Main executor: Executor,
@Main handler: Handler
- ) =
+ ): Optional<UnfoldTransitionProgressProvider> =
if (config.isEnabled) {
Optional.of(
createUnfoldTransitionProgressProvider(
@@ -59,15 +61,47 @@
sensorManager,
handler,
executor,
- tracingTagPrefix = "systemui"
- )
- )
+ tracingTagPrefix = "systemui"))
} else {
Optional.empty()
}
@Provides
@Singleton
+ fun provideFoldStateProvider(
+ context: Context,
+ config: UnfoldTransitionConfig,
+ screenStatusProvider: Lazy<LifecycleScreenStatusProvider>,
+ deviceStateManager: DeviceStateManager,
+ sensorManager: SensorManager,
+ @Main executor: Executor,
+ @Main handler: Handler
+ ): Optional<FoldStateProvider> =
+ if (!config.isHingeAngleEnabled) {
+ Optional.empty()
+ } else {
+ Optional.of(
+ createFoldStateProvider(
+ context,
+ config,
+ screenStatusProvider.get(),
+ deviceStateManager,
+ sensorManager,
+ handler,
+ executor))
+ }
+
+ @Provides
+ @Singleton
+ fun providesFoldStateLoggingProvider(
+ optionalFoldStateProvider: Optional<FoldStateProvider>
+ ): Optional<FoldStateLoggingProvider> =
+ optionalFoldStateProvider.map { foldStateProvider ->
+ FoldStateLoggingProviderImpl(foldStateProvider, SystemClockImpl())
+ }
+
+ @Provides
+ @Singleton
fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig =
createConfig(context)
@@ -77,13 +111,9 @@
context: Context,
windowManager: IWindowManager,
unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
- ) =
- unfoldTransitionProgressProvider.map {
- provider -> NaturalRotationUnfoldProgressProvider(
- context,
- windowManager,
- provider
- )
+ ): Optional<NaturalRotationUnfoldProgressProvider> =
+ unfoldTransitionProgressProvider.map { provider ->
+ NaturalRotationUnfoldProgressProvider(context, windowManager, provider)
}
@Provides
@@ -91,10 +121,8 @@
@Singleton
fun provideStatusBarScopedTransitionProvider(
source: Optional<NaturalRotationUnfoldProgressProvider>
- ) =
- source.map {
- provider -> ScopedUnfoldTransitionProgressProvider(provider)
- }
+ ): Optional<ScopedUnfoldTransitionProgressProvider> =
+ source.map { provider -> ScopedUnfoldTransitionProgressProvider(provider) }
@Provides
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e8f6de7..5e9579c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -761,7 +761,7 @@
}
static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
- return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
+ return new BubbleEntry(e.getSbn(), e.getRanking(), e.isDismissable(),
e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
e.shouldSuppressPeek());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 8fdcadd..5ad6517 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -34,6 +34,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
@@ -60,6 +61,7 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -75,6 +77,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -89,8 +92,6 @@
@Mock
private Handler mHandler;
@Mock
- private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
- @Mock
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
private MirrorWindowControl mMirrorWindowControl;
@@ -101,6 +102,7 @@
private TestableWindowManager mWindowManager;
private SysUiState mSysUiState = new SysUiState();
private Resources mResources;
+ private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
@@ -125,10 +127,11 @@
return null;
}).when(mHandler).post(
any(Runnable.class));
-
mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
mResources = getContext().getOrCreateTestableResources().getResources();
+ mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+ mContext);
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider,
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
@@ -157,6 +160,52 @@
}
@Test
+ public void enableWindowMagnification_notifySourceBoundsChanged() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
+ /* magnificationFrameOffsetRatioY= */ 0, null);
+ });
+
+ // Waits for the surface created
+ verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged(
+ (eq(mContext.getDisplayId())), any());
+ }
+
+ @Test
+ public void enableWindowMagnification_withAnimation_schedulesFrame() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(2.0f, 10,
+ 10, /* magnificationFrameOffsetRatioX= */ 0,
+ /* magnificationFrameOffsetRatioY= */ 0,
+ Mockito.mock(IRemoteMagnificationAnimationCallback.class));
+ });
+
+ verify(mSfVsyncFrameProvider,
+ timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeast(2)).postFrameCallback(any());
+ }
+
+ @Test
+ public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN, 0, 0, null);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.moveWindowMagnifier(10, 10);
+ });
+
+ final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
+ verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
+ (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+ assertEquals(mWindowMagnificationController.getCenterX(),
+ sourceBoundsCaptor.getValue().exactCenterX(), 0);
+ assertEquals(mWindowMagnificationController.getCenterY(),
+ sourceBoundsCaptor.getValue().exactCenterY(), 0);
+ }
+
+ @Test
public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index c898150..343658d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -149,6 +149,16 @@
}
@Test
+ public void onDrag_enabled_notifyCallback() throws RemoteException {
+ mCommandQueue.requestWindowMagnificationConnection(true);
+ waitForIdleSync();
+
+ mWindowMagnification.onDrag(TEST_DISPLAY);
+
+ verify(mConnectionCallback).onDrag(TEST_DISPLAY);
+ }
+
+ @Test
public void onConfigurationChanged_updateModeSwitches() {
final Configuration config = new Configuration();
config.densityDpi = Configuration.DENSITY_DPI_ANY;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 8cc2776..43d9a75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -25,7 +25,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.UniqueObjectHostView
@@ -57,7 +57,7 @@
@JvmField @Rule
val mockito = MockitoJUnit.rule()
- private val mediaHeaderView: MediaHeaderView = MediaHeaderView(context, null)
+ private val mediaContainerView: MediaContainerView = MediaContainerView(context, null)
private val hostView = UniqueObjectHostView(context)
private lateinit var keyguardMediaController: KeyguardMediaController
@@ -78,7 +78,7 @@
context,
configurationController
)
- keyguardMediaController.attachSinglePaneContainer(mediaHeaderView)
+ keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
keyguardMediaController.useSplitShade = false
}
@@ -88,7 +88,7 @@
keyguardMediaController.refreshMediaPosition()
- assertThat(mediaHeaderView.visibility).isEqualTo(GONE)
+ assertThat(mediaContainerView.visibility).isEqualTo(GONE)
}
@Test
@@ -102,7 +102,7 @@
private fun testStateVisibility(state: Int, visibility: Int) {
whenever(statusBarStateController.state).thenReturn(state)
keyguardMediaController.refreshMediaPosition()
- assertThat(mediaHeaderView.visibility).isEqualTo(visibility)
+ assertThat(mediaContainerView.visibility).isEqualTo(visibility)
}
@Test
@@ -112,7 +112,7 @@
keyguardMediaController.refreshMediaPosition()
- assertThat(mediaHeaderView.visibility).isEqualTo(GONE)
+ assertThat(mediaContainerView.visibility).isEqualTo(GONE)
}
@Test
@@ -130,7 +130,7 @@
keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
assertThat(splitShadeContainer.visibility).isEqualTo(GONE)
- assertThat(mediaHeaderView.visibility).isEqualTo(VISIBLE)
+ assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
}
@Test
@@ -149,6 +149,6 @@
keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
assertTrue("HostView wasn't attached to the single pane container",
- mediaHeaderView.childCount == 1)
+ mediaContainerView.childCount == 1)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index 8cd7d94..5a06048 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -100,4 +100,34 @@
Cam cam = Cam.fromInt(tertiaryMid);
Assert.assertEquals(cam.getHue(), 50.0, 10.0);
}
+
+ @Test
+ public void testSpritz() {
+ int colorInt = 0xffB3588A; // H350 C50 T50
+ ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+ Style.SPRITZ /* style */);
+ int primaryMid = colorScheme.getAccent1().get(colorScheme.getAccent1().size() / 2);
+ Cam cam = Cam.fromInt(primaryMid);
+ Assert.assertEquals(cam.getChroma(), 4.0, 1.0);
+ }
+
+ @Test
+ public void testVibrant() {
+ int colorInt = 0xffB3588A; // H350 C50 T50
+ ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+ Style.VIBRANT /* style */);
+ int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+ Cam cam = Cam.fromInt(neutralMid);
+ Assert.assertEquals(cam.getChroma(), 8.0, 1.0);
+ }
+
+ @Test
+ public void testExpressive() {
+ int colorInt = 0xffB3588A; // H350 C50 T50
+ ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+ Style.EXPRESSIVE /* style */);
+ int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+ Cam cam = Cam.fromInt(neutralMid);
+ Assert.assertEquals(cam.getChroma(), 16.0, 1.0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 2e1fb07..8ccf559 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -218,7 +218,8 @@
String expected = "TestableQSPanelControllerBase:\n"
+ " Tile records:\n"
+ " " + mockTileString + "\n"
- + " " + mockTileViewString + "\n";
+ + " " + mockTileViewString + "\n"
+ + " media bounds: null\n";
assertEquals(expected, w.getBuffer().toString());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 5de4c11..6c29ecc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -151,17 +151,17 @@
@Test
public void testShowTransient() {
int[] types = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
- mCommandQueue.showTransient(DEFAULT_DISPLAY, types);
+ mCommandQueue.showTransient(DEFAULT_DISPLAY, types, true /* isGestureOnSystemBar */);
waitForIdleSync();
- verify(mCallbacks).showTransient(eq(DEFAULT_DISPLAY), eq(types));
+ verify(mCallbacks).showTransient(eq(DEFAULT_DISPLAY), eq(types), eq(true));
}
@Test
public void testShowTransientForSecondaryDisplay() {
int[] types = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
- mCommandQueue.showTransient(SECONDARY_DISPLAY, types);
+ mCommandQueue.showTransient(SECONDARY_DISPLAY, types, true /* isGestureOnSystemBar */);
waitForIdleSync();
- verify(mCallbacks).showTransient(eq(SECONDARY_DISPLAY), eq(types));
+ verify(mCallbacks).showTransient(eq(SECONDARY_DISPLAY), eq(types), eq(true));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
index 38ad6b8..c0d1155 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
@@ -37,7 +37,7 @@
private val disableFlagsLogger = DisableFlagsLogger(disable1Flags, disable2Flags)
@Test
- fun getDisableFlagsString_oldAndNewSame_statesLoggedButDiffsNotLogged() {
+ fun getDisableFlagsString_oldAndNewSame_newAndUnchangedLoggedOldNotLogged() {
val state = DisableFlagsLogger.DisableState(
0b111, // ABC
0b01 // mN
@@ -45,10 +45,9 @@
val result = disableFlagsLogger.getDisableFlagsString(state, state)
- assertThat(result).contains("Old: ABC.mN")
- assertThat(result).contains("New: ABC.mN")
- assertThat(result).doesNotContain("(")
- assertThat(result).doesNotContain(")")
+ assertThat(result).doesNotContain("Old")
+ assertThat(result).contains("ABC.mN")
+ assertThat(result).contains("(unchanged)")
}
@Test
@@ -66,7 +65,7 @@
assertThat(result).contains("Old: ABC.mN")
assertThat(result).contains("New: abC.Mn")
- assertThat(result).contains("(ab.Mn)")
+ assertThat(result).contains("(changed: ab.Mn)")
}
@Test
@@ -82,7 +81,7 @@
)
)
- assertThat(result).contains("(.n)")
+ assertThat(result).contains("(changed: .n)")
}
@Test
@@ -96,8 +95,7 @@
)
assertThat(result).doesNotContain("Old")
- assertThat(result).contains("New: abC.mN")
- // We have no state to diff on, so we shouldn't see any diff in parentheses
+ assertThat(result).contains("abC.mN")
assertThat(result).doesNotContain("(")
assertThat(result).doesNotContain(")")
}
@@ -141,7 +139,7 @@
)
)
- assertThat(result).contains("local modification: Abc.Mn (A.M)")
+ assertThat(result).contains("local modification: Abc.Mn (changed: A.M)")
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 89435ae..13b8e81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -9,6 +9,8 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer
import com.android.systemui.media.MediaHierarchyManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
@@ -18,7 +20,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.phone.StatusBar
@@ -56,7 +58,8 @@
lateinit var transitionController: LockscreenShadeTransitionController
lateinit var row: ExpandableNotificationRow
@Mock lateinit var statusbarStateController: SysuiStatusBarStateController
- @Mock lateinit var lockscreenGestureLogger: LockscreenGestureLogger
+ @Mock lateinit var logger: LSShadeTransitionLogger
+ @Mock lateinit var dumpManager: DumpManager
@Mock lateinit var keyguardBypassController: KeyguardBypassController
@Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@Mock lateinit var falsingCollector: FalsingCollector
@@ -66,6 +69,7 @@
@Mock lateinit var scrimController: ScrimController
@Mock lateinit var configurationController: ConfigurationController
@Mock lateinit var falsingManager: FalsingManager
+ @Mock lateinit var buffer: LogBuffer
@Mock lateinit var notificationPanelController: NotificationPanelViewController
@Mock lateinit var nsslController: NotificationStackScrollLayoutController
@Mock lateinit var depthController: NotificationShadeDepthController
@@ -86,18 +90,18 @@
.addOverride(R.bool.config_use_split_notification_shade, false)
transitionController = LockscreenShadeTransitionController(
statusBarStateController = statusbarStateController,
- lockscreenGestureLogger = lockscreenGestureLogger,
+ logger = logger,
keyguardBypassController = keyguardBypassController,
lockScreenUserManager = lockScreenUserManager,
falsingCollector = falsingCollector,
ambientState = ambientState,
- displayMetrics = displayMetrics,
mediaHierarchyManager = mediaHierarchyManager,
scrimController = scrimController,
depthController = depthController,
context = context,
configurationController = configurationController,
- falsingManager = falsingManager
+ falsingManager = falsingManager,
+ dumpManager = dumpManager
)
whenever(nsslController.view).thenReturn(stackscroller)
whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 41163bf..a7f8b6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar.notification.collection;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
@@ -797,7 +799,7 @@
}
@Test
- public void testDismissingSummaryDoesNotDismissForegroundServiceChildren() {
+ public void testDismissingSummaryDoesDismissForegroundServiceChildren() {
// GIVEN a collection with three grouped notifs in it
CollectionEvent notif0 = postNotif(
buildNotif(TEST_PACKAGE, 0)
@@ -814,7 +816,31 @@
// WHEN the summary is dismissed
mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
- // THEN the foreground service child is not dismissed
+ // THEN the foreground service child is dismissed
+ assertEquals(DISMISSED, notif0.entry.getDismissState());
+ assertEquals(PARENT_DISMISSED, notif1.entry.getDismissState());
+ assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
+ }
+
+ @Test
+ public void testDismissingSummaryDoesNotDismissOngoingChildren() {
+ // GIVEN a collection with three grouped notifs in it
+ CollectionEvent notif0 = postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setGroup(mContext, GROUP_1)
+ .setGroupSummary(mContext, true));
+ CollectionEvent notif1 = postNotif(
+ buildNotif(TEST_PACKAGE, 1)
+ .setGroup(mContext, GROUP_1)
+ .setFlag(mContext, FLAG_ONGOING_EVENT, true));
+ CollectionEvent notif2 = postNotif(
+ buildNotif(TEST_PACKAGE, 2)
+ .setGroup(mContext, GROUP_1));
+
+ // WHEN the summary is dismissed
+ mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
+
+ // THEN the ongoing child is not dismissed
assertEquals(DISMISSED, notif0.entry.getDismissState());
assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
@@ -1427,6 +1453,37 @@
verify(mCollectionListener, never()).onEntryUpdated(any(), anyBoolean());
}
+ @Test
+ public void testCannotDismissOngoingNotificationChildren() {
+ // GIVEN an ongoing notification
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE)
+ .setId(47)
+ .setGroup(mContext, "group")
+ .setFlag(mContext, FLAG_ONGOING_EVENT, true)
+ .build();
+
+ // THEN its children are not dismissible
+ assertFalse(mCollection.shouldAutoDismissChildren(
+ container, container.getSbn().getGroupKey()));
+ }
+
+ @Test
+ public void testCanDismissFgsNotificationChildren() {
+ // GIVEN an FGS but not ongoing notification
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE)
+ .setId(47)
+ .setGroup(mContext, "group")
+ .setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ container.setDismissState(NOT_DISMISSED);
+
+ // THEN its children are dismissible
+ assertTrue(mCollection.shouldAutoDismissChildren(
+ container, container.getSbn().getGroupKey()));
+ }
+
private static NotificationEntryBuilder buildNotif(String pkg, int id, String tag) {
return new NotificationEntryBuilder()
.setPkg(pkg)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index 5271745..f773810 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -18,6 +18,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -31,18 +32,18 @@
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.notification.stack.PriorityBucket
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
-import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
class NodeSpecBuilderTest : SysuiTestCase() {
- @Mock
- private lateinit var viewBarn: NotifViewBarn
+ private val mediaContainerController: MediaContainerController = mock()
+ private val sectionsFeatureManager: NotificationSectionsFeatureManager = mock()
+ private val viewBarn: NotifViewBarn = mock()
private var rootController: NodeController = buildFakeController("rootController")
private var headerController0: NodeController = buildFakeController("header0")
@@ -66,13 +67,12 @@
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- `when`(viewBarn.requireNodeController(any())).thenAnswer {
+ whenever(mediaContainerController.mediaContainerView).thenReturn(mock())
+ whenever(viewBarn.requireNodeController(any())).thenAnswer {
fakeViewBarn.getViewByEntry(it.getArgument(0))
}
- specBuilder = NodeSpecBuilder(viewBarn)
+ specBuilder = NodeSpecBuilder(mediaContainerController, sectionsFeatureManager, viewBarn)
}
@Test
@@ -109,6 +109,30 @@
@Test
fun testSimpleMapping() {
checkOutput(
+ // GIVEN a simple flat list of notifications all in the same headerless section
+ listOf(
+ notif(0, section0NoHeader),
+ notif(1, section0NoHeader),
+ notif(2, section0NoHeader),
+ notif(3, section0NoHeader)
+ ),
+
+ // THEN we output a similarly simple flag list of nodes
+ tree(
+ notifNode(0),
+ notifNode(1),
+ notifNode(2),
+ notifNode(3)
+ )
+ )
+ }
+
+ @Test
+ fun testSimpleMappingWithMedia() {
+ // WHEN media controls are enabled
+ whenever(sectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true)
+
+ checkOutput(
// GIVEN a simple flat list of notifications all in the same headerless section
listOf(
notif(0, section0NoHeader),
@@ -117,8 +141,9 @@
notif(3, section0NoHeader)
),
- // THEN we output a similarly simple flag list of nodes
+ // THEN we output a similarly simple flag list of nodes, with media at the top
tree(
+ node(mediaContainerController),
notifNode(0),
notifNode(1),
notifNode(2),
@@ -333,7 +358,7 @@
private fun buildFakeController(name: String): NodeController {
val controller = Mockito.mock(NodeController::class.java)
- `when`(controller.nodeLabel).thenReturn(name)
+ whenever(controller.nodeLabel).thenReturn(name)
return controller
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index e9e1911..4ea9321 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -16,9 +16,12 @@
package com.android.systemui.statusbar.notification.row;
+import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
import static org.junit.Assert.assertEquals;
@@ -29,6 +32,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -330,4 +334,28 @@
assertTrue(row.getIsNonblockable());
}
+
+ @Test
+ public void testCanDismissNoClear() throws Exception {
+ ExpandableNotificationRow row =
+ mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
+ modifySbn(row.getEntry())
+ .setFlag(mContext, FLAG_NO_CLEAR, true)
+ .build();
+ row.performDismiss(false);
+ verify(mNotificationTestHelper.mOnUserInteractionCallback)
+ .onDismiss(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testCannotDismissOngoing() throws Exception {
+ ExpandableNotificationRow row =
+ mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
+ modifySbn(row.getEntry())
+ .setFlag(mContext, FLAG_ONGOING_EVENT, true)
+ .build();
+ row.performDismiss(false);
+ verify(mNotificationTestHelper.mOnUserInteractionCallback, never())
+ .onDismiss(any(), anyInt(), any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
index 9039e1b..1f92b0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
@@ -57,7 +57,7 @@
@Test
public void setDismissOnClick() {
- mView.setDismissButtonClickListener(mock(View.OnClickListener.class));
+ mView.setClearAllButtonClickListener(mock(View.OnClickListener.class));
assertTrue(mView.findSecondaryView().hasOnClickListeners());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index e4721b1..4457ae0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -115,6 +115,7 @@
private final IconManager mIconManager;
private StatusBarStateController mStatusBarStateController;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+ public final OnUserInteractionCallback mOnUserInteractionCallback;
public NotificationTestHelper(
Context context,
@@ -173,6 +174,7 @@
verify(collection).addCollectionListener(collectionListenerCaptor.capture());
mBindPipelineEntryListener = collectionListenerCaptor.getValue();
mPeopleNotificationIdentifier = mock(PeopleNotificationIdentifier.class);
+ mOnUserInteractionCallback = mock(OnUserInteractionCallback.class);
}
/**
@@ -499,7 +501,7 @@
new FalsingCollectorFake(),
mStatusBarStateController,
mPeopleNotificationIdentifier,
- mock(OnUserInteractionCallback.class),
+ mOnUserInteractionCallback,
Optional.of(mock(BubblesManager.class)),
mock(NotificationGutsManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 4d2c0c3..ac9fcc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -41,7 +41,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -54,6 +53,7 @@
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.MediaContainerController;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -87,6 +87,7 @@
@Mock private NotificationRowComponent mNotificationRowComponent;
@Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@Mock private NotificationSectionsLogger mLogger;
+ @Mock private MediaContainerController mMediaContainerController;
@Mock private SectionHeaderController mIncomingHeaderController;
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@@ -114,6 +115,8 @@
});
when(mNotificationRowComponent.getActivatableNotificationViewController())
.thenReturn(mActivatableNotificationViewController);
+ when(mMediaContainerController.getMediaContainerView())
+ .thenReturn(mock(MediaContainerView.class));
when(mIncomingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
when(mPeopleHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
when(mAlertingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
@@ -126,6 +129,7 @@
mSectionsFeatureManager,
mLogger,
mNotifPipelineFlags,
+ mMediaContainerController,
mIncomingHeaderController,
mPeopleHeaderController,
mAlertingHeaderController,
@@ -134,7 +138,7 @@
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
.thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext));
+ mSectionsManager.initialize(mNssl);
when(mNssl.indexOfChild(any(View.class))).thenReturn(-1);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
@@ -142,7 +146,7 @@
@Test(expected = IllegalStateException.class)
public void testDuplicateInitializeThrows() {
- mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext));
+ mSectionsManager.initialize(mNssl);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 4cc1be6..04c6f6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -379,18 +379,18 @@
@Test
public void testDismissListener() {
- ArgumentCaptor<NotificationStackScrollLayout.DismissListener>
+ ArgumentCaptor<NotificationStackScrollLayout.ClearAllListener>
dismissListenerArgumentCaptor = ArgumentCaptor.forClass(
- NotificationStackScrollLayout.DismissListener.class);
+ NotificationStackScrollLayout.ClearAllListener.class);
mController.attach(mNotificationStackScrollLayout);
- verify(mNotificationStackScrollLayout).setDismissListener(
+ verify(mNotificationStackScrollLayout).setClearAllListener(
dismissListenerArgumentCaptor.capture());
- NotificationStackScrollLayout.DismissListener dismissListener =
+ NotificationStackScrollLayout.ClearAllListener dismissListener =
dismissListenerArgumentCaptor.getValue();
- dismissListener.onDismiss(ROWS_ALL);
+ dismissListener.onClearAll(ROWS_ALL);
verify(mUiEventLogger).log(NotificationPanelEvent.fromSelection(ROWS_ALL));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index eda0e83..46ba097 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -447,7 +447,7 @@
public void testClearNotifications_All() {
final int[] numCalls = {0};
final int[] selected = {-1};
- mStackScroller.setDismissListener(selectedRows -> {
+ mStackScroller.setClearAllListener(selectedRows -> {
numCalls[0]++;
selected[0] = selectedRows;
});
@@ -461,7 +461,7 @@
public void testClearNotifications_Gentle() {
final int[] numCalls = {0};
final int[] selected = {-1};
- mStackScroller.setDismissListener(selectedRows -> {
+ mStackScroller.setClearAllListener(selectedRows -> {
numCalls[0]++;
selected[0] = selectedRows;
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 5ada6d4..35f671bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -455,7 +455,8 @@
mStatusBarStateController,
mFalsingManager,
mLockscreenShadeTransitionController,
- new FalsingCollectorFake());
+ new FalsingCollectorFake(),
+ mDumpManager);
when(mKeyguardStatusViewComponentFactory.build(any()))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 34407b0..f804d83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -39,6 +39,7 @@
import android.content.Intent;
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayIdentifier;
+import android.database.ContentObserver;
import android.graphics.Color;
import android.os.Handler;
import android.os.UserHandle;
@@ -55,6 +56,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.monet.Style;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -117,6 +119,9 @@
private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessLifecycleObserver;
@Captor
private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallback;
+ @Captor
+ private ArgumentCaptor<ContentObserver> mSettingsObserver;
+ private Style mCurrentStyle;
@Before
public void setup() {
@@ -130,10 +135,11 @@
mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
@Nullable
@Override
- protected FabricatedOverlay getOverlay(int color, int type) {
+ protected FabricatedOverlay getOverlay(int color, int type, Style style) {
FabricatedOverlay overlay = mock(FabricatedOverlay.class);
when(overlay.getIdentifier())
.thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
+ mCurrentStyle = style;
return overlay;
}
};
@@ -148,6 +154,10 @@
verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture());
verify(mDumpManager).registerDumpable(any(), any());
verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
+ verify(mSecureSettings).registerContentObserverForUser(
+ eq(Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)),
+ eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL)
+ );
}
@Test
@@ -211,12 +221,6 @@
verify(mThemeOverlayApplier)
.applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
- // Assert that we received the colors that we were expecting
- assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
- .isEqualTo(new OverlayIdentifier("ffff0000"));
- assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
- .isEqualTo(new OverlayIdentifier("ffff0000"));
-
// Should not change theme after changing wallpapers, if intent doesn't have
// WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true.
clearInvocations(mThemeOverlayApplier);
@@ -322,6 +326,40 @@
}
@Test
+ public void onSettingChanged_honorThemeStyle() {
+ when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
+ for (Style style : Style.values()) {
+ reset(mSecureSettings);
+
+ String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.theme_style\":\"" + style.name() + "\"}";
+
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+
+ mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
+
+ assertThat(mCurrentStyle).isEqualTo(style);
+ }
+ }
+
+ @Test
+ public void onSettingChanged_invalidStyle() {
+ when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
+ String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.theme_style\":\"some_invalid_name\"}";
+
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+
+ mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
+
+ assertThat(mCurrentStyle).isEqualTo(Style.TONAL_SPOT);
+ }
+
+ @Test
public void onWallpaperColorsChanged_ResetThemeWithNewHomeAndLockWallpaper() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -611,11 +649,10 @@
mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
@Nullable
@Override
- protected FabricatedOverlay getOverlay(int color, int type) {
+ protected FabricatedOverlay getOverlay(int color, int type, Style style) {
FabricatedOverlay overlay = mock(FabricatedOverlay.class);
when(overlay.getIdentifier())
.thenReturn(new OverlayIdentifier("com.thebest.livewallpaperapp.ever"));
-
return overlay;
}
@@ -648,7 +685,7 @@
mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
@Nullable
@Override
- protected FabricatedOverlay getOverlay(int color, int type) {
+ protected FabricatedOverlay getOverlay(int color, int type, Style style) {
FabricatedOverlay overlay = mock(FabricatedOverlay.class);
when(overlay.getIdentifier())
.thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
new file mode 100644
index 0000000..8076b4e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FoldStateLoggingProviderTest : SysuiTestCase() {
+
+ @Captor
+ private lateinit var foldUpdatesListener: ArgumentCaptor<FoldStateProvider.FoldUpdatesListener>
+
+ @Mock private lateinit var foldStateProvider: FoldStateProvider
+
+ private val fakeClock = FakeSystemClock()
+
+ private lateinit var foldStateLoggingProvider: FoldStateLoggingProvider
+
+ private val foldLoggingUpdates: MutableList<FoldStateChange> = arrayListOf()
+
+ private val foldStateLoggingListener =
+ object : FoldStateLoggingListener {
+ override fun onFoldUpdate(foldStateUpdate: FoldStateChange) {
+ foldLoggingUpdates.add(foldStateUpdate)
+ }
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ foldStateLoggingProvider =
+ FoldStateLoggingProviderImpl(foldStateProvider, fakeClock).apply {
+ addCallback(foldStateLoggingListener)
+ init()
+ }
+
+ verify(foldStateProvider).addCallback(foldUpdatesListener.capture())
+ }
+
+ @Test
+ fun onFoldUpdate_noPreviousOne_finishHalfOpen_nothingReported() {
+ sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+ assertThat(foldLoggingUpdates).isEmpty()
+ }
+
+ @Test
+ fun onFoldUpdate_noPreviousOne_finishFullOpen_nothingReported() {
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ assertThat(foldLoggingUpdates).isEmpty()
+ }
+
+ @Test
+ fun onFoldUpdate_noPreviousOne_finishClosed_nothingReported() {
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ assertThat(foldLoggingUpdates).isEmpty()
+ }
+
+ @Test
+ fun onFoldUpdate_startOpening_fullOpen_changeReported() {
+ val dtTime = 10L
+
+ sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(FoldStateChange(FULLY_CLOSED, FULLY_OPENED, dtTime))
+ }
+
+ @Test
+ fun onFoldUpdate_startClosingThenFinishClosed_noInitialState_nothingReported() {
+ val dtTime = 10L
+
+ sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ assertThat(foldLoggingUpdates).isEmpty()
+ }
+
+ @Test
+ fun onFoldUpdate_startClosingThenFinishClosed_initiallyOpened_changeReported() {
+ val dtTime = 10L
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(FoldStateChange(FULLY_OPENED, FULLY_CLOSED, dtTime))
+ }
+
+ @Test
+ fun onFoldUpdate_startOpeningThenHalf_initiallyClosed_changeReported() {
+ val dtTime = 10L
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(FoldStateChange(FULLY_CLOSED, HALF_OPENED, dtTime))
+ }
+
+ @Test
+ fun onFoldUpdate_startClosingThenHalf_initiallyOpened_changeReported() {
+ val dtTime = 10L
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(FoldStateChange(FULLY_OPENED, HALF_OPENED, dtTime))
+ }
+
+ @Test
+ fun onFoldUpdate_foldThenUnfold_multipleReported() {
+ val foldTime = 24L
+ val unfoldTime = 42L
+ val waitingTime = 424L
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ // Fold
+ sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ fakeClock.advanceTime(foldTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+ fakeClock.advanceTime(waitingTime)
+ // unfold
+ sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+ fakeClock.advanceTime(unfoldTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(
+ FoldStateChange(FULLY_OPENED, FULLY_CLOSED, foldTime),
+ FoldStateChange(FULLY_CLOSED, FULLY_OPENED, unfoldTime))
+ }
+
+ @Test
+ fun uninit_removesCallback() {
+ foldStateLoggingProvider.uninit()
+
+ verify(foldStateProvider).removeCallback(foldUpdatesListener.value)
+ }
+
+ private fun sendFoldUpdate(@FoldUpdate foldUpdate: Int) {
+ foldUpdatesListener.value.onFoldUpdate(foldUpdate)
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index ad3e1d5..e93ac47 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1036,6 +1036,30 @@
}
}
+
+ @Override
+ public Region getCurrentMagnificationRegion(int displayId) {
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getCurrentMagnificationRegion", "displayId=" + displayId);
+ }
+ synchronized (mLock) {
+ final Region region = Region.obtain();
+ if (!hasRightsToCurrentUserLocked()) {
+ return region;
+ }
+ MagnificationProcessor magnificationProcessor =
+ mSystemSupport.getMagnificationProcessor();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ magnificationProcessor.getCurrentMagnificationRegion(displayId,
+ region, mSecurityPolicy.canControlMagnification(this));
+ return region;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
@Override
public float getMagnificationCenterX(int displayId) {
if (svcConnTracingEnabled()) {
@@ -1103,6 +1127,31 @@
}
@Override
+ public boolean resetCurrentMagnification(int displayId, boolean animate) {
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("resetCurrentMagnification",
+ "displayId=" + displayId + ";animate=" + animate);
+ }
+ synchronized (mLock) {
+ if (!hasRightsToCurrentUserLocked()) {
+ return false;
+ }
+ if (!mSecurityPolicy.canControlMagnification(this)) {
+ return false;
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ MagnificationProcessor magnificationProcessor =
+ mSystemSupport.getMagnificationProcessor();
+ return (magnificationProcessor.resetCurrentMagnification(displayId, animate)
+ || !magnificationProcessor.isMagnifying(displayId));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean setMagnificationConfig(int displayId,
@NonNull MagnificationConfig config, boolean animate) {
if (svcConnTracingEnabled()) {
@@ -1542,9 +1591,9 @@
}
public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
mInvocationHandler
- .notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+ .notifyMagnificationChangedLocked(displayId, region, config);
}
public void notifySoftKeyboardShowModeChangedLocked(int showState) {
@@ -1564,15 +1613,15 @@
* state of magnification has changed.
*/
private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
- + scale + ", " + centerX + ", " + centerY);
+ + config.toString());
}
- listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
+ listener.onMagnificationChanged(displayId, region, config);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
}
@@ -1899,11 +1948,9 @@
case MSG_ON_MAGNIFICATION_CHANGED: {
final SomeArgs args = (SomeArgs) message.obj;
final Region region = (Region) args.arg1;
- final float scale = (float) args.arg2;
- final float centerX = (float) args.arg3;
- final float centerY = (float) args.arg4;
+ final MagnificationConfig config = (MagnificationConfig) args.arg2;
final int displayId = args.argi1;
- notifyMagnificationChangedInternal(displayId, region, scale, centerX, centerY);
+ notifyMagnificationChangedInternal(displayId, region, config);
args.recycle();
} break;
@@ -1932,7 +1979,7 @@
}
public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
synchronized (mLock) {
if (mMagnificationCallbackState.get(displayId) == null) {
return;
@@ -1941,9 +1988,7 @@
final SomeArgs args = SomeArgs.obtain();
args.arg1 = region;
- args.arg2 = scale;
- args.arg3 = centerX;
- args.arg4 = centerY;
+ args.arg2 = config;
args.argi1 = displayId;
final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e59a3d6..aa69a09 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -44,6 +44,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.MagnificationConfig;
import android.accessibilityservice.TouchInteractionController;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1301,18 +1302,22 @@
* Called by the MagnificationController when the state of display
* magnification changes.
*
- * @param displayId The logical display id.
+ * <p>
+ * It can notify window magnification change if the service supports controlling all the
+ * magnification mode.
+ * </p>
+ *
+ * @param displayId The logical display id
* @param region the new magnified region, may be empty if
* magnification is not enabled (e.g. scale is 1)
- * @param scale the new scale
- * @param centerX the new screen-relative center X coordinate
- * @param centerY the new screen-relative center Y coordinate
+ * @param config The magnification config. That has magnification mode, the new scale and the
+ * new screen-relative center position
*/
public void notifyMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
synchronized (mLock) {
notifyClearAccessibilityCacheLocked();
- notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+ notifyMagnificationChangedLocked(displayId, region, config);
}
}
@@ -1613,11 +1618,11 @@
}
private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
final AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
- service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+ service.notifyMagnificationChangedLocked(displayId, region, config);
}
}
@@ -2393,6 +2398,7 @@
somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
somethingChanged |= readMagnificationCapabilitiesLocked(userState);
+ somethingChanged |= readMagnificationFollowTypingLocked(userState);
return somethingChanged;
}
@@ -3868,6 +3874,9 @@
private final Uri mMagnificationCapabilityUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY);
+ private final Uri mMagnificationFollowTypingUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
+
public AccessibilityContentObserver(Handler handler) {
super(handler);
}
@@ -3906,6 +3915,8 @@
mMagnificationModeUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mMagnificationCapabilityUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
+ mMagnificationFollowTypingUri, false, this, UserHandle.USER_ALL);
}
@Override
@@ -3973,6 +3984,8 @@
if (readMagnificationCapabilitiesLocked(userState)) {
updateMagnificationCapabilitiesSettingsChangeLocked(userState);
}
+ } else if (mMagnificationFollowTypingUri.equals(uri)) {
+ readMagnificationFollowTypingLocked(userState);
}
}
}
@@ -4069,6 +4082,19 @@
return false;
}
+ boolean readMagnificationFollowTypingLocked(AccessibilityUserState userState) {
+ final boolean followTypeEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+ 1, userState.mUserId) == 1;
+ if (followTypeEnabled != userState.isMagnificationFollowTypingEnabled()) {
+ userState.setMagnificationFollowTypingEnabled(followTypeEnabled);
+ mMagnificationController.setMagnificationFollowTypingEnabled(followTypeEnabled);
+ return true;
+ }
+ return false;
+ }
+
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
mMainHandler.sendMessage(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 9324e3e..0f35456 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -129,6 +129,8 @@
private final SparseIntArray mMagnificationModes = new SparseIntArray();
// The magnification capabilities used to know magnification mode could be switched.
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ // Whether the following typing focus feature for magnification is enabled.
+ private boolean mMagnificationFollowTypingEnabled = true;
/** The stroke width of the focus rectangle in pixels */
private int mFocusStrokeWidth;
@@ -210,6 +212,7 @@
mMagnificationModes.clear();
mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
mFocusColor = mFocusColorDefaultValue;
+ mMagnificationFollowTypingEnabled = true;
}
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -685,6 +688,14 @@
mMagnificationCapabilities = capabilities;
}
+ public void setMagnificationFollowTypingEnabled(boolean enabled) {
+ mMagnificationFollowTypingEnabled = enabled;
+ }
+
+ public boolean isMagnificationFollowTypingEnabled() {
+ return mMagnificationFollowTypingEnabled;
+ }
+
/**
* Sets the magnification mode to the given display.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 72bc850..e39b979 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -17,10 +17,12 @@
package com.android.server.accessibility.magnification;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
+import android.accessibilityservice.MagnificationConfig;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -89,6 +91,8 @@
private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0);
private final Rect mTempRect = new Rect();
+ // Whether the following typing focus feature for magnification is enabled.
+ private boolean mMagnificationFollowTypingEnabled = true;
/**
* This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
@@ -363,9 +367,16 @@
return mIdOfLastServiceToMagnify;
}
+ @GuardedBy("mLock")
void onMagnificationChangedLocked() {
- mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion,
- getScale(), getCenterX(), getCenterY());
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
+ .setScale(getScale())
+ .setCenterX(getCenterX())
+ .setCenterY(getCenterY()).build();
+ mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId,
+ mMagnificationRegion,
+ config);
if (mUnregisterPending && !isMagnifying()) {
unregister(mDeleteAfterUnregister);
}
@@ -735,6 +746,9 @@
public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
int bottom) {
synchronized (mLock) {
+ if (!mMagnificationFollowTypingEnabled) {
+ return;
+ }
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return;
@@ -751,6 +765,10 @@
}
}
+ void setMagnificationFollowTypingEnabled(boolean enabled) {
+ mMagnificationFollowTypingEnabled = enabled;
+ }
+
/**
* Remove the display magnification with given id.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 037dc1f..b70ffb2 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
@@ -379,6 +380,16 @@
mAms.changeMagnificationMode(displayId, magnificationMode);
}
+ @Override
+ public void onSourceBoundsChanged(int displayId, Rect bounds) {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(MAGNIFICATION_MODE_WINDOW)
+ .setScale(mScaleProvider.getScale(displayId))
+ .setCenterX(bounds.exactCenterX())
+ .setCenterY(bounds.exactCenterY()).build();
+ mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
+ }
+
private void disableFullScreenMagnificationIfNeeded(int displayId) {
final FullScreenMagnificationController fullScreenMagnificationController =
getFullScreenMagnificationController();
@@ -426,6 +437,7 @@
synchronized (mLock) {
mImeWindowVisible = shown;
}
+ getWindowMagnificationMgr().onImeWindowVisibilityChanged(shown);
logMagnificationModeWithImeOnIfNeeded();
}
@@ -518,6 +530,16 @@
mMagnificationCapabilities = capabilities;
}
+ /**
+ * Called when the following typing focus feature is switched.
+ *
+ * @param enabled Enable the following typing focus feature
+ */
+ public void setMagnificationFollowTypingEnabled(boolean enabled) {
+ getWindowMagnificationMgr().setMagnificationFollowTypingEnabled(enabled);
+ getFullScreenMagnificationController().setMagnificationFollowTypingEnabled(enabled);
+ }
+
private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
int displayId) {
return mMagnificationEndRunnableSparseArray.get(displayId);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 40f77b0..8f15d5c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -203,6 +203,36 @@
}
/**
+ * Returns the region of the screen currently active for magnification if the
+ * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}.
+ * Returns the region of screen projected on the magnification window if the controlling
+ * magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}.
+ * <p>
+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+ * the returned region will be empty if the magnification is
+ * not active. And the magnification is active if magnification gestures are enabled
+ * or if a service is running that can control magnification.
+ * </p><p>
+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+ * the returned region will be empty if the magnification is not activated.
+ * </p>
+ *
+ * @param displayId The logical display id
+ * @param outRegion the region to populate
+ * @param canControlMagnification Whether the service can control magnification
+ */
+ public void getCurrentMagnificationRegion(int displayId, @NonNull Region outRegion,
+ boolean canControlMagnification) {
+ int currentMode = getControllingMode(displayId);
+ if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) {
+ getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
+ } else if (currentMode == MAGNIFICATION_MODE_WINDOW) {
+ mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+ outRegion);
+ }
+ }
+
+ /**
* Returns the magnification bounds of full-screen magnification on the given display.
*
* @param displayId The logical display id
@@ -224,8 +254,8 @@
}
/**
- * Resets the current magnification on the given display. The reset mode could be
- * full-screen or window if it is activated.
+ * Resets the controlling magnifier on the given display.
+ * For resetting window magnifier, it disables the magnifier by setting the scale to 1.
*
* @param displayId The logical display id.
* @param animate {@code true} to animate the transition, {@code false}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index c4a577d..95c7d44 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -97,6 +97,8 @@
private ConnectionCallback mConnectionCallback;
@GuardedBy("mLock")
private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
+ // Whether the following typing focus feature for magnification is enabled.
+ private boolean mMagnificationFollowTypingEnabled = true;
private boolean mReceiverRegistered = false;
@VisibleForTesting
@@ -139,6 +141,14 @@
void onWindowMagnificationActivationState(int displayId, boolean activated);
/**
+ * Called when the magnification source bounds are changed.
+ *
+ * @param displayId The logical display id.
+ * @param bounds The magnified source bounds on the display.
+ */
+ void onSourceBoundsChanged(int displayId, Rect bounds);
+
+ /**
* Called from {@link IWindowMagnificationConnection} to request changing the magnification
* mode on the given display.
*
@@ -291,13 +301,73 @@
@Override
public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
int bottom) {
- // TODO(b/194668976): We will implement following typing focus in window mode after
- // our refactor.
+ if (!mMagnificationFollowTypingEnabled) {
+ return;
+ }
+
+ float toCenterX = (float) (left + right) / 2;
+ float toCenterY = (float) (top + bottom) / 2;
+
+ if (!isPositionInSourceBounds(displayId, toCenterX, toCenterY)
+ && isTrackingTypingFocusEnabled(displayId)) {
+ enableWindowMagnification(displayId, Float.NaN, toCenterX, toCenterY);
+ }
+ }
+
+ void setMagnificationFollowTypingEnabled(boolean enabled) {
+ mMagnificationFollowTypingEnabled = enabled;
+ }
+
+ /**
+ * Enable or disable tracking typing focus for the specific magnification window.
+ *
+ * The tracking typing focus should be set to enabled with the following conditions:
+ * 1. IME is shown.
+ *
+ * The tracking typing focus should be set to disabled with the following conditions:
+ * 1. A user drags the magnification window by 1 finger.
+ * 2. A user scroll the magnification window by 2 fingers.
+ *
+ * @param displayId The logical display id.
+ * @param trackingTypingFocusEnabled Enabled or disable the function of tracking typing focus.
+ */
+ private void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return;
+ }
+ magnifier.setTrackingTypingFocusEnabled(trackingTypingFocusEnabled);
+ }
+ }
+
+ /**
+ * Enable tracking typing focus function for all magnifications.
+ */
+ private void enableAllTrackingTypingFocus() {
+ synchronized (mLock) {
+ for (int i = 0; i < mWindowMagnifiers.size(); i++) {
+ WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
+ magnifier.setTrackingTypingFocusEnabled(true);
+ }
+ }
+ }
+
+ /**
+ * Called when the IME window visibility changed.
+ *
+ * @param shown {@code true} means the IME window shows on the screen. Otherwise, it's hidden.
+ */
+ void onImeWindowVisibilityChanged(boolean shown) {
+ if (shown) {
+ enableAllTrackingTypingFocus();
+ }
}
@Override
public boolean processScroll(int displayId, float distanceX, float distanceY) {
moveWindowMagnification(displayId, -distanceX, -distanceY);
+ setTrackingTypingFocusEnabled(displayId, false);
return /* event consumed: */ true;
}
@@ -469,6 +539,16 @@
}
}
+ boolean isPositionInSourceBounds(int displayId, float x, float y) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return false;
+ }
+ return magnifier.isPositionInSourceBounds(x, y);
+ }
+ }
+
/**
* Indicates whether window magnification is enabled on specified display.
*
@@ -598,6 +678,16 @@
}
}
+ boolean isTrackingTypingFocusEnabled(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return false;
+ }
+ return magnifier.isTrackingTypingFocusEnabled();
+ }
+ }
+
/**
* Populates magnified bounds on the screen. And the populated magnified bounds would be
* empty If window magnifier is not activated.
@@ -689,6 +779,7 @@
}
magnifier.onSourceBoundsChanged(sourceBounds);
}
+ mCallback.onSourceBoundsChanged(displayId, sourceBounds);
}
@Override
@@ -714,6 +805,17 @@
}
@Override
+ public void onDrag(int displayId) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onDrag",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId);
+ }
+ setTrackingTypingFocusEnabled(displayId, false);
+ }
+
+ @Override
public void binderDied() {
synchronized (mLock) {
Slog.w(TAG, "binderDied DeathRecipient :" + mExpiredDeathRecipient);
@@ -749,7 +851,9 @@
private int mIdOfLastServiceToControl = INVALID_SERVICE_ID;
- private PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
+ private final PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
+
+ private boolean mTrackingTypingFocusEnabled = true;
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
mDisplayId = displayId;
@@ -790,7 +894,6 @@
}
}
- @GuardedBy("mLock")
boolean disableWindowMagnificationInternal(
@Nullable MagnificationAnimationCallback animationResultCallback) {
if (!mEnabled) {
@@ -800,6 +903,7 @@
mDisplayId, animationResultCallback)) {
mEnabled = false;
mIdOfLastServiceToControl = INVALID_SERVICE_ID;
+ mTrackingTypingFocusEnabled = false;
return true;
}
return false;
@@ -848,7 +952,18 @@
return count;
}
- @GuardedBy("mLock")
+ boolean isPositionInSourceBounds(float x, float y) {
+ return mSourceBounds.contains((int) x, (int) y);
+ }
+
+ void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) {
+ mTrackingTypingFocusEnabled = trackingTypingFocusEnabled;
+ }
+
+ boolean isTrackingTypingFocusEnabled() {
+ return mTrackingTypingFocusEnabled;
+ }
+
boolean isEnabled() {
return mEnabled;
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 5aa1c93..c18f5fa 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -19,7 +19,7 @@
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
-import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -1169,7 +1169,7 @@
} else {
scanner.startScan(
filters,
- new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
+ new ScanSettings.Builder().setScanMode(SCAN_MODE_LOW_POWER).build(),
mBleScanCallback);
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 262933d..bc8da84 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -45,7 +45,6 @@
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;
-import android.bluetooth.IBluetoothLeCallControl;
import android.content.ActivityNotFoundException;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
@@ -1324,15 +1323,11 @@
+ bluetoothProfile);
}
- Intent intent;
- if (bluetoothProfile == BluetoothProfile.HEADSET) {
- intent = new Intent(IBluetoothHeadset.class.getName());
- } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
- intent = new Intent(IBluetoothLeCallControl.class.getName());
- } else {
+ if (bluetoothProfile != BluetoothProfile.HEADSET) {
return false;
}
+ Intent intent = new Intent(IBluetoothHeadset.class.getName());
psc = new ProfileServiceConnections(intent);
if (!psc.bindService()) {
return false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d2b4e7..da8c407 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -189,7 +189,6 @@
import android.app.PropertyInvalidatedCache;
import android.app.SyncNotedAppOp;
import android.app.WaitResult;
-import android.app.WtfException;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
@@ -12635,7 +12634,6 @@
int callingUid;
int callingPid;
boolean instantApp;
- boolean throwWtfException = false;
synchronized(this) {
if (caller != null) {
callerApp = getRecordForAppLOSP(caller);
@@ -12730,9 +12728,13 @@
+ "RECEIVER_NOT_EXPORTED be specified when registering a "
+ "receiver");
} else {
- // will be removed when enforcement is required
+ Slog.wtf(TAG,
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
// Assume default behavior-- flag check is not enforced
- throwWtfException = true;
flags |= Context.RECEIVER_EXPORTED;
}
} else if (!requireExplicitFlagForDynamicReceivers) {
@@ -12863,15 +12865,6 @@
}
}
- if (throwWtfException) {
- throw new WtfException(
- callerPackage + ": Targeting T+ (version "
- + Build.VERSION_CODES.TIRAMISU
- + " and above) requires that one of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED be specified when registering a "
- + "receiver");
- }
-
return sticky;
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 2ec4a02..eb8a4e9 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -349,6 +349,11 @@
prr.removeCurReceiver(r);
}
}
+
+ // if something bad happens here, launch the app and try again
+ if (app.isKilled()) {
+ throw new RemoteException("app gets killed during broadcasting");
+ }
}
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index a27e4b77..023a11e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -238,6 +238,9 @@
//------------------------------------------------------------
/*package*/ void dump(PrintWriter pw, String prefix) {
+ pw.println("\n" + prefix + "BECOMING_NOISY_INTENT_DEVICES_SET=");
+ BECOMING_NOISY_INTENT_DEVICES_SET.forEach(device -> {
+ pw.print(" 0x" + Integer.toHexString(device)); });
pw.println("\n" + prefix + "Preferred devices for strategy:");
mPreferredDevices.forEach((strategy, device) -> {
pw.println(" " + prefix + "strategy:" + strategy + " device:" + device); });
@@ -1204,10 +1207,13 @@
state == AudioService.CONNECTION_STATE_CONNECTED
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED);
if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
+ Log.i(TAG, "not sending NOISY: state=" + state);
mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return
return 0;
}
if (!BECOMING_NOISY_INTENT_DEVICES_SET.contains(device)) {
+ Log.i(TAG, "not sending NOISY: device=0x" + Integer.toHexString(device)
+ + " not in set " + BECOMING_NOISY_INTENT_DEVICES_SET);
mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return
return 0;
}
@@ -1217,18 +1223,24 @@
if (((di.mDeviceType & AudioSystem.DEVICE_BIT_IN) == 0)
&& BECOMING_NOISY_INTENT_DEVICES_SET.contains(di.mDeviceType)) {
devices.add(di.mDeviceType);
+ Log.i(TAG, "NOISY: adding 0x" + Integer.toHexString(di.mDeviceType));
}
}
if (musicDevice == AudioSystem.DEVICE_NONE) {
musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
+ Log.i(TAG, "NOISY: musicDevice changing from NONE to 0x"
+ + Integer.toHexString(musicDevice));
}
// always ignore condition on device being actually used for music when in communication
// because music routing is altered in this case.
// also checks whether media routing if affected by a dynamic policy or mirroring
- if (((device == musicDevice) || mDeviceBroker.isInCommunication())
- && AudioSystem.isSingleAudioDeviceType(devices, device)
- && !mDeviceBroker.hasMediaDynamicPolicy()
+ final boolean inCommunication = mDeviceBroker.isInCommunication();
+ final boolean singleAudioDeviceType = AudioSystem.isSingleAudioDeviceType(devices, device);
+ final boolean hasMediaDynamicPolicy = mDeviceBroker.hasMediaDynamicPolicy();
+ if (((device == musicDevice) || inCommunication)
+ && singleAudioDeviceType
+ && !hasMediaDynamicPolicy
&& (musicDevice != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)) {
if (!mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0 /*not looking in past*/)
&& !mDeviceBroker.hasAudioFocusUsers()) {
@@ -1241,6 +1253,12 @@
}
mDeviceBroker.postBroadcastBecomingNoisy();
delay = AudioService.BECOMING_NOISY_DELAY_MS;
+ } else {
+ Log.i(TAG, "not sending NOISY: device:0x" + Integer.toHexString(device)
+ + " musicDevice:0x" + Integer.toHexString(musicDevice)
+ + " inComm:" + inCommunication
+ + " mediaPolicy:" + hasMediaDynamicPolicy
+ + " singleDevice:" + singleAudioDeviceType);
}
mmi.set(MediaMetrics.Property.DELAY_MS, delay).record();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index b73e911..26bbb40 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors;
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -48,7 +50,6 @@
* Interface that ClientMonitor holders should use to receive callbacks.
*/
public interface Callback {
-
/**
* Invoked when the ClientMonitor operation has been started (e.g. reached the head of
* the queue and becomes the current operation).
@@ -203,7 +204,8 @@
}
/** Signals this operation has completed its lifecycle and should no longer be used. */
- void destroy() {
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void destroy() {
mAlreadyDone = true;
if (mToken != null) {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index a358bc2..1f91c4d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -17,15 +17,14 @@
package com.android.server.biometrics.sensors;
import android.annotation.IntDef;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
@@ -55,6 +54,7 @@
* We currently assume (and require) that each biometric sensor have its own instance of a
* {@link BiometricScheduler}. See {@link CoexCoordinator}.
*/
+@MainThread
public class BiometricScheduler {
private static final String BASE_TAG = "BiometricScheduler";
@@ -110,123 +110,6 @@
}
}
- /**
- * Contains all the necessary information for a HAL operation.
- */
- @VisibleForTesting
- static final class Operation {
-
- /**
- * The operation is added to the list of pending operations and waiting for its turn.
- */
- static final int STATE_WAITING_IN_QUEUE = 0;
-
- /**
- * The operation is added to the list of pending operations, but a subsequent operation
- * has been added. This state only applies to {@link Interruptable} operations. When this
- * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
- */
- static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
-
- /**
- * The operation has reached the front of the queue and has started.
- */
- static final int STATE_STARTED = 2;
-
- /**
- * The operation was started, but is now canceling. Operations should wait for the HAL to
- * acknowledge that the operation was canceled, at which point it finishes.
- */
- static final int STATE_STARTED_CANCELING = 3;
-
- /**
- * The operation has reached the head of the queue but is waiting for BiometricService
- * to acknowledge and start the operation.
- */
- static final int STATE_WAITING_FOR_COOKIE = 4;
-
- /**
- * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
- */
- static final int STATE_FINISHED = 5;
-
- @IntDef({STATE_WAITING_IN_QUEUE,
- STATE_WAITING_IN_QUEUE_CANCELING,
- STATE_STARTED,
- STATE_STARTED_CANCELING,
- STATE_WAITING_FOR_COOKIE,
- STATE_FINISHED})
- @Retention(RetentionPolicy.SOURCE)
- @interface OperationState {}
-
- @NonNull final BaseClientMonitor mClientMonitor;
- @Nullable final BaseClientMonitor.Callback mClientCallback;
- @OperationState int mState;
-
- Operation(
- @NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback callback
- ) {
- this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
- }
-
- protected Operation(
- @NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback callback,
- @OperationState int state
- ) {
- mClientMonitor = clientMonitor;
- mClientCallback = callback;
- mState = state;
- }
-
- public boolean isHalOperation() {
- return mClientMonitor instanceof HalClientMonitor<?>;
- }
-
- /**
- * @return true if the operation requires the HAL, and the HAL is null.
- */
- public boolean isUnstartableHalOperation() {
- if (isHalOperation()) {
- final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
- if (client.getFreshDaemon() == null) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- return mClientMonitor + ", State: " + mState;
- }
- }
-
- /**
- * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will
- * kill the current operation and forcibly start the next.
- */
- private static final class CancellationWatchdog implements Runnable {
- static final int DELAY_MS = 3000;
-
- final String tag;
- final Operation operation;
- CancellationWatchdog(String tag, Operation operation) {
- this.tag = tag;
- this.operation = operation;
- }
-
- @Override
- public void run() {
- if (operation.mState != Operation.STATE_FINISHED) {
- Slog.e(tag, "[Watchdog Triggered]: " + operation);
- operation.mClientMonitor.mCallback
- .onClientFinished(operation.mClientMonitor, false /* success */);
- }
- }
- }
-
private static final class CrashState {
static final int NUM_ENTRIES = 10;
final String timestamp;
@@ -263,10 +146,9 @@
private final @SensorType int mSensorType;
@Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@NonNull private final IBiometricService mBiometricService;
- @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper());
- @NonNull private final InternalCallback mInternalCallback;
- @VisibleForTesting @NonNull final Deque<Operation> mPendingOperations;
- @VisibleForTesting @Nullable Operation mCurrentOperation;
+ @NonNull protected final Handler mHandler;
+ @VisibleForTesting @NonNull final Deque<BiometricSchedulerOperation> mPendingOperations;
+ @VisibleForTesting @Nullable BiometricSchedulerOperation mCurrentOperation;
@NonNull private final ArrayDeque<CrashState> mCrashStates;
private int mTotalOperationsHandled;
@@ -277,7 +159,7 @@
// Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as
// starting the next client).
- public class InternalCallback implements BaseClientMonitor.Callback {
+ private final BaseClientMonitor.Callback mInternalCallback = new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
Slog.d(getTag(), "[Started] " + clientMonitor);
@@ -286,16 +168,11 @@
mCoexCoordinator.addAuthenticationClient(mSensorType,
(AuthenticationClient<?>) clientMonitor);
}
-
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
- }
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
mHandler.post(() -> {
- clientMonitor.destroy();
if (mCurrentOperation == null) {
Slog.e(getTag(), "[Finishing] " + clientMonitor
+ " but current operation is null, success: " + success
@@ -303,9 +180,9 @@
return;
}
- if (clientMonitor != mCurrentOperation.mClientMonitor) {
+ if (!mCurrentOperation.isFor(clientMonitor)) {
Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match"
- + " current: " + mCurrentOperation.mClientMonitor);
+ + " current: " + mCurrentOperation);
return;
}
@@ -315,36 +192,33 @@
(AuthenticationClient<?>) clientMonitor);
}
- mCurrentOperation.mState = Operation.STATE_FINISHED;
-
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success);
- }
-
if (mGestureAvailabilityDispatcher != null) {
mGestureAvailabilityDispatcher.markSensorActive(
- mCurrentOperation.mClientMonitor.getSensorId(), false /* active */);
+ mCurrentOperation.getSensorId(), false /* active */);
}
if (mRecentOperations.size() >= mRecentOperationsLimit) {
mRecentOperations.remove(0);
}
- mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum());
+ mRecentOperations.add(mCurrentOperation.getProtoEnum());
mCurrentOperation = null;
mTotalOperationsHandled++;
startNextOperationIfIdle();
});
}
- }
+ };
@VisibleForTesting
- BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ BiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull IBiometricService biometricService, int recentOperationsLimit,
+ @NonNull IBiometricService biometricService,
+ int recentOperationsLimit,
@NonNull CoexCoordinator coexCoordinator) {
mBiometricTag = tag;
+ mHandler = handler;
mSensorType = sensorType;
- mInternalCallback = new InternalCallback();
mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
mPendingOperations = new ArrayDeque<>();
mBiometricService = biometricService;
@@ -356,24 +230,26 @@
/**
* Creates a new scheduler.
+ *
* @param tag for the specific instance of the scheduler. Should be unique.
+ * @param handler handler for callbacks (all methods of this class must be called on the
+ * thread associated with this handler)
* @param sensorType the sensorType that this scheduler is handling.
* @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
* (such as fingerprint swipe).
*/
public BiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
@SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,
- CoexCoordinator.getInstance());
+ this(tag, handler, sensorType, gestureAvailabilityDispatcher,
+ IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+ LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance());
}
- /**
- * @return A reference to the internal callback that should be invoked whenever the scheduler
- * needs to (e.g. client started, client finished).
- */
- @NonNull protected InternalCallback getInternalCallback() {
+ @VisibleForTesting
+ public BaseClientMonitor.Callback getInternalCallback() {
return mInternalCallback;
}
@@ -392,72 +268,46 @@
}
mCurrentOperation = mPendingOperations.poll();
- final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor;
Slog.d(getTag(), "[Polled] " + mCurrentOperation);
// If the operation at the front of the queue has been marked for cancellation, send
// ERROR_CANCELED. No need to start this client.
- if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ if (mCurrentOperation.isMarkedCanceling()) {
Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
- if (!(currentClient instanceof Interruptable)) {
- throw new IllegalStateException("Mis-implemented client or scheduler, "
- + "trying to cancel non-interruptable operation: " + mCurrentOperation);
- }
-
- final Interruptable interruptable = (Interruptable) currentClient;
- interruptable.cancelWithoutStarting(getInternalCallback());
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
// Now we wait for the client to send its FinishCallback, which kicks off the next
// operation.
return;
}
- if (mGestureAvailabilityDispatcher != null
- && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) {
+ if (mGestureAvailabilityDispatcher != null && mCurrentOperation.isAcquisitionOperation()) {
mGestureAvailabilityDispatcher.markSensorActive(
- mCurrentOperation.mClientMonitor.getSensorId(),
- true /* active */);
+ mCurrentOperation.getSensorId(), true /* active */);
}
// Not all operations start immediately. BiometricPrompt waits for its operation
// to arrive at the head of the queue, before pinging it to start.
- final boolean shouldStartNow = currentClient.getCookie() == 0;
- if (shouldStartNow) {
- if (mCurrentOperation.isUnstartableHalOperation()) {
- final HalClientMonitor<?> halClientMonitor =
- (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
+ final int cookie = mCurrentOperation.isReadyToStart();
+ if (cookie == 0) {
+ if (!mCurrentOperation.start(mInternalCallback)) {
// Note down current length of queue
final int pendingOperationsLength = mPendingOperations.size();
- final Operation lastOperation = mPendingOperations.peekLast();
+ final BiometricSchedulerOperation lastOperation = mPendingOperations.peekLast();
Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation
+ ". Last pending operation: " + lastOperation);
- // For current operations, 1) unableToStart, which notifies the caller-side, then
- // 2) notify operation's callback, to notify applicable system service that the
- // operation failed.
- halClientMonitor.unableToStart();
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(
- mCurrentOperation.mClientMonitor, false /* success */);
- }
-
// Then for each operation currently in the pending queue at the time of this
// failure, do the same as above. Otherwise, it's possible that something like
// setActiveUser fails, but then authenticate (for the wrong user) is invoked.
for (int i = 0; i < pendingOperationsLength; i++) {
- final Operation operation = mPendingOperations.pollFirst();
- if (operation == null) {
+ final BiometricSchedulerOperation operation = mPendingOperations.pollFirst();
+ if (operation != null) {
+ Slog.w(getTag(), "[Aborting Operation] " + operation);
+ operation.abort();
+ } else {
Slog.e(getTag(), "Null operation, index: " + i
+ ", expected length: " + pendingOperationsLength);
- break;
}
- if (operation.isHalOperation()) {
- ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart();
- }
- if (operation.mClientCallback != null) {
- operation.mClientCallback.onClientFinished(operation.mClientMonitor,
- false /* success */);
- }
- Slog.w(getTag(), "[Aborted Operation] " + operation);
}
// It's possible that during cleanup a new set of operations came in. We can try to
@@ -465,25 +315,20 @@
// actually be multiple operations (i.e. updateActiveUser + authenticate).
mCurrentOperation = null;
startNextOperationIfIdle();
- } else {
- Slog.d(getTag(), "[Starting] " + mCurrentOperation);
- currentClient.start(getInternalCallback());
- mCurrentOperation.mState = Operation.STATE_STARTED;
}
} else {
try {
- mBiometricService.onReadyForAuthentication(currentClient.getCookie());
+ mBiometricService.onReadyForAuthentication(cookie);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
}
Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
- mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE;
}
}
/**
* Starts the {@link #mCurrentOperation} if
- * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and
+ * 1) its state is {@link BiometricSchedulerOperation#STATE_WAITING_FOR_COOKIE} and
* 2) its cookie matches this cookie
*
* This is currently only used by {@link com.android.server.biometrics.BiometricService}, which
@@ -499,45 +344,13 @@
Slog.e(getTag(), "Current operation is null");
return;
}
- if (mCurrentOperation.mState != Operation.STATE_WAITING_FOR_COOKIE) {
- if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
- Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: "
- + mCurrentOperation);
- // This should trigger the internal onClientFinished callback, which clears the
- // operation and starts the next one.
- final ErrorConsumer errorConsumer =
- (ErrorConsumer) mCurrentOperation.mClientMonitor;
- errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- 0 /* vendorCode */);
- return;
- } else {
- Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
- + ", expected STATE_WAITING_FOR_COOKIE");
- return;
- }
- }
- if (mCurrentOperation.mClientMonitor.getCookie() != cookie) {
- Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
- + ", received: " + cookie);
- return;
- }
- if (mCurrentOperation.isUnstartableHalOperation()) {
+ if (mCurrentOperation.startWithCookie(mInternalCallback, cookie)) {
+ Slog.d(getTag(), "[Started] Prepared client: " + mCurrentOperation);
+ } else {
Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation);
- // This is BiometricPrompt trying to auth but something's wrong with the HAL.
- final HalClientMonitor<?> halClientMonitor =
- (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
- halClientMonitor.unableToStart();
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor,
- false /* success */);
- }
mCurrentOperation = null;
startNextOperationIfIdle();
- } else {
- Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
- mCurrentOperation.mState = Operation.STATE_STARTED;
- mCurrentOperation.mClientMonitor.start(getInternalCallback());
}
}
@@ -562,17 +375,13 @@
// pending clients as canceling. Once they reach the head of the queue, the scheduler will
// send ERROR_CANCELED and skip the operation.
if (clientMonitor.interruptsPrecedingClients()) {
- for (Operation operation : mPendingOperations) {
- if (operation.mClientMonitor instanceof Interruptable
- && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
- Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
- + operation.mClientMonitor);
- operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
- }
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
+ Slog.d(getTag(), "New client, marking pending op as canceling: " + operation);
+ operation.markCanceling();
}
}
- mPendingOperations.add(new Operation(clientMonitor, clientCallback));
+ mPendingOperations.add(new BiometricSchedulerOperation(clientMonitor, clientCallback));
Slog.d(getTag(), "[Added] " + clientMonitor
+ ", new queue size: " + mPendingOperations.size());
@@ -580,67 +389,34 @@
// cancellable, start the cancellation process.
if (clientMonitor.interruptsPrecedingClients()
&& mCurrentOperation != null
- && mCurrentOperation.mClientMonitor instanceof Interruptable
- && mCurrentOperation.mState == Operation.STATE_STARTED) {
+ && mCurrentOperation.isInterruptable()
+ && mCurrentOperation.isStarted()) {
Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
- cancelInternal(mCurrentOperation);
- }
-
- startNextOperationIfIdle();
- }
-
- private void cancelInternal(Operation operation) {
- if (operation != mCurrentOperation) {
- Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation);
- return;
- }
- if (!(operation.mClientMonitor instanceof Interruptable)) {
- Slog.w(getTag(), "Operation not interruptable: " + operation);
- return;
- }
- if (operation.mState == Operation.STATE_STARTED_CANCELING) {
- Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
- return;
- }
- if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
- Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
- // We can set it to null immediately, since the HAL was never notified to start.
- if (mCurrentOperation != null) {
- mCurrentOperation.mClientMonitor.destroy();
- }
- mCurrentOperation = null;
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
+ } else {
startNextOperationIfIdle();
- return;
}
- Slog.d(getTag(), "[Cancelling] Current client: " + operation.mClientMonitor);
- final Interruptable interruptable = (Interruptable) operation.mClientMonitor;
- interruptable.cancel();
- operation.mState = Operation.STATE_STARTED_CANCELING;
-
- // Add a watchdog. If the HAL does not acknowledge within the timeout, we will
- // forcibly finish this client.
- mHandler.postDelayed(new CancellationWatchdog(getTag(), operation),
- CancellationWatchdog.DELAY_MS);
}
/**
* Requests to cancel enrollment.
* @param token from the caller, should match the token passed in when requesting enrollment
*/
- public void cancelEnrollment(IBinder token) {
- if (mCurrentOperation == null) {
- Slog.e(getTag(), "Unable to cancel enrollment, null operation");
- return;
- }
- final boolean isEnrolling = mCurrentOperation.mClientMonitor instanceof EnrollClient;
- final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
- if (!isEnrolling || !tokenMatches) {
- Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling
- + " tokenMatches: " + tokenMatches);
- return;
- }
+ public void cancelEnrollment(IBinder token, long requestId) {
+ Slog.d(getTag(), "cancelEnrollment, requestId: " + requestId);
- cancelInternal(mCurrentOperation);
+ if (mCurrentOperation != null
+ && canCancelEnrollOperation(mCurrentOperation, token, requestId)) {
+ Slog.d(getTag(), "Cancelling enrollment op: " + mCurrentOperation);
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
+ } else {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
+ if (canCancelEnrollOperation(operation, token, requestId)) {
+ Slog.d(getTag(), "Cancelling pending enrollment op: " + operation);
+ operation.markCanceling();
+ }
+ }
+ }
}
/**
@@ -649,62 +425,42 @@
* @param requestId the id returned when requesting authentication
*/
public void cancelAuthenticationOrDetection(IBinder token, long requestId) {
- Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId
- + " current: " + mCurrentOperation
- + " stack size: " + mPendingOperations.size());
+ Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId);
if (mCurrentOperation != null
&& canCancelAuthOperation(mCurrentOperation, token, requestId)) {
- Slog.d(getTag(), "Cancelling: " + mCurrentOperation);
- cancelInternal(mCurrentOperation);
+ Slog.d(getTag(), "Cancelling auth/detect op: " + mCurrentOperation);
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
} else {
- // Look through the current queue for all authentication clients for the specified
- // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking
- // all of them, instead of just the first one, since the API surface currently doesn't
- // allow us to distinguish between multiple authentication requests from the same
- // process. However, this generally does not happen anyway, and would be a class of
- // bugs on its own.
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
if (canCancelAuthOperation(operation, token, requestId)) {
- Slog.d(getTag(), "Marking " + operation
- + " as STATE_WAITING_IN_QUEUE_CANCELING");
- operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ Slog.d(getTag(), "Cancelling pending auth/detect op: " + operation);
+ operation.markCanceling();
}
}
}
}
- private static boolean canCancelAuthOperation(Operation operation, IBinder token,
- long requestId) {
+ private static boolean canCancelEnrollOperation(BiometricSchedulerOperation operation,
+ IBinder token, long requestId) {
+ return operation.isEnrollOperation()
+ && operation.isMatchingToken(token)
+ && operation.isMatchingRequestId(requestId);
+ }
+
+ private static boolean canCancelAuthOperation(BiometricSchedulerOperation operation,
+ IBinder token, long requestId) {
// TODO: restrict callers that can cancel without requestId (negative value)?
- return isAuthenticationOrDetectionOperation(operation)
- && operation.mClientMonitor.getToken() == token
- && isMatchingRequestId(operation, requestId);
- }
-
- // By default, monitors are not associated with a request id to retain the original
- // behavior (i.e. if no requestId is explicitly set then assume it matches)
- private static boolean isMatchingRequestId(Operation operation, long requestId) {
- return !operation.mClientMonitor.hasRequestId()
- || operation.mClientMonitor.getRequestId() == requestId;
- }
-
- private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
- final boolean isAuthentication =
- operation.mClientMonitor instanceof AuthenticationConsumer;
- final boolean isDetection =
- operation.mClientMonitor instanceof DetectionConsumer;
- return isAuthentication || isDetection;
+ return operation.isAuthenticationOrDetectionOperation()
+ && operation.isMatchingToken(token)
+ && operation.isMatchingRequestId(requestId);
}
/**
* @return the current operation
*/
public BaseClientMonitor getCurrentClient() {
- if (mCurrentOperation == null) {
- return null;
- }
- return mCurrentOperation.mClientMonitor;
+ return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null;
}
public int getCurrentPendingCount() {
@@ -719,7 +475,7 @@
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
final String timestamp = dateFormat.format(new Date(System.currentTimeMillis()));
final List<String> pendingOperations = new ArrayList<>();
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
pendingOperations.add(operation.toString());
}
@@ -735,7 +491,7 @@
pw.println("Type: " + mSensorType);
pw.println("Current operation: " + mCurrentOperation);
pw.println("Pending operations: " + mPendingOperations.size());
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
pw.println("Pending operation: " + operation);
}
for (CrashState crashState : mCrashStates) {
@@ -746,7 +502,7 @@
public byte[] dumpProtoState(boolean clearSchedulerBuffer) {
final ProtoOutputStream proto = new ProtoOutputStream();
proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null
- ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE);
+ ? mCurrentOperation.getProtoEnum() : BiometricsProto.CM_NONE);
proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled);
if (!mRecentOperations.isEmpty()) {
@@ -771,6 +527,7 @@
* HAL dies.
*/
public void reset() {
+ Slog.d(getTag(), "Resetting scheduler");
mPendingOperations.clear();
mCurrentOperation = null;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
new file mode 100644
index 0000000..a8cce15
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains all the necessary information for a HAL operation.
+ */
+public class BiometricSchedulerOperation {
+ protected static final String TAG = "BiometricSchedulerOperation";
+
+ /**
+ * The operation is added to the list of pending operations and waiting for its turn.
+ */
+ protected static final int STATE_WAITING_IN_QUEUE = 0;
+
+ /**
+ * The operation is added to the list of pending operations, but a subsequent operation
+ * has been added. This state only applies to {@link Interruptable} operations. When this
+ * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
+ */
+ protected static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
+
+ /**
+ * The operation has reached the front of the queue and has started.
+ */
+ protected static final int STATE_STARTED = 2;
+
+ /**
+ * The operation was started, but is now canceling. Operations should wait for the HAL to
+ * acknowledge that the operation was canceled, at which point it finishes.
+ */
+ protected static final int STATE_STARTED_CANCELING = 3;
+
+ /**
+ * The operation has reached the head of the queue but is waiting for BiometricService
+ * to acknowledge and start the operation.
+ */
+ protected static final int STATE_WAITING_FOR_COOKIE = 4;
+
+ /**
+ * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
+ */
+ protected static final int STATE_FINISHED = 5;
+
+ @IntDef({STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_IN_QUEUE_CANCELING,
+ STATE_STARTED,
+ STATE_STARTED_CANCELING,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_FINISHED})
+ @Retention(RetentionPolicy.SOURCE)
+ protected @interface OperationState {}
+
+ private static final int CANCEL_WATCHDOG_DELAY_MS = 3000;
+
+ @NonNull
+ private final BaseClientMonitor mClientMonitor;
+ @Nullable
+ private final BaseClientMonitor.Callback mClientCallback;
+ @OperationState
+ private int mState;
+ @VisibleForTesting
+ @NonNull
+ final Runnable mCancelWatchdog;
+
+ BiometricSchedulerOperation(
+ @NonNull BaseClientMonitor clientMonitor,
+ @Nullable BaseClientMonitor.Callback callback
+ ) {
+ this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
+ }
+
+ protected BiometricSchedulerOperation(
+ @NonNull BaseClientMonitor clientMonitor,
+ @Nullable BaseClientMonitor.Callback callback,
+ @OperationState int state
+ ) {
+ mClientMonitor = clientMonitor;
+ mClientCallback = callback;
+ mState = state;
+ mCancelWatchdog = () -> {
+ if (!isFinished()) {
+ Slog.e(TAG, "[Watchdog Triggered]: " + this);
+ getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+ }
+ };
+ }
+
+ /**
+ * Zero if this operation is ready to start or has already started. A non-zero cookie
+ * is returned if the operation has not started and is waiting on
+ * {@link android.hardware.biometrics.IBiometricService#onReadyForAuthentication(int)}.
+ *
+ * @return cookie or 0 if ready/started
+ */
+ public int isReadyToStart() {
+ if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) {
+ final int cookie = mClientMonitor.getCookie();
+ if (cookie != 0) {
+ mState = STATE_WAITING_FOR_COOKIE;
+ }
+ return cookie;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Start this operation without waiting for a cookie
+ * (i.e. {@link #isReadyToStart() returns zero}
+ *
+ * @param callback lifecycle callback
+ * @return if this operation started
+ */
+ public boolean start(@NonNull BaseClientMonitor.Callback callback) {
+ checkInState("start",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (mClientMonitor.getCookie() != 0) {
+ throw new IllegalStateException("operation requires cookie");
+ }
+
+ return doStart(callback);
+ }
+
+ /**
+ * Start this operation after receiving the given cookie.
+ *
+ * @param callback lifecycle callback
+ * @param cookie cookie indicting the operation should begin
+ * @return if this operation started
+ */
+ public boolean startWithCookie(@NonNull BaseClientMonitor.Callback callback, int cookie) {
+ checkInState("start",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (mClientMonitor.getCookie() != cookie) {
+ Slog.e(TAG, "Mismatched cookie for operation: " + this + ", received: " + cookie);
+ return false;
+ }
+
+ return doStart(callback);
+ }
+
+ private boolean doStart(@NonNull BaseClientMonitor.Callback callback) {
+ final BaseClientMonitor.Callback cb = getWrappedCallback(callback);
+
+ if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
+ Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this);
+
+ cb.onClientFinished(mClientMonitor, true /* success */);
+ if (mClientMonitor instanceof ErrorConsumer) {
+ final ErrorConsumer errorConsumer = (ErrorConsumer) mClientMonitor;
+ errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ 0 /* vendorCode */);
+ } else {
+ Slog.w(TAG, "monitor cancelled but does not implement ErrorConsumer");
+ }
+
+ return false;
+ }
+
+ if (isUnstartableHalOperation()) {
+ Slog.v(TAG, "unable to start: " + this);
+ ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+ cb.onClientFinished(mClientMonitor, false /* success */);
+ return false;
+ }
+
+ mState = STATE_STARTED;
+ mClientMonitor.start(cb);
+
+ Slog.v(TAG, "started: " + this);
+ return true;
+ }
+
+ /**
+ * Abort a pending operation.
+ *
+ * This is similar to cancel but the operation must not have been started. It will
+ * immediately abort the operation and notify the client that it has finished unsuccessfully.
+ */
+ public void abort() {
+ checkInState("cannot abort a non-pending operation",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (isHalOperation()) {
+ ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+ }
+ getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+
+ Slog.v(TAG, "Aborted: " + this);
+ }
+
+ /** Flags this operation as canceled, but does not cancel it until started. */
+ public void markCanceling() {
+ if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) {
+ mState = STATE_WAITING_IN_QUEUE_CANCELING;
+ Slog.v(TAG, "Marked cancelling: " + this);
+ }
+ }
+
+ /**
+ * Cancel the operation now.
+ *
+ * @param handler handler to use for the cancellation watchdog
+ * @param callback lifecycle callback (only used if this operation hasn't started, otherwise
+ * the callback used from {@link #start(BaseClientMonitor.Callback)} is used)
+ */
+ public void cancel(@NonNull Handler handler, @NonNull BaseClientMonitor.Callback callback) {
+ checkNotInState("cancel", STATE_FINISHED);
+
+ final int currentState = mState;
+ if (!isInterruptable()) {
+ Slog.w(TAG, "Cannot cancel - operation not interruptable: " + this);
+ return;
+ }
+ if (currentState == STATE_STARTED_CANCELING) {
+ Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this);
+ return;
+ }
+
+ mState = STATE_STARTED_CANCELING;
+ if (currentState == STATE_WAITING_IN_QUEUE
+ || currentState == STATE_WAITING_IN_QUEUE_CANCELING
+ || currentState == STATE_WAITING_FOR_COOKIE) {
+ Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor);
+ ((Interruptable) mClientMonitor).cancelWithoutStarting(getWrappedCallback(callback));
+ } else {
+ Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor);
+ ((Interruptable) mClientMonitor).cancel();
+ }
+
+ // forcibly finish this client if the HAL does not acknowledge within the timeout
+ handler.postDelayed(mCancelWatchdog, CANCEL_WATCHDOG_DELAY_MS);
+ }
+
+ @NonNull
+ private BaseClientMonitor.Callback getWrappedCallback() {
+ return getWrappedCallback(null);
+ }
+
+ @NonNull
+ private BaseClientMonitor.Callback getWrappedCallback(
+ @Nullable BaseClientMonitor.Callback callback) {
+ final BaseClientMonitor.Callback destroyCallback = new BaseClientMonitor.Callback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mClientMonitor.destroy();
+ mState = STATE_FINISHED;
+ }
+ };
+ return new BaseClientMonitor.CompositeCallback(destroyCallback, callback, mClientCallback);
+ }
+
+ /** {@link BaseClientMonitor#getSensorId()}. */
+ public int getSensorId() {
+ return mClientMonitor.getSensorId();
+ }
+
+ /** {@link BaseClientMonitor#getProtoEnum()}. */
+ public int getProtoEnum() {
+ return mClientMonitor.getProtoEnum();
+ }
+
+ /** {@link BaseClientMonitor#getTargetUserId()}. */
+ public int getTargetUserId() {
+ return mClientMonitor.getTargetUserId();
+ }
+
+ /** If the given clientMonitor is the same as the one in the constructor. */
+ public boolean isFor(@NonNull BaseClientMonitor clientMonitor) {
+ return mClientMonitor == clientMonitor;
+ }
+
+ /** If this operation is {@link Interruptable}. */
+ public boolean isInterruptable() {
+ return mClientMonitor instanceof Interruptable;
+ }
+
+ private boolean isHalOperation() {
+ return mClientMonitor instanceof HalClientMonitor<?>;
+ }
+
+ private boolean isUnstartableHalOperation() {
+ if (isHalOperation()) {
+ final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
+ if (client.getFreshDaemon() == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** If this operation is an enrollment. */
+ public boolean isEnrollOperation() {
+ return mClientMonitor instanceof EnrollClient;
+ }
+
+ /** If this operation is authentication. */
+ public boolean isAuthenticateOperation() {
+ return mClientMonitor instanceof AuthenticationClient;
+ }
+
+ /** If this operation is authentication or detection. */
+ public boolean isAuthenticationOrDetectionOperation() {
+ final boolean isAuthentication = mClientMonitor instanceof AuthenticationConsumer;
+ final boolean isDetection = mClientMonitor instanceof DetectionConsumer;
+ return isAuthentication || isDetection;
+ }
+
+ /** If this operation performs acquisition {@link AcquisitionClient}. */
+ public boolean isAcquisitionOperation() {
+ return mClientMonitor instanceof AcquisitionClient;
+ }
+
+ /**
+ * If this operation matches the original requestId.
+ *
+ * By default, monitors are not associated with a request id to retain the original
+ * behavior (i.e. if no requestId is explicitly set then assume it matches)
+ *
+ * @param requestId a unique id {@link BaseClientMonitor#setRequestId(long)}.
+ */
+ public boolean isMatchingRequestId(long requestId) {
+ return !mClientMonitor.hasRequestId()
+ || mClientMonitor.getRequestId() == requestId;
+ }
+
+ /** If the token matches */
+ public boolean isMatchingToken(@Nullable IBinder token) {
+ return mClientMonitor.getToken() == token;
+ }
+
+ /** If this operation has started. */
+ public boolean isStarted() {
+ return mState == STATE_STARTED;
+ }
+
+ /** If this operation is cancelling but has not yet completed. */
+ public boolean isCanceling() {
+ return mState == STATE_STARTED_CANCELING;
+ }
+
+ /** If this operation has finished and completed its lifecycle. */
+ public boolean isFinished() {
+ return mState == STATE_FINISHED;
+ }
+
+ /** If {@link #markCanceling()} was called but the operation hasn't been canceled. */
+ public boolean isMarkedCanceling() {
+ return mState == STATE_WAITING_IN_QUEUE_CANCELING;
+ }
+
+ /**
+ * The monitor passed to the constructor.
+ * @deprecated avoid using and move to encapsulate within the operation
+ */
+ @Deprecated
+ public BaseClientMonitor getClientMonitor() {
+ return mClientMonitor;
+ }
+
+ private void checkNotInState(String message, @OperationState int... states) {
+ for (int state : states) {
+ if (mState == state) {
+ throw new IllegalStateException(message + ": illegal state= " + state);
+ }
+ }
+ }
+
+ private void checkInState(String message, @OperationState int... states) {
+ for (int state : states) {
+ if (mState == state) {
+ return;
+ }
+ }
+ throw new IllegalStateException(message + ": illegal state= " + mState);
+ }
+
+ @Override
+ public String toString() {
+ return mClientMonitor + ", State: " + mState;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index fab98b6..d5093c75 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -32,6 +32,11 @@
* {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens
* if the client is still waiting in the pending queue and got notified that a subsequent
* operation is preempting it.
+ *
+ * This method must invoke
+ * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)} on the
+ * given callback (with success).
+ *
* @param callback invoked when the operation is completed.
*/
void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback);
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index b056bf8..19eaa17 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -16,10 +16,13 @@
package com.android.server.biometrics.sensors;
+import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
+import android.os.Handler;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
@@ -68,9 +71,8 @@
return;
}
- Slog.d(getTag(), "[Client finished] "
- + clientMonitor + ", success: " + success);
- if (mCurrentOperation != null && mCurrentOperation.mClientMonitor == mOwner) {
+ Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success);
+ if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
mCurrentOperation = null;
startNextOperationIfIdle();
} else {
@@ -83,26 +85,31 @@
}
@VisibleForTesting
- UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ UserAwareBiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull IBiometricService biometricService,
@NonNull CurrentUserRetriever currentUserRetriever,
@NonNull UserSwitchCallback userSwitchCallback,
@NonNull CoexCoordinator coexCoordinator) {
- super(tag, sensorType, gestureAvailabilityDispatcher, biometricService,
+ super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService,
LOG_NUM_RECENT_OPERATIONS, coexCoordinator);
mCurrentUserRetriever = currentUserRetriever;
mUserSwitchCallback = userSwitchCallback;
}
- public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ public UserAwareBiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull CurrentUserRetriever currentUserRetriever,
@NonNull UserSwitchCallback userSwitchCallback) {
- this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever,
- userSwitchCallback, CoexCoordinator.getInstance());
+ this(tag, handler, sensorType, gestureAvailabilityDispatcher,
+ IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+ currentUserRetriever, userSwitchCallback, CoexCoordinator.getInstance());
}
@Override
@@ -122,7 +129,7 @@
}
final int currentUserId = mCurrentUserRetriever.getCurrentUserId();
- final int nextUserId = mPendingOperations.getFirst().mClientMonitor.getTargetUserId();
+ final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
if (nextUserId == currentUserId) {
super.startNextOperationIfIdle();
@@ -133,8 +140,8 @@
new ClientFinishedCallback(startClient);
Slog.d(getTag(), "[Starting User] " + startClient);
- mCurrentOperation = new Operation(
- startClient, finishedCallback, Operation.STATE_STARTED);
+ mCurrentOperation = new BiometricSchedulerOperation(
+ startClient, finishedCallback, STATE_STARTED);
startClient.start(finishedCallback);
} else {
if (mStopUserClient != null) {
@@ -147,8 +154,8 @@
Slog.d(getTag(), "[Stopping User] current: " + currentUserId
+ ", next: " + nextUserId + ". " + mStopUserClient);
- mCurrentOperation = new Operation(
- mStopUserClient, finishedCallback, Operation.STATE_STARTED);
+ mCurrentOperation = new BiometricSchedulerOperation(
+ mStopUserClient, finishedCallback, STATE_STARTED);
mStopUserClient.start(finishedCallback);
}
}
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 675ee545..039b08e 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
@@ -213,7 +213,7 @@
}
@Override // Binder call
- public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
+ public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
@@ -221,23 +221,24 @@
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
- return;
+ return -1;
}
- provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+ return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
}
@Override // Binder call
- public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
+ public long enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
// TODO(b/145027036): Implement this.
+ return -1;
}
@Override // Binder call
- public void cancelEnrollment(final IBinder token) {
+ public void cancelEnrollment(final IBinder token, long requestId) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -246,7 +247,7 @@
return;
}
- provider.second.cancelEnrollment(provider.first, token);
+ provider.second.cancelEnrollment(provider.first, token, requestId);
}
@Override // Binder call
@@ -624,7 +625,7 @@
private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
mServiceProviders.add(
- new Face10(getContext(), hidlSensor, mLockoutResetDispatcher));
+ Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index e099ba3..77e431c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -94,12 +94,12 @@
void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge);
- void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+ long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
@NonNull int[] disabledFeatures, @Nullable Surface previewSurface,
boolean debugConsent);
- void cancelEnrollment(int sensorId, @NonNull IBinder token);
+ void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index a806277..aae4fbe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -82,13 +82,14 @@
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName,
+ @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser,
boolean debugConsent) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */);
+ setRequestId(requestId);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
mEnrollIgnoreListVendor = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 4bae775..ae507ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -327,17 +327,18 @@
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
@Nullable Surface previewSurface, boolean debugConsent) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
final int maxTemplatesPerUser = mSensors.get(
sensorId).getSensorProperties().maxEnrollmentsPerUser;
final FaceEnrollClient client = new FaceEnrollClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures,
+ opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
debugConsent);
scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@@ -351,11 +352,13 @@
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() ->
+ mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 206b8f0..3927043 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -494,7 +494,7 @@
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
- mScheduler = new UserAwareBiometricScheduler(tag,
+ mScheduler = new UserAwareBiometricScheduler(tag, mHandler,
BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index f4dcbbb..493c0a0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -333,12 +333,13 @@
Face10(@NonNull Context context,
@NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull Handler handler,
@NonNull BiometricScheduler scheduler) {
mSensorProperties = sensorProps;
mContext = context;
mSensorId = sensorProps.sensorId;
mScheduler = scheduler;
- mHandler = new Handler(Looper.getMainLooper());
+ mHandler = handler;
mUsageStats = new UsageStats(context);
mAuthenticatorIds = new HashMap<>();
mLazyDaemon = Face10.this::getDaemon;
@@ -357,10 +358,12 @@
}
}
- public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps,
+ public static Face10 newInstance(@NonNull Context context,
+ @NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
- this(context, sensorProps, lockoutResetDispatcher,
- new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
+ final Handler handler = new Handler(Looper.getMainLooper());
+ return new Face10(context, sensorProps, lockoutResetDispatcher, handler,
+ new BiometricScheduler(TAG, handler, BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityTracker */));
}
@@ -573,10 +576,11 @@
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
@Nullable Surface previewSurface, boolean debugConsent) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -584,7 +588,7 @@
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
+ opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@@ -598,13 +602,12 @@
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> {
- mScheduler.cancelEnrollment(token);
- });
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 80828cced..31e5c86 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -53,12 +53,13 @@
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */);
+ setRequestId(requestId);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
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 3e70ee5..6366e19 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
@@ -249,7 +249,7 @@
}
@Override // Binder call
- public void enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
+ public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
final int userId, final IFingerprintServiceReceiver receiver,
final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
@@ -257,15 +257,15 @@
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
- return;
+ return -1;
}
- provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+ return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, enrollReason);
}
@Override // Binder call
- public void cancelEnrollment(final IBinder token) {
+ public void cancelEnrollment(final IBinder token, long requestId) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -274,7 +274,7 @@
return;
}
- provider.second.cancelEnrollment(provider.first, token);
+ provider.second.cancelEnrollment(provider.first, token, requestId);
}
@SuppressWarnings("deprecation")
@@ -818,7 +818,7 @@
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
} else {
fingerprint21 = Fingerprint21.newInstance(getContext(),
- mFingerprintStateCallback, hidlSensor,
+ mFingerprintStateCallback, hidlSensor, mHandler,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
mServiceProviders.add(fingerprint21);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 1772f81..535705c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -88,11 +88,11 @@
/**
* Schedules fingerprint enrollment.
*/
- void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+ long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
@NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
- void cancelEnrollment(int sensorId, @NonNull IBinder token);
+ void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index ccb34aa..67507cc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -57,7 +57,7 @@
private boolean mIsPointerDown;
FingerprintEnrollClient(@NonNull Context context,
- @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+ @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@@ -69,6 +69,7 @@
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
!sensorProps.isAnyUdfpsType() /* shouldVibrate */);
+ setRequestId(requestId);
mSensorProps = sensorProps;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 734b173..eb16c76 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -347,15 +347,16 @@
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@FingerprintManager.EnrollReason int enrollReason) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
.maxEnrollmentsPerUser;
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mSensors.get(sensorId).getLazySession(), token,
+ mSensors.get(sensorId).getLazySession(), token, id,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
mSensors.get(sensorId).getSensorProperties(),
@@ -378,11 +379,13 @@
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() ->
+ mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 59e4b58..256761a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -449,7 +449,7 @@
mHandler = handler;
mSensorProperties = sensorProperties;
mLockoutCache = new LockoutCache();
- mScheduler = new UserAwareBiometricScheduler(tag,
+ mScheduler = new UserAwareBiometricScheduler(tag, handler,
BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
gestureAvailabilityDispatcher,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 5f2f4cf..d352cda 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -42,7 +42,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IHwBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -320,7 +319,8 @@
Fingerprint21(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
+ @NonNull BiometricScheduler scheduler,
+ @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
mContext = context;
@@ -356,16 +356,15 @@
public static Fingerprint21 newInstance(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
+ @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- final Handler handler = new Handler(Looper.getMainLooper());
final BiometricScheduler scheduler =
- new BiometricScheduler(TAG,
+ new BiometricScheduler(TAG, handler,
BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
gestureAvailabilityDispatcher);
final HalResultController controller = new HalResultController(sensorProps.sensorId,
- context, handler,
- scheduler);
+ context, handler, scheduler);
return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
lockoutResetDispatcher, controller);
}
@@ -558,18 +557,20 @@
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@FingerprintManager.EnrollReason int enrollReason) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
- mSidefpsController, enrollReason);
+ mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
+ userId, hardwareAuthToken, opPackageName,
+ FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
+ mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
+ enrollReason);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -588,13 +589,12 @@
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> {
- mScheduler.cancelEnrollment(token);
- });
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index dd68b4d..20dab55 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -26,7 +26,6 @@
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Handler;
import android.os.IBinder;
@@ -135,43 +134,17 @@
@NonNull private final RestartAuthRunnable mRestartAuthRunnable;
private static class TestableBiometricScheduler extends BiometricScheduler {
- @NonNull private final TestableInternalCallback mInternalCallback;
@NonNull private Fingerprint21UdfpsMock mFingerprint21;
- TestableBiometricScheduler(@NonNull String tag,
+ TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
+ super(tag, handler, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
gestureAvailabilityDispatcher);
- mInternalCallback = new TestableInternalCallback();
- }
-
- class TestableInternalCallback extends InternalCallback {
- @Override
- public void onClientStarted(BaseClientMonitor clientMonitor) {
- super.onClientStarted(clientMonitor);
- Slog.d(TAG, "Client started: " + clientMonitor);
- mFingerprint21.setDebugMessage("Started: " + clientMonitor);
- }
-
- @Override
- public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) {
- super.onClientFinished(clientMonitor, success);
- Slog.d(TAG, "Client finished: " + clientMonitor);
- mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
- }
}
void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
mFingerprint21 = fingerprint21;
}
-
- /**
- * Expose the internal finish callback so it can be used for testing
- */
- @Override
- @NonNull protected InternalCallback getInternalCallback() {
- return mInternalCallback;
- }
}
/**
@@ -280,7 +253,7 @@
final Handler handler = new Handler(Looper.getMainLooper());
final TestableBiometricScheduler scheduler =
- new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
+ new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher);
final MockHalResultController controller =
new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 1ebf44c..cc50bdf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -55,7 +55,7 @@
FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
+ long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@@ -64,6 +64,7 @@
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
+ setRequestId(requestId);
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mEnrollReason = enrollReason;
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 72e900b..cc9efbc 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -68,7 +68,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsManagerInternal;
import java.time.Clock;
import java.time.ZoneId;
@@ -262,8 +261,10 @@
private long getNetworkTotalBytes(long start, long end) {
try {
- return LocalServices.getService(NetworkStatsManagerInternal.class)
- .getNetworkTotalBytes(mNetworkTemplate, start, end);
+ final android.app.usage.NetworkStats.Bucket ret =
+ mContext.getSystemService(NetworkStatsManager.class)
+ .querySummaryForDevice(mNetworkTemplate, start, end);
+ return ret.getRxBytes() + ret.getTxBytes();
} catch (RuntimeException e) {
Log.w(TAG, "Failed to get data usage: " + e);
return -1;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c4f2b14..be889e4 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1781,6 +1781,14 @@
return mDisplayModeDirector.getModeSwitchingType();
}
+ private boolean getDisplayDecorationSupportInternal(int displayId) {
+ final IBinder displayToken = getDisplayToken(displayId);
+ if (null == displayToken) {
+ return false;
+ }
+ return SurfaceControl.getDisplayDecorationSupport(displayToken);
+ }
+
private void setBrightnessConfigurationForDisplayInternal(
@Nullable BrightnessConfiguration c, String uniqueId, @UserIdInt int userId,
String packageName) {
@@ -3441,6 +3449,16 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override // Binder call
+ public boolean getDisplayDecorationSupport(int displayId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getDisplayDecorationSupportInternal(displayId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
private static boolean isValidBrightness(float brightness) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index e40d86a..9fb1d8e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -896,8 +896,8 @@
info.packageName = mPackage;
info.attributionTag = mAttributionTag;
info.type = (mUid == Process.SYSTEM_UID)
- ? HostEndpointInfo.Type.TYPE_FRAMEWORK
- : HostEndpointInfo.Type.TYPE_APP;
+ ? HostEndpointInfo.Type.FRAMEWORK
+ : HostEndpointInfo.Type.APP;
mContextHubProxy.onHostEndpointConnected(info);
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index cc5aaf4..f84b68e 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -453,8 +453,13 @@
public int sendMessageToContextHub(
short hostEndpointId, int contextHubId, NanoAppMessage message)
throws RemoteException {
- return toTransactionResult(mHub.sendMessageToHub(contextHubId,
- ContextHubServiceUtil.createAidlContextHubMessage(hostEndpointId, message)));
+ try {
+ mHub.sendMessageToHub(contextHubId,
+ ContextHubServiceUtil.createAidlContextHubMessage(hostEndpointId, message));
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
@@ -462,31 +467,55 @@
int transactionId) throws RemoteException {
android.hardware.contexthub.NanoappBinary aidlNanoAppBinary =
ContextHubServiceUtil.createAidlNanoAppBinary(binary);
- return toTransactionResult(
- mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId));
+ try {
+ mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
public int unloadNanoapp(int contextHubId, long nanoappId, int transactionId)
throws RemoteException {
- return toTransactionResult(mHub.unloadNanoapp(contextHubId, nanoappId, transactionId));
+ try {
+ mHub.unloadNanoapp(contextHubId, nanoappId, transactionId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
public int enableNanoapp(int contextHubId, long nanoappId, int transactionId)
throws RemoteException {
- return toTransactionResult(mHub.enableNanoapp(contextHubId, nanoappId, transactionId));
+ try {
+ mHub.enableNanoapp(contextHubId, nanoappId, transactionId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
public int disableNanoapp(int contextHubId, long nanoappId, int transactionId)
throws RemoteException {
- return toTransactionResult(mHub.disableNanoapp(contextHubId, nanoappId, transactionId));
+ try {
+ mHub.disableNanoapp(contextHubId, nanoappId, transactionId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
public int queryNanoapps(int contextHubId) throws RemoteException {
- return toTransactionResult(mHub.queryNanoapps(contextHubId));
+ try {
+ mHub.queryNanoapps(contextHubId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
@@ -494,12 +523,6 @@
mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId));
}
- @ContextHubTransaction.Result
- private int toTransactionResult(boolean success) {
- return success ? ContextHubTransaction.RESULT_SUCCESS
- : ContextHubTransaction.RESULT_FAILED_UNKNOWN;
- }
-
private void onSettingChanged(byte setting, boolean enabled) {
try {
mHub.onSettingChanged(setting, enabled);
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index a52c9ce..5093f5d 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -131,8 +131,8 @@
return mPermitted;
}
- boolean onLocationPermissionsChanged(String packageName) {
- if (getIdentity().getPackageName().equals(packageName)) {
+ boolean onLocationPermissionsChanged(@Nullable String packageName) {
+ if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
return onLocationPermissionsChanged();
}
@@ -242,7 +242,7 @@
mLocationPermissionsListener =
new LocationPermissionsHelper.LocationPermissionsListener() {
@Override
- public void onLocationPermissionsChanged(String packageName) {
+ public void onLocationPermissionsChanged(@Nullable String packageName) {
GeofenceManager.this.onLocationPermissionsChanged(packageName);
}
@@ -494,7 +494,7 @@
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- void onLocationPermissionsChanged(String packageName) {
+ void onLocationPermissionsChanged(@Nullable String packageName) {
updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 5e6ae68..a540476 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -119,8 +119,8 @@
*/
protected void onGnssListenerUnregister() {}
- boolean onLocationPermissionsChanged(String packageName) {
- if (getIdentity().getPackageName().equals(packageName)) {
+ boolean onLocationPermissionsChanged(@Nullable String packageName) {
+ if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
return onLocationPermissionsChanged();
}
@@ -197,7 +197,7 @@
mLocationPermissionsListener =
new LocationPermissionsHelper.LocationPermissionsListener() {
@Override
- public void onLocationPermissionsChanged(String packageName) {
+ public void onLocationPermissionsChanged(@Nullable String packageName) {
GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
}
@@ -390,7 +390,7 @@
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onLocationPermissionsChanged(String packageName) {
+ private void onLocationPermissionsChanged(@Nullable String packageName) {
updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
}
diff --git a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
index 2df2101..557ecda 100644
--- a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
@@ -18,6 +18,7 @@
import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
import com.android.server.location.LocationPermissions;
@@ -36,9 +37,10 @@
public interface LocationPermissionsListener {
/**
- * Called when something has changed about location permissions for the given package.
+ * Called when something has changed about location permissions for the given package. A
+ * null package indicates this affects every package.
*/
- void onLocationPermissionsChanged(String packageName);
+ void onLocationPermissionsChanged(@Nullable String packageName);
/**
* Called when something has changed about location permissions for the given uid.
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 1ba32ac..d42e2c6 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -509,8 +509,8 @@
}
@GuardedBy("mLock")
- final boolean onLocationPermissionsChanged(String packageName) {
- if (getIdentity().getPackageName().equals(packageName)) {
+ final boolean onLocationPermissionsChanged(@Nullable String packageName) {
+ if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
return onLocationPermissionsChanged();
}
@@ -915,30 +915,19 @@
return null;
}
+ // acquire a wakelock for non-passive requests
+ boolean useWakeLock =
+ getRequest().getIntervalMillis() != LocationRequest.PASSIVE_INTERVAL;
+
// deliver location
return new ListenerOperation<LocationTransport>() {
- private boolean mUseWakeLock;
-
@Override
public void onPreExecute() {
- mUseWakeLock = false;
-
- // don't acquire a wakelock for passive requests or for mock locations
- if (getRequest().getIntervalMillis() != LocationRequest.PASSIVE_INTERVAL) {
- final int size = locationResult.size();
- for (int i = 0; i < size; ++i) {
- if (!locationResult.get(i).isMock()) {
- mUseWakeLock = true;
- break;
- }
- }
- }
-
// update last delivered location
setLastDeliveredLocation(locationResult.getLastLocation());
- if (mUseWakeLock) {
+ if (useWakeLock) {
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
}
}
@@ -955,14 +944,14 @@
}
listener.deliverOnLocationChanged(deliverLocationResult,
- mUseWakeLock ? mWakeLockReleaser : null);
+ useWakeLock ? mWakeLockReleaser : null);
EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@Override
public void onPostExecute(boolean success) {
- if (!success && mUseWakeLock) {
+ if (!success && useWakeLock) {
mWakeLock.release();
}
@@ -1355,7 +1344,7 @@
private final LocationPermissionsListener mLocationPermissionsListener =
new LocationPermissionsListener() {
@Override
- public void onLocationPermissionsChanged(String packageName) {
+ public void onLocationPermissionsChanged(@Nullable String packageName) {
LocationProviderManager.this.onLocationPermissionsChanged(packageName);
}
@@ -2361,7 +2350,7 @@
}
}
- private void onLocationPermissionsChanged(String packageName) {
+ private void onLocationPermissionsChanged(@Nullable String packageName) {
synchronized (mLock) {
updateRegistrations(
registration -> registration.onLocationPermissionsChanged(packageName));
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2068632..755c50d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -595,6 +595,7 @@
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
private boolean mLockScreenAllowSecureNotifications = true;
+ boolean mAllowFgsDismissal = false;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
@@ -1137,8 +1138,9 @@
id = r.getSbn().getId();
}
}
+ int mustNotHaveFlags = FLAG_ONGOING_EVENT;
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
+ mustNotHaveFlags,
true, userId, REASON_CANCEL, nv.rank, nv.count,null);
nv.recycle();
}
@@ -2411,7 +2413,7 @@
publishLocalService(NotificationManagerInternal.class, mInternalService);
}
- private void registerDeviceConfigChange() {
+ void registerDeviceConfigChange() {
mDeviceConfigChangedListener = properties -> {
if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
return;
@@ -2440,9 +2442,19 @@
} else if ("false".equals(value)) {
mAssistants.disallowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
}
+ } else if (SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED.equals(name)) {
+ String value = properties.getString(name, null);
+ if ("true".equals(value)) {
+ mAllowFgsDismissal = true;
+ } else if ("false".equals(value)) {
+ mAllowFgsDismissal = false;
+ }
}
}
};
+ mAllowFgsDismissal = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, false);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI,
new HandlerExecutor(mHandler),
@@ -3343,8 +3355,7 @@
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
- // Calling from user space, don't allow the canceling of actively
- // running foreground services.
+ // Don't allow the app to cancel active FGS notifications
cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
pkg, null, 0, FLAG_FOREGROUND_SERVICE, true, userId,
REASON_APP_CANCEL_ALL, null);
@@ -4414,8 +4425,9 @@
@GuardedBy("mNotificationLock")
private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
- cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
+ int mustNotHaveFlags = FLAG_ONGOING_EVENT;
+ cancelNotification(callingUid, callingPid, pkg, tag, id, 0 /* mustHaveFlags */,
+ mustNotHaveFlags,
true,
userId, REASON_LISTENER_CANCEL, info);
}
@@ -6132,12 +6144,11 @@
return;
}
StatusBarNotification sbn = r.getSbn();
- // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
+ // NoMan adds flags FLAG_ONGOING_EVENT when it sees
// FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
// FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
// initially *and* force remove FLAG_FOREGROUND_SERVICE.
- sbn.getNotification().flags =
- (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
+ sbn.getNotification().flags = (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
}
};
@@ -6910,7 +6921,6 @@
r.getKey(), true /* notifSuppressed */, isBubbleSuppressed);
return;
}
-
if ((r.getNotification().flags & mMustHaveFlags) != mMustHaveFlags) {
return;
}
@@ -6918,19 +6928,25 @@
return;
}
- // Bubbled children get to stick around if the summary was manually cancelled
- // (user removed) from systemui.
- FlagChecker childrenFlagChecker = null;
- if (mReason == REASON_CANCEL
- || mReason == REASON_CLICK
- || mReason == REASON_CANCEL_ALL) {
- childrenFlagChecker = (flags) -> {
- if ((flags & FLAG_BUBBLE) != 0) {
+ FlagChecker childrenFlagChecker = (flags) -> {
+ if (mReason == REASON_CANCEL
+ || mReason == REASON_CLICK
+ || mReason == REASON_CANCEL_ALL) {
+ // Bubbled children get to stick around if the summary was manually
+ // cancelled (user removed) from systemui.
+ if ((flags & FLAG_BUBBLE) != 0) {
+ return false;
+ }
+ } else if (mReason == REASON_APP_CANCEL) {
+ if ((flags & FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
+ }
+ }
+ if ((flags & mMustNotHaveFlags) != 0) {
return false;
}
return true;
};
- }
// Cancel the notification.
boolean wasPosted = removeFromNotificationListsLocked(r);
@@ -7141,8 +7157,10 @@
// Ensure if this is a foreground service that the proper additional
// flags are set.
if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
- notification.flags |= FLAG_ONGOING_EVENT
- | FLAG_NO_CLEAR;
+ notification.flags |= FLAG_NO_CLEAR;
+ if (!mAllowFgsDismissal) {
+ notification.flags |= FLAG_ONGOING_EVENT;
+ }
}
mRankingHelper.extractSignals(r);
@@ -7417,13 +7435,20 @@
mSummaryByGroupKey.put(group, r);
}
+ FlagChecker childrenFlagChecker = (flags) -> {
+ if ((flags & FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
+ }
+ return true;
+ };
+
// Clear out group children of the old notification if the update
// causes the group summary to go away. This happens when the old
// notification was a summary and the new one isn't, or when the old
// notification was a summary and its group key changed.
if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */,
- null, REASON_APP_CANCEL);
+ childrenFlagChecker, REASON_APP_CANCEL);
}
}
@@ -9042,7 +9067,6 @@
final StatusBarNotification childSbn = childR.getSbn();
if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
childR.getGroupKey().equals(parentNotification.getGroupKey())
- && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0
&& (flagChecker == null || flagChecker.apply(childR.getFlags()))
&& (!childR.getChannel().isImportantConversation()
|| reason != REASON_CANCEL)) {
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index dd66130..fcf4a02 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -49,8 +49,6 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -309,10 +307,6 @@
@Nullable
String getRenamedPackage(@NonNull String packageName);
- @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
- @NonNull
- WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries();
-
/**
* @return set of packages to notify
*/
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 2f4f271..e37aaa5 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -3490,9 +3490,8 @@
return mSettings.getRenamedPackageLPr(packageName);
}
- @NonNull
- @Override
- public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+ private WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+ getSharedLibraries() {
return mSharedLibraries.getAll();
}
@@ -4788,7 +4787,7 @@
@Override
public List<PackageStateInternal> findSharedNonSystemLibraries(
@NonNull PackageStateInternal pkgSetting) {
- List<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
+ List<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
if (!deps.isEmpty()) {
List<PackageStateInternal> retValue = new ArrayList<>();
for (SharedLibraryInfo info : deps) {
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index bd730e9..529aca3 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -46,8 +46,6 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -269,14 +267,6 @@
@NonNull
@Override
- public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
- synchronized (mLock) {
- return super.getSharedLibraries();
- }
- }
-
- @NonNull
- @Override
public ArraySet<String> getNotifyPackagesForReplacedReceived(@NonNull String[] packages) {
synchronized (mLock) {
return super.getNotifyPackagesForReplacedReceived(packages);
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index e6ff836..52309ce 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -47,8 +47,6 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -703,14 +701,6 @@
@NonNull
@Override
- public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
- try (ThreadComputer current = snapshot()) {
- return current.mComputer.getSharedLibraries();
- }
- }
-
- @NonNull
- @Override
public ArraySet<String> getNotifyPackagesForReplacedReceived(@NonNull String[] packages) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getNotifyPackagesForReplacedReceived(packages);
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index eac38af..dcad3ec 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -379,7 +379,7 @@
// at boot, or background job), the passed 'targetCompilerFilter' stays the same,
// and the first package that uses the library will dexopt it. The
// others will see that the compiled code for the library is up to date.
- Collection<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
+ Collection<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
final String[] instructionSets = getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 27b6282..9302aad 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -894,8 +894,6 @@
final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
- final Map<String, PackageSetting> lastStaticSharedLibSettings =
- new ArrayMap<>(requests.size());
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
boolean success = false;
try {
@@ -955,35 +953,22 @@
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
- if (result.mStaticSharedLibraryInfo != null) {
- final PackageSetting staticSharedLibLatestVersionSetting =
- mSharedLibraries.getStaticSharedLibLatestVersionSetting(result);
- if (staticSharedLibLatestVersionSetting != null) {
- lastStaticSharedLibSettings.put(
- result.mPkgSetting.getPkg().getPackageName(),
- staticSharedLibLatestVersionSetting);
- }
- }
} catch (PackageManagerException e) {
request.mInstallResult.setError("Scanning Failed.", e);
return;
}
}
- ReconcileRequest
- reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
- installResults,
- prepareResults,
- mSharedLibraries.getAll(),
- Collections.unmodifiableMap(mPm.mPackages), versionInfos,
- lastStaticSharedLibSettings);
+ ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
+ installResults, prepareResults,
+ Collections.unmodifiableMap(mPm.mPackages), versionInfos);
CommitRequest commitRequest = null;
synchronized (mPm.mLock) {
Map<String, ReconciledPackage> reconciledPackages;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
reconciledPackages = ReconcilePackageUtils.reconcilePackages(
- reconcileRequest, mPm.mSettings.getKeySetManagerService(),
- mPm.mInjector);
+ reconcileRequest, mSharedLibraries,
+ mPm.mSettings.getKeySetManagerService());
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
request.mInstallResult.setError("Reconciliation failed...", e);
@@ -3586,15 +3571,12 @@
final String pkgName = scanResult.mPkgSetting.getPackageName();
final ReconcileRequest reconcileRequest = new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
- mSharedLibraries.getAll(), mPm.mPackages,
+ mPm.mPackages,
Collections.singletonMap(pkgName,
- mPm.getSettingsVersionForPackage(parsedPackage)),
- Collections.singletonMap(pkgName,
- mSharedLibraries.getStaticSharedLibLatestVersionSetting(
- scanResult)));
+ mPm.getSettingsVersionForPackage(parsedPackage)));
final Map<String, ReconciledPackage> reconcileResult =
ReconcilePackageUtils.reconcilePackages(reconcileRequest,
- mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
+ mSharedLibraries, mPm.mSettings.getKeySetManagerService());
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(reconcileResult.get(pkgName),
mPm.mUserManager.getUserIds());
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 1bdc9f3..c219f80 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -40,7 +40,7 @@
per-file KeySetManagerService.java = cbrubaker@google.com, nnk@google.com
per-file PackageKeySetData.java = cbrubaker@google.com, nnk@google.com
per-file PackageSignatures.java = cbrubaker@google.com, nnk@google.com
-per-file SELinuxMMAC* = alanstokes@google.com, cbrubaker@google.com, jeffv@google.com, jgalenson@google.com
+per-file SELinuxMMAC* = alanstokes@google.com, cbrubaker@google.com, jeffv@google.com
# shortcuts
per-file LauncherAppsService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 5a25004..67f6b12 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -42,8 +42,8 @@
final class ReconcilePackageUtils {
public static Map<String, ReconciledPackage> reconcilePackages(
- final ReconcileRequest request, KeySetManagerService ksms,
- PackageManagerServiceInjector injector)
+ final ReconcileRequest request, SharedLibrariesImpl sharedLibraries,
+ KeySetManagerService ksms)
throws ReconcileFailure {
final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
@@ -67,11 +67,10 @@
// in the first pass, we'll build up the set of incoming shared libraries
final List<SharedLibraryInfo> allowedSharedLibInfos =
- SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
- request.mSharedLibrarySource);
+ sharedLibraries.getAllowedSharedLibInfos(scanResult);
if (allowedSharedLibInfos != null) {
for (SharedLibraryInfo info : allowedSharedLibInfos) {
- if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
+ if (!SharedLibraryUtils.addSharedLibraryToPackageVersionMap(
incomingSharedLibraries, info)) {
throw new ReconcileFailure("Shared Library " + info.getName()
+ " is being installed twice in this set!");
@@ -113,7 +112,8 @@
final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
final PackageSetting lastStaticSharedLibSetting =
- request.mLastStaticSharedLibSettings.get(installPackageName);
+ scanResult.mStaticSharedLibraryInfo == null ? null
+ : sharedLibraries.getStaticSharedLibLatestVersionSetting(scanResult);
final PackageSetting signatureCheckPs =
(prepareResult != null && lastStaticSharedLibSetting != null)
? lastStaticSharedLibSetting
@@ -264,11 +264,9 @@
}
try {
result.get(installPackageName).mCollectedSharedLibraryInfos =
- SharedLibraryHelper.collectSharedLibraryInfos(
- scanResult.mRequest.mParsedPackage,
- combinedPackages, request.mSharedLibrarySource,
- incomingSharedLibraries, injector.getCompatibility());
-
+ sharedLibraries.collectSharedLibraryInfos(
+ scanResult.mRequest.mParsedPackage, combinedPackages,
+ incomingSharedLibraries);
} catch (PackageManagerException e) {
throw new ReconcileFailure(e.error, e.getMessage());
}
diff --git a/services/core/java/com/android/server/pm/ReconcileRequest.java b/services/core/java/com/android/server/pm/ReconcileRequest.java
index 3188138..9e4e986 100644
--- a/services/core/java/com/android/server/pm/ReconcileRequest.java
+++ b/services/core/java/com/android/server/pm/ReconcileRequest.java
@@ -16,10 +16,7 @@
package com.android.server.pm;
-import android.content.pm.SharedLibraryInfo;
-
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.utils.WatchedLongSparseArray;
import java.util.Collections;
import java.util.Map;
@@ -37,38 +34,29 @@
public final Map<String, ScanResult> mScannedPackages;
public final Map<String, AndroidPackage> mAllPackages;
- public final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> mSharedLibrarySource;
public final Map<String, InstallArgs> mInstallArgs;
public final Map<String, PackageInstalledInfo> mInstallResults;
public final Map<String, PrepareResult> mPreparedPackages;
public final Map<String, Settings.VersionInfo> mVersionInfos;
- public final Map<String, PackageSetting> mLastStaticSharedLibSettings;
ReconcileRequest(Map<String, ScanResult> scannedPackages,
Map<String, InstallArgs> installArgs,
Map<String, PackageInstalledInfo> installResults,
Map<String, PrepareResult> preparedPackages,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
- Map<String, Settings.VersionInfo> versionInfos,
- Map<String, PackageSetting> lastStaticSharedLibSettings) {
+ Map<String, Settings.VersionInfo> versionInfos) {
mScannedPackages = scannedPackages;
mInstallArgs = installArgs;
mInstallResults = installResults;
mPreparedPackages = preparedPackages;
- mSharedLibrarySource = sharedLibrarySource;
mAllPackages = allPackages;
mVersionInfos = versionInfos;
- mLastStaticSharedLibSettings = lastStaticSharedLibSettings;
}
ReconcileRequest(Map<String, ScanResult> scannedPackages,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
- Map<String, Settings.VersionInfo> versionInfos,
- Map<String, PackageSetting> lastStaticSharedLibSettings) {
+ Map<String, Settings.VersionInfo> versionInfos) {
this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
- Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
- lastStaticSharedLibSettings);
+ Collections.emptyMap(), allPackages, versionInfos);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 21df5a9..7085682 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4863,11 +4863,11 @@
pw.print(userState.isInstantApp());
pw.print(" virtual=");
pw.println(userState.isVirtualPreload());
- pw.print(" installReason=");
+ pw.print(" installReason=");
pw.println(userState.getInstallReason());
final PackageUserStateInternal pus = ps.readUserState(user.id);
- pw.print(" firstInstallTime=");
+ pw.print(" firstInstallTime=");
date.setTime(pus.getFirstInstallTime());
pw.println(sdf.format(date));
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 0055f4e..aa23050 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -16,19 +16,27 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.VersionedPackage;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.service.pm.PackageServiceDumpProto;
import android.util.ArraySet;
+import android.util.PackageUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -36,8 +44,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
+import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
@@ -47,10 +57,13 @@
import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.Watcher;
+import libcore.util.HexEncoding;
+
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
@@ -62,6 +75,24 @@
* Current known shared libraries on the device.
*/
public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable, Snappable {
+ private static final boolean DEBUG_SHARED_LIBRARIES = false;
+
+ /**
+ * Apps targeting Android S and above need to declare dependencies to the public native
+ * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+ * in its {@code AndroidManifest.xml}.
+ *
+ * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+ * the package manager rejects to install the app. The dependency can be specified as optional
+ * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+ * dependency doesn't stop the installation.
+ * <p>Once installed, an app is provided with only the native shared libraries that are
+ * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+ * in the app manifest will fail even if it actually exists on the device.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
// TODO(b/200588896): remove PMS dependency
private final PackageManagerService mPm;
@@ -493,10 +524,8 @@
@Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
@NonNull Map<String, AndroidPackage> availablePackages)
throws PackageManagerException {
- final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
- SharedLibraryHelper.collectSharedLibraryInfos(
- pkgSetting.getPkg(), availablePackages, mSharedLibraries,
- null /* newLibraries */, mInjector.getCompatibility());
+ final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
+ pkgSetting.getPkg(), availablePackages, null /* newLibraries */);
executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting,
sharedLibraryInfos, mPm.mUserManager.getUserIds());
}
@@ -735,6 +764,234 @@
}
/**
+ * Compare the newly scanned package with current system state to see which of its declared
+ * shared libraries should be allowed to be added to the system.
+ */
+ List<SharedLibraryInfo> getAllowedSharedLibInfos(ScanResult scanResult) {
+ // Let's used the parsed package as scanResult.pkgSetting may be null
+ final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+ if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null
+ && scanResult.mDynamicSharedLibraryInfos == null) {
+ return null;
+ }
+
+ // Any app can add new SDKs and static shared libraries.
+ if (scanResult.mSdkSharedLibraryInfo != null) {
+ return Collections.singletonList(scanResult.mSdkSharedLibraryInfo);
+ }
+ if (scanResult.mStaticSharedLibraryInfo != null) {
+ return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
+ }
+ final boolean hasDynamicLibraries = parsedPackage.isSystem()
+ && scanResult.mDynamicSharedLibraryInfos != null;
+ if (!hasDynamicLibraries) {
+ return null;
+ }
+ final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
+ .isUpdatedSystemApp();
+ // We may not yet have disabled the updated package yet, so be sure to grab the
+ // current setting if that's the case.
+ final PackageSetting updatedSystemPs = isUpdatedSystemApp
+ ? scanResult.mRequest.mDisabledPkgSetting == null
+ ? scanResult.mRequest.mOldPkgSetting
+ : scanResult.mRequest.mDisabledPkgSetting
+ : null;
+ if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
+ || updatedSystemPs.getPkg().getLibraryNames() == null)) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " declares libraries that are not declared on the system image; skipping");
+ return null;
+ }
+ final ArrayList<SharedLibraryInfo> infos =
+ new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
+ for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
+ final String name = info.getName();
+ if (isUpdatedSystemApp) {
+ // New library entries can only be added through the
+ // system image. This is important to get rid of a lot
+ // of nasty edge cases: for example if we allowed a non-
+ // system update of the app to add a library, then uninstalling
+ // the update would make the library go away, and assumptions
+ // we made such as through app install filtering would now
+ // have allowed apps on the device which aren't compatible
+ // with it. Better to just have the restriction here, be
+ // conservative, and create many fewer cases that can negatively
+ // impact the user experience.
+ if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " declares library " + name
+ + " that is not declared on system image; skipping");
+ continue;
+ }
+ }
+ synchronized (mPm.mLock) {
+ if (getSharedLibraryInfo(name, SharedLibraryInfo.VERSION_UNDEFINED) != null) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
+ + name + " that already exists; skipping");
+ continue;
+ }
+ }
+ infos.add(info);
+ }
+ return infos;
+ }
+
+ /**
+ * Collects shared library infos that are being used by the given package.
+ *
+ * @param pkg The package using shared libraries.
+ * @param availablePackages The available packages which are installed and being installed,
+ * @param newLibraries Shared libraries defined by packages which are being installed.
+ * @return A list of shared library infos
+ */
+ ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(@Nullable AndroidPackage pkg,
+ @NonNull Map<String, AndroidPackage> availablePackages,
+ @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
+ throws PackageManagerException {
+ if (pkg == null) {
+ return null;
+ }
+ final PlatformCompat platformCompat = mInjector.getCompatibility();
+ // The collection used here must maintain the order of addition (so
+ // that libraries are searched in the correct order) and must have no
+ // duplicates.
+ ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
+ if (!pkg.getUsesLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
+ pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
+ availablePackages, newLibraries);
+ }
+ if (!pkg.getUsesStaticLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
+ pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
+ pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, newLibraries);
+ }
+ if (!pkg.getUsesOptionalLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
+ pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, newLibraries);
+ }
+ if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
+ pkg.getPackageName(), pkg.getTargetSdkVersion())) {
+ if (!pkg.getUsesNativeLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+ null, pkg.getPackageName(), "native shared", true,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+ newLibraries);
+ }
+ if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+ null, null, pkg.getPackageName(), "native shared", false,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+ newLibraries);
+ }
+ }
+ if (!pkg.getUsesSdkLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
+ pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
+ pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
+ availablePackages, newLibraries);
+ }
+ return usesLibraryInfos;
+ }
+
+ private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
+ @NonNull List<String> requestedLibraries,
+ @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
+ @NonNull String packageName, @NonNull String libraryType, boolean required,
+ int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
+ @NonNull final Map<String, AndroidPackage> availablePackages,
+ @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
+ throws PackageManagerException {
+ final int libCount = requestedLibraries.size();
+ for (int i = 0; i < libCount; i++) {
+ final String libName = requestedLibraries.get(i);
+ final long libVersion = requiredVersions != null ? requiredVersions[i]
+ : SharedLibraryInfo.VERSION_UNDEFINED;
+ final SharedLibraryInfo libraryInfo;
+ synchronized (mPm.mLock) {
+ libraryInfo = SharedLibraryUtils.getSharedLibraryInfo(
+ libName, libVersion, mSharedLibraries, newLibraries);
+ }
+ if (libraryInfo == null) {
+ if (required) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library " + libName + "; failing!");
+ } else if (DEBUG_SHARED_LIBRARIES) {
+ Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
+ + " library " + libName + "; ignoring!");
+ }
+ } else {
+ if (requiredVersions != null && requiredCertDigests != null) {
+ if (libraryInfo.getLongVersion() != requiredVersions[i]) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library " + libName + " version "
+ + libraryInfo.getLongVersion() + "; failing!");
+ }
+ AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
+ SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
+ if (libPkg == null) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library; failing!");
+ }
+ final String[] expectedCertDigests = requiredCertDigests[i];
+ if (expectedCertDigests.length > 1) {
+ // For apps targeting O MR1 we require explicit enumeration of all certs.
+ final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
+ ? PackageUtils.computeSignaturesSha256Digests(
+ libPkg.getSignatures())
+ : PackageUtils.computeSignaturesSha256Digests(
+ new Signature[]{libPkg.getSignatures()[0]});
+
+ // Take a shortcut if sizes don't match. Note that if an app doesn't
+ // target O we don't parse the "additional-certificate" tags similarly
+ // how we only consider all certs only for apps targeting O (see above).
+ // Therefore, the size check is safe to make.
+ if (expectedCertDigests.length != libCertDigests.length) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
+ }
+
+ // Use a predictable order as signature order may vary
+ Arrays.sort(libCertDigests);
+ Arrays.sort(expectedCertDigests);
+
+ final int certCount = libCertDigests.length;
+ for (int j = 0; j < certCount; j++) {
+ if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
+ }
+ }
+ } else {
+ // lib signing cert could have rotated beyond the one expected, check to see
+ // if the new one has been blessed by the old
+ byte[] digestBytes = HexEncoding.decode(
+ expectedCertDigests[0], false /* allowSingleChar */);
+ if (!libPkg.hasSha256Certificate(digestBytes)) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
+ }
+ }
+ }
+ if (outUsedLibraries == null) {
+ outUsedLibraries = new ArrayList<>();
+ }
+ outUsedLibraries.add(libraryInfo);
+ }
+ }
+ return outUsedLibraries;
+ }
+
+ /**
* Dump all shared libraries.
*/
@GuardedBy("mPm.mLock")
diff --git a/services/core/java/com/android/server/pm/SharedLibraryHelper.java b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
deleted file mode 100644
index dd8fad0..0000000
--- a/services/core/java/com/android/server/pm/SharedLibraryHelper.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-
-import static com.android.server.pm.PackageManagerService.TAG;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.Signature;
-import android.content.pm.SigningDetails;
-import android.os.Build;
-import android.util.PackageUtils;
-import android.util.Slog;
-
-import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedLongSparseArray;
-
-import libcore.util.HexEncoding;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-final class SharedLibraryHelper {
- private static final boolean DEBUG_SHARED_LIBRARIES = false;
-
- /**
- * Apps targeting Android S and above need to declare dependencies to the public native
- * shared libraries that are defined by the device maker using {@code uses-native-library} tag
- * in its {@code AndroidManifest.xml}.
- *
- * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
- * the package manager rejects to install the app. The dependency can be specified as optional
- * using {@code android:required} attribute in the tag, in which case failing to satisfy the
- * dependency doesn't stop the installation.
- * <p>Once installed, an app is provided with only the native shared libraries that are
- * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
- * in the app manifest will fail even if it actually exists on the device.
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
- private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
-
- /**
- * Compare the newly scanned package with current system state to see which of its declared
- * shared libraries should be allowed to be added to the system.
- */
- public static List<SharedLibraryInfo> getAllowedSharedLibInfos(
- ScanResult scanResult,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
- // Let's used the parsed package as scanResult.pkgSetting may be null
- final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
- if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null
- && scanResult.mDynamicSharedLibraryInfos == null) {
- return null;
- }
-
- // Any app can add new SDKs and static shared libraries.
- if (scanResult.mSdkSharedLibraryInfo != null) {
- return Collections.singletonList(scanResult.mSdkSharedLibraryInfo);
- }
- if (scanResult.mStaticSharedLibraryInfo != null) {
- return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
- }
- final boolean hasDynamicLibraries = parsedPackage.isSystem()
- && scanResult.mDynamicSharedLibraryInfos != null;
- if (!hasDynamicLibraries) {
- return null;
- }
- final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
- .isUpdatedSystemApp();
- // We may not yet have disabled the updated package yet, so be sure to grab the
- // current setting if that's the case.
- final PackageSetting updatedSystemPs = isUpdatedSystemApp
- ? scanResult.mRequest.mDisabledPkgSetting == null
- ? scanResult.mRequest.mOldPkgSetting
- : scanResult.mRequest.mDisabledPkgSetting
- : null;
- if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
- || updatedSystemPs.getPkg().getLibraryNames() == null)) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " declares libraries that are not declared on the system image; skipping");
- return null;
- }
- final ArrayList<SharedLibraryInfo> infos =
- new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
- for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
- final String name = info.getName();
- if (isUpdatedSystemApp) {
- // New library entries can only be added through the
- // system image. This is important to get rid of a lot
- // of nasty edge cases: for example if we allowed a non-
- // system update of the app to add a library, then uninstalling
- // the update would make the library go away, and assumptions
- // we made such as through app install filtering would now
- // have allowed apps on the device which aren't compatible
- // with it. Better to just have the restriction here, be
- // conservative, and create many fewer cases that can negatively
- // impact the user experience.
- if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " declares library " + name
- + " that is not declared on system image; skipping");
- continue;
- }
- }
- if (sharedLibExists(
- name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
- + name + " that already exists; skipping");
- continue;
- }
- infos.add(info);
- }
- return infos;
- }
-
- public static boolean sharedLibExists(final String name, final long version,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) {
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
- return versionedLib != null && versionedLib.indexOfKey(version) >= 0;
- }
-
- /**
- * Returns false if the adding shared library already exists in the map and so could not be
- * added.
- */
- public static boolean addSharedLibraryToPackageVersionMap(
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
- SharedLibraryInfo library) {
- final String name = library.getName();
- if (target.containsKey(name)) {
- if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
- // We've already added this non-version-specific library to the map.
- return false;
- } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
- // We've already added this version of a version-specific library to the map.
- return false;
- }
- } else {
- target.put(name, new WatchedLongSparseArray<>());
- }
- target.get(name).put(library.getLongVersion(), library);
- return true;
- }
-
- public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
- Map<String, AndroidPackage> availablePackages,
- @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries,
- PlatformCompat platformCompat) throws PackageManagerException {
- if (pkg == null) {
- return null;
- }
- // The collection used here must maintain the order of addition (so
- // that libraries are searched in the correct order) and must have no
- // duplicates.
- ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
- if (!pkg.getUsesLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
- pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
- availablePackages, existingLibraries, newLibraries);
- }
- if (!pkg.getUsesStaticLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
- pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
- pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
- }
- if (!pkg.getUsesOptionalLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
- pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
- }
- if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
- pkg.getPackageName(), pkg.getTargetSdkVersion())) {
- if (!pkg.getUsesNativeLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
- null, pkg.getPackageName(), "native shared", true,
- pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
- existingLibraries, newLibraries);
- }
- if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
- null, null, pkg.getPackageName(), "native shared", false,
- pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
- existingLibraries, newLibraries);
- }
- }
- if (!pkg.getUsesSdkLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
- pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
- pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
- availablePackages, existingLibraries, newLibraries);
- }
- return usesLibraryInfos;
- }
-
- public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
- @NonNull List<String> requestedLibraries,
- @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
- @NonNull String packageName, @NonNull String libraryType, boolean required,
- int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
- @NonNull final Map<String, AndroidPackage> availablePackages,
- @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
- throws PackageManagerException {
- final int libCount = requestedLibraries.size();
- for (int i = 0; i < libCount; i++) {
- final String libName = requestedLibraries.get(i);
- final long libVersion = requiredVersions != null ? requiredVersions[i]
- : SharedLibraryInfo.VERSION_UNDEFINED;
- final SharedLibraryInfo libraryInfo =
- getSharedLibraryInfo(libName, libVersion, existingLibraries, newLibraries);
- if (libraryInfo == null) {
- if (required) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable " + libraryType
- + " library " + libName + "; failing!");
- } else if (DEBUG_SHARED_LIBRARIES) {
- Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
- + " library " + libName + "; ignoring!");
- }
- } else {
- if (requiredVersions != null && requiredCertDigests != null) {
- if (libraryInfo.getLongVersion() != requiredVersions[i]) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable " + libraryType
- + " library " + libName + " version "
- + libraryInfo.getLongVersion() + "; failing!");
- }
- AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
- SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
- if (libPkg == null) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable " + libraryType
- + " library; failing!");
- }
- final String[] expectedCertDigests = requiredCertDigests[i];
- if (expectedCertDigests.length > 1) {
- // For apps targeting O MR1 we require explicit enumeration of all certs.
- final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
- ? PackageUtils.computeSignaturesSha256Digests(
- libPkg.getSignatures())
- : PackageUtils.computeSignaturesSha256Digests(
- new Signature[]{libPkg.getSignatures()[0]});
-
- // Take a shortcut if sizes don't match. Note that if an app doesn't
- // target O we don't parse the "additional-certificate" tags similarly
- // how we only consider all certs only for apps targeting O (see above).
- // Therefore, the size check is safe to make.
- if (expectedCertDigests.length != libCertDigests.length) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed "
- + libraryType + " library; failing!");
- }
-
- // Use a predictable order as signature order may vary
- Arrays.sort(libCertDigests);
- Arrays.sort(expectedCertDigests);
-
- final int certCount = libCertDigests.length;
- for (int j = 0; j < certCount; j++) {
- if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
- throw new PackageManagerException(
- INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed "
- + libraryType + " library; failing!");
- }
- }
- } else {
- // lib signing cert could have rotated beyond the one expected, check to see
- // if the new one has been blessed by the old
- byte[] digestBytes = HexEncoding.decode(
- expectedCertDigests[0], false /* allowSingleChar */);
- if (!libPkg.hasSha256Certificate(digestBytes)) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed "
- + libraryType + " library; failing!");
- }
- }
- }
- if (outUsedLibraries == null) {
- outUsedLibraries = new ArrayList<>();
- }
- outUsedLibraries.add(libraryInfo);
- }
- }
- return outUsedLibraries;
- }
-
- @Nullable
- public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
- if (newLibraries != null) {
- final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
- SharedLibraryInfo info = null;
- if (versionedLib != null) {
- info = versionedLib.get(version);
- }
- if (info != null) {
- return info;
- }
- }
- final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
- if (versionedLib == null) {
- return null;
- }
- return versionedLib.get(version);
- }
-
- public static List<SharedLibraryInfo> findSharedLibraries(PackageStateInternal pkgSetting) {
- if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
- ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
- Set<String> collectedNames = new HashSet<>();
- for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
- findSharedLibrariesRecursive(info, retValue, collectedNames);
- }
- return retValue;
- } else {
- return Collections.emptyList();
- }
- }
-
- private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
- ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
- if (!collectedNames.contains(info.getName())) {
- collectedNames.add(info.getName());
- collected.add(info);
-
- if (info.getDependencies() != null) {
- for (SharedLibraryInfo dep : info.getDependencies()) {
- findSharedLibrariesRecursive(dep, collected, collectedNames);
- }
- }
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/pm/SharedLibraryUtils.java b/services/core/java/com/android/server/pm/SharedLibraryUtils.java
new file mode 100644
index 0000000..274870d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SharedLibraryUtils.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.SharedLibraryInfo;
+
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+final class SharedLibraryUtils {
+
+ /**
+ * Returns false if the adding shared library already exists in the map and so could not be
+ * added.
+ */
+ public static boolean addSharedLibraryToPackageVersionMap(
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
+ SharedLibraryInfo library) {
+ final String name = library.getName();
+ if (target.containsKey(name)) {
+ if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
+ // We've already added this non-version-specific library to the map.
+ return false;
+ } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
+ // We've already added this version of a version-specific library to the map.
+ return false;
+ }
+ } else {
+ target.put(name, new WatchedLongSparseArray<>());
+ }
+ target.get(name).put(library.getLongVersion(), library);
+ return true;
+ }
+
+ @Nullable
+ public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+ @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
+ if (newLibraries != null) {
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
+ SharedLibraryInfo info = null;
+ if (versionedLib != null) {
+ info = versionedLib.get(version);
+ }
+ if (info != null) {
+ return info;
+ }
+ }
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
+ if (versionedLib == null) {
+ return null;
+ }
+ return versionedLib.get(version);
+ }
+
+ public static List<SharedLibraryInfo> findSharedLibraries(PackageStateInternal pkgSetting) {
+ if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
+ ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
+ Set<String> collectedNames = new HashSet<>();
+ for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
+ findSharedLibrariesRecursive(info, retValue, collectedNames);
+ }
+ return retValue;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
+ ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
+ if (!collectedNames.contains(info.getName())) {
+ collectedNames.add(info.getName());
+ collected.add(info);
+
+ if (info.getDependencies() != null) {
+ for (SharedLibraryInfo dep : info.getDependencies()) {
+ findSharedLibrariesRecursive(dep, collected, collectedNames);
+ }
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index b15e495..af524db 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -217,6 +217,11 @@
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.NEARBY_WIFI_DEVICES);
}
+ private static final Set<String> NOTIFICATION_PERMISSIONS = new ArraySet<>();
+ static {
+ NOTIFICATION_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS);
+ }
+
private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
private static final String ACTION_TRACK = "com.android.fitness.TRACK";
@@ -378,18 +383,43 @@
grantPermissionsToSysComponentsAndPrivApps(pm, userId);
grantDefaultSystemHandlerPermissions(pm, userId);
+ grantSignatureAppsNotificationPermissions(pm, userId);
grantDefaultPermissionExceptions(pm, userId);
// Apply delayed state
pm.apply();
}
+ private void grantSignatureAppsNotificationPermissions(PackageManagerWrapper pm, int userId) {
+ Log.i(TAG, "Granting Notification permissions to platform signature apps for user "
+ + userId);
+ List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser(
+ DEFAULT_PACKAGE_INFO_QUERY_FLAGS, UserHandle.USER_SYSTEM);
+ for (PackageInfo pkg : packages) {
+ if (pkg == null || !pkg.applicationInfo.isSystemApp()
+ || !pkg.applicationInfo.isSignedWithPlatformKey()) {
+ continue;
+ }
+ grantRuntimePermissionsForSystemPackage(pm, userId, pkg, NOTIFICATION_PERMISSIONS);
+ }
+
+ }
+
private void grantRuntimePermissionsForSystemPackage(PackageManagerWrapper pm,
int userId, PackageInfo pkg) {
+ grantRuntimePermissionsForSystemPackage(pm, userId, pkg, null);
+ }
+
+ private void grantRuntimePermissionsForSystemPackage(PackageManagerWrapper pm,
+ int userId, PackageInfo pkg, Set<String> filterPermissions) {
+ if (ArrayUtils.isEmpty(pkg.requestedPermissions)) {
+ return;
+ }
Set<String> permissions = new ArraySet<>();
for (String permission : pkg.requestedPermissions) {
final PermissionInfo perm = pm.getPermissionInfo(permission);
- if (perm == null) {
+ if (perm == null
+ || (filterPermissions != null && !filterPermissions.contains(permission))) {
continue;
}
if (perm.isRuntime()) {
@@ -547,23 +577,31 @@
String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
+ // PermissionController
+ grantSystemFixedPermissionsToSystemPackage(pm,
+ mContext.getPackageManager().getPermissionControllerPackageName(), userId,
+ NOTIFICATION_PERMISSIONS);
+
// Installer
grantSystemFixedPermissionsToSystemPackage(pm,
ArrayUtils.firstOrNull(getKnownPackages(
PackageManagerInternal.PACKAGE_INSTALLER, userId)),
- userId, STORAGE_PERMISSIONS);
+ userId, STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Verifier
final String verifier = ArrayUtils.firstOrNull(getKnownPackages(
PackageManagerInternal.PACKAGE_VERIFIER, userId));
grantSystemFixedPermissionsToSystemPackage(pm, verifier, userId, STORAGE_PERMISSIONS);
- grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
+ grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
// SetupWizard
final String setupWizardPackage = ArrayUtils.firstOrNull(getKnownPackages(
PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId));
grantPermissionsToSystemPackage(pm, setupWizardPackage, userId, PHONE_PERMISSIONS,
CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
+ grantSystemFixedPermissionsToSystemPackage(pm, setupWizardPackage, userId,
+ NOTIFICATION_PERMISSIONS);
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)
|| mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE,
0)) {
@@ -585,12 +623,12 @@
// Media provider
grantSystemFixedPermissionsToSystemPackage(pm,
getDefaultProviderAuthorityPackage(MediaStore.AUTHORITY, userId), userId,
- STORAGE_PERMISSIONS);
+ STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Downloads provider
grantSystemFixedPermissionsToSystemPackage(pm,
getDefaultProviderAuthorityPackage("downloads", userId), userId,
- STORAGE_PERMISSIONS);
+ STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Downloads UI
grantSystemFixedPermissionsToSystemPackage(pm,
@@ -649,7 +687,7 @@
// Cell Broadcast Receiver
grantSystemFixedPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId),
- userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
+ userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Carrier Provisioning Service
grantPermissionsToSystemPackage(pm,
@@ -661,7 +699,7 @@
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackageForCategory(pm,
Intent.CATEGORY_APP_CALENDAR, userId),
- userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS);
+ userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Calendar provider
String calendarProvider =
@@ -762,7 +800,8 @@
grantPermissionsToSystemPackage(pm, packageName, userId,
CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
- SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
+ SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
ALWAYS_LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
}
@@ -791,7 +830,7 @@
.addCategory(Intent.CATEGORY_LAUNCHER_APP);
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, homeIntent, userId), userId,
- ALWAYS_LOCATION_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Watches
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
@@ -816,7 +855,7 @@
// Print Spooler
grantSystemFixedPermissionsToSystemPackage(pm, PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
- userId, ALWAYS_LOCATION_PERMISSIONS);
+ userId, ALWAYS_LOCATION_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// EmergencyInfo
grantSystemFixedPermissionsToSystemPackage(pm,
@@ -920,12 +959,13 @@
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
if (isPhonePermFixed) {
grantSystemFixedPermissionsToSystemPackage(pm, dialerPackage, userId,
- PHONE_PERMISSIONS);
+ PHONE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
} else {
grantPermissionsToSystemPackage(pm, dialerPackage, userId, PHONE_PERMISSIONS);
}
grantPermissionsToSystemPackage(pm, dialerPackage, userId,
- CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+ CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
boolean isAndroidAutomotive =
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
if (isAndroidAutomotive) {
@@ -937,7 +977,8 @@
String smsPackage, int userId) {
grantPermissionsToSystemPackage(pm, smsPackage, userId,
PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS,
- STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+ STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
}
private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(PackageManagerWrapper pm,
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index cdab91b..411f3dc 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -136,7 +136,8 @@
@Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName);
/** @see com.android.internal.statusbar.IStatusBar#showTransient */
- void showTransient(int displayId, @InternalInsetsType int[] types);
+ void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar);
/** @see com.android.internal.statusbar.IStatusBar#abortTransient */
void abortTransient(int displayId, @InternalInsetsType int[] types);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 54ec884..17f5566 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -567,11 +567,12 @@
}
@Override
- public void showTransient(int displayId, @InternalInsetsType int[] types) {
+ public void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar) {
getUiState(displayId).showTransient(types);
if (mBar != null) {
try {
- mBar.showTransient(displayId, types);
+ mBar.showTransient(displayId, types, isGestureOnSystemBar);
} catch (RemoteException ex) { }
}
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 4576957..bd8d13b 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -20,8 +20,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.server.VcnManagementService.LOCAL_LOG;
@@ -29,10 +29,10 @@
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnCellUnderlyingNetworkPriority;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnManager;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
-import android.net.vcn.VcnWifiUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.telephony.SubscriptionManager;
@@ -76,7 +76,7 @@
public static int calculatePriorityClass(
VcnContext vcnContext,
UnderlyingNetworkRecord networkRecord,
- LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+ LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -94,7 +94,7 @@
}
int priorityIndex = 0;
- for (VcnUnderlyingNetworkPriority nwPriority : underlyingNetworkPriorities) {
+ for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkPriorities) {
if (checkMatchesPriorityRule(
vcnContext,
nwPriority,
@@ -113,7 +113,7 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static boolean checkMatchesPriorityRule(
VcnContext vcnContext,
- VcnUnderlyingNetworkPriority networkPriority,
+ VcnUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
@@ -130,32 +130,32 @@
return true;
}
- if (networkPriority instanceof VcnWifiUnderlyingNetworkPriority) {
+ if (networkPriority instanceof VcnWifiUnderlyingNetworkTemplate) {
return checkMatchesWifiPriorityRule(
- (VcnWifiUnderlyingNetworkPriority) networkPriority,
+ (VcnWifiUnderlyingNetworkTemplate) networkPriority,
networkRecord,
currentlySelected,
carrierConfig);
}
- if (networkPriority instanceof VcnCellUnderlyingNetworkPriority) {
+ if (networkPriority instanceof VcnCellUnderlyingNetworkTemplate) {
return checkMatchesCellPriorityRule(
vcnContext,
- (VcnCellUnderlyingNetworkPriority) networkPriority,
+ (VcnCellUnderlyingNetworkTemplate) networkPriority,
networkRecord,
subscriptionGroup,
snapshot);
}
logWtf(
- "Got unknown VcnUnderlyingNetworkPriority class: "
+ "Got unknown VcnUnderlyingNetworkTemplate class: "
+ networkPriority.getClass().getSimpleName());
return false;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static boolean checkMatchesWifiPriorityRule(
- VcnWifiUnderlyingNetworkPriority networkPriority,
+ VcnWifiUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
UnderlyingNetworkRecord currentlySelected,
PersistableBundle carrierConfig) {
@@ -202,7 +202,7 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static boolean checkMatchesCellPriorityRule(
VcnContext vcnContext,
- VcnCellUnderlyingNetworkPriority networkPriority,
+ VcnCellUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot) {
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index cd124ee..ca2e449 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -33,7 +33,7 @@
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnGatewayConnectionConfig;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
@@ -498,10 +498,10 @@
pw.println(
"Currently selected: " + (mCurrentRecord == null ? null : mCurrentRecord.network));
- pw.println("VcnUnderlyingNetworkPriority list:");
+ pw.println("VcnUnderlyingNetworkTemplate list:");
pw.increaseIndent();
int index = 0;
- for (VcnUnderlyingNetworkPriority priority :
+ for (VcnUnderlyingNetworkTemplate priority :
mConnectionConfig.getVcnUnderlyingNetworkPriorities()) {
pw.println("Priority index: " + index);
priority.dump(pw);
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index 27ba854..df2f0d5 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -21,7 +21,7 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -77,7 +77,7 @@
static Comparator<UnderlyingNetworkRecord> getComparator(
VcnContext vcnContext,
- LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+ LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -133,7 +133,7 @@
void dump(
VcnContext vcnContext,
IndentingPrintWriter pw,
- LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+ LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 49a51d5..f506a9f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -405,7 +405,7 @@
WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
? getStatusBar() : getNavigationBar();
if (targetBar != null) {
- requestTransientBars(targetBar);
+ requestTransientBars(targetBar, true /* isGestureOnSystemBar */);
}
}
break;
@@ -448,26 +448,25 @@
// TODO(b/181821798) Migrate SystemGesturesPointerEventListener to use window context.
mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
new SystemGesturesPointerEventListener.Callbacks() {
+
@Override
public void onSwipeFromTop() {
synchronized (mLock) {
- if (mStatusBar != null) {
- requestTransientBars(mStatusBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_TOP,
- false /* allowForAllPositions */);
+ final WindowState bar = mStatusBar != null
+ ? mStatusBar
+ : findAltBarMatchingPosition(ALT_BAR_TOP);
+ requestTransientBars(bar, true /* isGestureOnSystemBar */);
}
}
@Override
public void onSwipeFromBottom() {
synchronized (mLock) {
- if (mNavigationBar != null
- && mNavigationBarPosition == NAV_BAR_BOTTOM) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM,
- false /* allowForAllPositions */);
+ final WindowState bar = mNavigationBar != null
+ && mNavigationBarPosition == NAV_BAR_BOTTOM
+ ? mNavigationBar
+ : findAltBarMatchingPosition(ALT_BAR_BOTTOM);
+ requestTransientBars(bar, true /* isGestureOnSystemBar */);
}
}
@@ -477,13 +476,8 @@
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
- final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
- !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
- if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_RIGHT
- || allowSideSwipe)) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT, allowSideSwipe);
+ requestTransientBarsForSideSwipe(excludedRegion, NAV_BAR_RIGHT,
+ ALT_BAR_RIGHT);
}
excludedRegion.recycle();
}
@@ -494,17 +488,33 @@
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
- final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
- !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
- if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_LEFT
- || allowSideSwipe)) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_LEFT, allowSideSwipe);
+ requestTransientBarsForSideSwipe(excludedRegion, NAV_BAR_LEFT,
+ ALT_BAR_LEFT);
}
excludedRegion.recycle();
}
+ private void requestTransientBarsForSideSwipe(Region excludedRegion,
+ int navBarSide, int altBarSide) {
+ final WindowState barMatchingSide = mNavigationBar != null
+ && mNavigationBarPosition == navBarSide
+ ? mNavigationBar
+ : findAltBarMatchingPosition(altBarSide);
+ final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
+ !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
+ if (barMatchingSide == null && !allowSideSwipe) {
+ return;
+ }
+
+ // Request transient bars on the matching bar, or any bar if we always allow
+ // side swipes to show the bars
+ final boolean isGestureOnSystemBar = barMatchingSide != null;
+ final WindowState bar = barMatchingSide != null
+ ? barMatchingSide
+ : findTransientNavOrAltBar();
+ requestTransientBars(bar, isGestureOnSystemBar);
+ }
+
@Override
public void onFling(int duration) {
if (mService.mPowerManagerInternal != null) {
@@ -654,21 +664,39 @@
mHandler.post(mGestureNavigationSettingsObserver::register);
}
- private void checkAltBarSwipeForTransientBars(@WindowManagerPolicy.AltBarPosition int pos,
- boolean allowForAllPositions) {
- if (mStatusBarAlt != null && (mStatusBarAltPosition == pos || allowForAllPositions)) {
- requestTransientBars(mStatusBarAlt);
+ /**
+ * Returns the first non-null alt bar window matching the given position.
+ */
+ private WindowState findAltBarMatchingPosition(@WindowManagerPolicy.AltBarPosition int pos) {
+ if (mStatusBarAlt != null && mStatusBarAltPosition == pos) {
+ return mStatusBarAlt;
}
- if (mNavigationBarAlt != null
- && (mNavigationBarAltPosition == pos || allowForAllPositions)) {
- requestTransientBars(mNavigationBarAlt);
+ if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) {
+ return mNavigationBarAlt;
}
- if (mClimateBarAlt != null && (mClimateBarAltPosition == pos || allowForAllPositions)) {
- requestTransientBars(mClimateBarAlt);
+ if (mClimateBarAlt != null && mClimateBarAltPosition == pos) {
+ return mClimateBarAlt;
}
- if (mExtraNavBarAlt != null && (mExtraNavBarAltPosition == pos || allowForAllPositions)) {
- requestTransientBars(mExtraNavBarAlt);
+ if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) {
+ return mExtraNavBarAlt;
}
+ return null;
+ }
+
+ /**
+ * Finds the first non-null nav bar to request transient for.
+ */
+ private WindowState findTransientNavOrAltBar() {
+ if (mNavigationBar != null) {
+ return mNavigationBar;
+ }
+ if (mNavigationBarAlt != null) {
+ return mNavigationBarAlt;
+ }
+ if (mExtraNavBarAlt != null) {
+ return mExtraNavBarAlt;
+ }
+ return null;
}
void systemReady() {
@@ -2170,8 +2198,8 @@
updateSystemBarAttributes();
}
- private void requestTransientBars(WindowState swipeTarget) {
- if (!mService.mPolicy.isUserSetupComplete()) {
+ private void requestTransientBars(WindowState swipeTarget, boolean isGestureOnSystemBar) {
+ if (swipeTarget == null || !mService.mPolicy.isUserSetupComplete()) {
// Swipe-up for navigation bar is disabled during setup
return;
}
@@ -2207,7 +2235,8 @@
if (controlTarget.canShowTransient()) {
// Show transient bars if they are hidden; restore position if they are visible.
- mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE);
+ mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE,
+ isGestureOnSystemBar);
controlTarget.showInsets(restorePositionTypes, false);
} else {
// Restore visibilities and positions of system bars.
@@ -2423,7 +2452,8 @@
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
if (!isNavBarEmpty(disableFlags)) {
- mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC);
+ mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC,
+ true /* isGestureOnSystemBar */);
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index dff7ff9..9326a2e 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -160,7 +160,7 @@
return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
}
- void showTransient(@InternalInsetsType int[] types) {
+ void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) {
boolean changed = false;
for (int i = types.length - 1; i >= 0; i--) {
final @InternalInsetsType int type = types[i];
@@ -177,8 +177,8 @@
StatusBarManagerInternal statusBarManagerInternal =
mPolicy.getStatusBarManagerInternal();
if (statusBarManagerInternal != null) {
- statusBarManagerInternal.showTransient(
- mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+ statusBarManagerInternal.showTransient(mDisplayContent.getDisplayId(),
+ mShowingTransientTypes.toArray(), isGestureOnSystemBar);
}
updateBarControlTarget(mFocusedWin);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 535bbb7..3f2e975 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2051,19 +2051,25 @@
try {
final Task task = r.getTask();
- final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
- // This will change the root pinned task's windowing mode to its original mode, ensuring
- // we only have one root task that is in pinned mode.
+ // If the activity in current PIP task needs to be moved back to the parent Task of next
+ // PIP activity, we can't use that parent Task as the next PIP Task.
+ // Because we need to start the Shell transition from the root Task, we delay to dismiss
+ // the current PIP task until root Task is ready.
+ boolean origPipWillBeMovedToTask = false;
+ final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
if (rootPinnedTask != null) {
- rootPinnedTask.dismissPip();
+ final ActivityRecord topPipActivity = rootPinnedTask.getTopMostActivity();
+ if (topPipActivity != null && topPipActivity.getLastParentBeforePip() == task) {
+ origPipWillBeMovedToTask = true;
+ }
}
// Set a transition to ensure that we don't immediately try and update the visibility
// of the activity entering PIP
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
- final boolean singleActivity = task.getChildCount() == 1;
+ final boolean singleActivity = task.getChildCount() == 1 && !origPipWillBeMovedToTask;
final Task rootTask;
if (singleActivity) {
rootTask = task;
@@ -2117,7 +2123,7 @@
final ActivityRecord oldTopActivity = task.getTopMostActivity();
if (oldTopActivity != null && oldTopActivity.isState(STOPPED)
&& task.getDisplayContent().mAppTransition.containsTransitRequest(
- TRANSIT_TO_BACK)) {
+ TRANSIT_TO_BACK) && !origPipWillBeMovedToTask) {
task.getDisplayContent().mClosingApps.add(oldTopActivity);
oldTopActivity.mRequestForceTransition = true;
}
@@ -2133,6 +2139,13 @@
}
rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
+ // This will change the root pinned task's windowing mode to its original mode, ensuring
+ // we only have one root task that is in pinned mode.
+ if (rootPinnedTask != null) {
+ rootTask.mTransitionController.collect(rootPinnedTask);
+ rootPinnedTask.dismissPip();
+ }
+
// Defer the windowing mode change until after the transition to prevent the activity
// from doing work and changing the activity visuals while animating
// TODO(task-org): Figure-out more structured way to do this long term.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 659c771..7617726 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -478,7 +478,6 @@
// to layout without loading all the task snapshots
final PersistedTaskSnapshotData mLastTaskSnapshotData;
- private Dimmer mDimmer = new Dimmer(this);
private final Rect mTmpDimBoundsRect = new Rect();
/** @see #setCanAffectSystemUiFlags */
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 8299cea..d133ca9 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -161,6 +161,8 @@
*/
int mMinHeight;
+ Dimmer mDimmer = new Dimmer(this);
+
/** This task fragment will be removed when the cleanup of its children are done. */
private boolean mIsRemovalRequested;
@@ -2349,6 +2351,34 @@
}
@Override
+ Dimmer getDimmer() {
+ // If the window is in an embedded TaskFragment, we want to dim at the TaskFragment.
+ if (asTask() == null) {
+ return mDimmer;
+ }
+
+ return super.getDimmer();
+ }
+
+ @Override
+ void prepareSurfaces() {
+ if (asTask() != null) {
+ super.prepareSurfaces();
+ return;
+ }
+
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+
+ // Bounds need to be relative, as the dim layer is a child.
+ final Rect dimBounds = getBounds();
+ dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+ if (mDimmer.updateDims(getPendingTransaction(), dimBounds)) {
+ scheduleAnimation();
+ }
+ }
+
+ @Override
boolean canBeAnimationTarget() {
return true;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index df8953c..db8da11 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -258,6 +258,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManager.UserRestrictionSource;
import android.os.storage.StorageManager;
import android.permission.AdminPermissionControlParams;
import android.permission.IPermissionManager;
@@ -286,6 +287,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.DebugUtils;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Pair;
@@ -13271,14 +13273,29 @@
try {
List<UserManager.EnforcingUser> sources = mUserManager
.getUserRestrictionSources(restriction, UserHandle.of(userId));
- if (sources == null || sources.isEmpty()) {
+ if (sources == null) {
// The restriction is not enforced.
return null;
- } else if (sources.size() > 1) {
+ }
+ int sizeBefore = sources.size();
+ if (sizeBefore > 1) {
+ Slogf.d(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): "
+ + "%d sources found, excluding those set by UserManager",
+ userId, restriction, sizeBefore);
+ sources = getDevicePolicySources(sources);
+ }
+ if (sources.isEmpty()) {
+ // The restriction is not enforced (or is just enforced by the system)
+ return null;
+ }
+
+ if (sources.size() > 1) {
// In this case, we'll show an admin support dialog that does not
// specify the admin.
// TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
// the admin for the calling user.
+ Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple "
+ + "sources for restriction %s on user %d", restriction, userId);
result = new Bundle();
result.putInt(Intent.EXTRA_USER_ID, userId);
return result;
@@ -13324,6 +13341,32 @@
}
/**
+ * Excludes restrictions imposed by UserManager.
+ */
+ private List<UserManager.EnforcingUser> getDevicePolicySources(
+ List<UserManager.EnforcingUser> sources) {
+ int sizeBefore = sources.size();
+ List<UserManager.EnforcingUser> realSources = new ArrayList<>(sizeBefore);
+ for (int i = 0; i < sizeBefore; i++) {
+ UserManager.EnforcingUser source = sources.get(i);
+ int type = source.getUserRestrictionSource();
+ if (type != UserManager.RESTRICTION_SOURCE_PROFILE_OWNER
+ && type != UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
+ // TODO(b/128928355): add unit test
+ Slogf.d(LOG_TAG, "excluding source of type %s at index %d",
+ userRestrictionSourceToString(type), i);
+ continue;
+ }
+ realSources.add(source);
+ }
+ return realSources;
+ }
+
+ private static String userRestrictionSourceToString(@UserRestrictionSource int source) {
+ return DebugUtils.flagsToString(UserManager.class, "RESTRICTION_", source);
+ }
+
+ /**
* @param restriction The restriction enforced by admin. It could be any user restriction or
* policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
* {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 44a8b30..04a6eee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -74,6 +74,7 @@
import com.android.server.testutils.nullable
import com.android.server.testutils.whenever
import com.android.server.utils.WatchedArrayMap
+import libcore.util.HexEncoding
import org.junit.Assert
import org.junit.rules.TestRule
import org.junit.runner.Description
@@ -140,6 +141,7 @@
.mockStatic(EventLog::class.java)
.mockStatic(LocalServices::class.java)
.mockStatic(DeviceConfig::class.java)
+ .mockStatic(HexEncoding::class.java)
.apply(withSession)
session = apply.startMocking()
whenever(mocks.settings.insertPackageSettingLPw(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
new file mode 100644
index 0000000..6de12cb9
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm
+
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.SharedLibraryInfo
+import android.content.pm.VersionedPackage
+import android.os.Build
+import android.os.storage.StorageManager
+import android.util.ArrayMap
+import android.util.PackageUtils
+import com.android.server.SystemConfig.SharedLibraryEntry
+import com.android.server.compat.PlatformCompat
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.parsing.pkg.PackageImpl
+import com.android.server.pm.parsing.pkg.ParsedPackage
+import com.android.server.testutils.any
+import com.android.server.testutils.eq
+import com.android.server.testutils.mock
+import com.android.server.testutils.nullable
+import com.android.server.testutils.spy
+import com.android.server.testutils.whenever
+import com.android.server.utils.WatchedLongSparseArray
+import com.google.common.truth.Truth.assertThat
+import libcore.util.HexEncoding
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.io.File
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@RunWith(JUnit4::class)
+class SharedLibrariesImplTest {
+
+ companion object {
+ const val TEST_LIB_NAME = "test.lib"
+ const val TEST_LIB_PACKAGE_NAME = "com.android.lib.test"
+ const val BUILTIN_LIB_NAME = "builtin.lib"
+ const val STATIC_LIB_NAME = "static.lib"
+ const val STATIC_LIB_VERSION = 7L
+ const val STATIC_LIB_PACKAGE_NAME = "com.android.lib.static.provider"
+ const val DYNAMIC_LIB_NAME = "dynamic.lib"
+ const val DYNAMIC_LIB_PACKAGE_NAME = "com.android.lib.dynamic.provider"
+ const val CONSUMER_PACKAGE_NAME = "com.android.lib.consumer"
+ const val VERSION_UNDEFINED = SharedLibraryInfo.VERSION_UNDEFINED.toLong()
+ }
+
+ @Rule
+ @JvmField
+ val mRule = MockSystemRule()
+
+ private val mExistingPackages: ArrayMap<String, AndroidPackage> = ArrayMap()
+ private val mExistingSettings: MutableMap<String, PackageSetting> = mutableMapOf()
+
+ private lateinit var mSharedLibrariesImpl: SharedLibrariesImpl
+ private lateinit var mPms: PackageManagerService
+ private lateinit var mSettings: Settings
+
+ @Mock
+ private lateinit var mDeletePackageHelper: DeletePackageHelper
+ @Mock
+ private lateinit var mStorageManager: StorageManager
+ @Mock
+ private lateinit var mFile: File
+ @Mock
+ private lateinit var mPlatformCompat: PlatformCompat
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mRule.system().stageNominalSystemState()
+ addExistingPackages()
+
+ val testParams = PackageManagerServiceTestParams().apply {
+ packages = mExistingPackages
+ }
+ mPms = spy(PackageManagerService(mRule.mocks().injector, testParams))
+ mSettings = mRule.mocks().injector.settings
+ mSharedLibrariesImpl = SharedLibrariesImpl(mPms, mRule.mocks().injector)
+ mSharedLibrariesImpl.setDeletePackageHelper(mDeletePackageHelper)
+ addExistingSharedLibraries()
+
+ whenever(mSettings.getPackageLPr(any())) { mExistingSettings[arguments[0]] }
+ whenever(mRule.mocks().injector.getSystemService(StorageManager::class.java))
+ .thenReturn(mStorageManager)
+ whenever(mStorageManager.findPathForUuid(nullable())).thenReturn(mFile)
+ doAnswer { it.arguments[0] }.`when`(mPms).resolveInternalPackageNameLPr(any(), any())
+ whenever(mDeletePackageHelper.deletePackageX(any(), any(), any(), any(), any()))
+ .thenReturn(PackageManager.DELETE_SUCCEEDED)
+ whenever(mRule.mocks().injector.compatibility).thenReturn(mPlatformCompat)
+ wheneverStatic { HexEncoding.decode(STATIC_LIB_NAME, false) }
+ .thenReturn(PackageUtils.computeSha256DigestBytes(
+ mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+ .pkg.signingDetails.signatures!![0].toByteArray()))
+ }
+
+ @Test
+ fun snapshot_shouldSealed() {
+ val builtinLibs = mSharedLibrariesImpl.snapshot().all[BUILTIN_LIB_NAME]
+ assertThat(builtinLibs).isNotNull()
+
+ assertFailsWith(IllegalStateException::class) {
+ mSharedLibrariesImpl.snapshot().all[BUILTIN_LIB_NAME] = WatchedLongSparseArray()
+ }
+ assertFailsWith(IllegalStateException::class) {
+ builtinLibs!!.put(VERSION_UNDEFINED, libOfBuiltin(BUILTIN_LIB_NAME))
+ }
+ }
+
+ @Test
+ fun addBuiltInSharedLibrary() {
+ mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(libEntry(TEST_LIB_NAME))
+
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(TEST_LIB_NAME)).isNotNull()
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfo(TEST_LIB_NAME, VERSION_UNDEFINED))
+ .isNotNull()
+ }
+
+ @Test
+ fun addBuiltInSharedLibrary_withDuplicateLibName() {
+ val duplicate = libEntry(BUILTIN_LIB_NAME, "duplicate.path")
+ mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(duplicate)
+ val sharedLibInfo = mSharedLibrariesImpl
+ .getSharedLibraryInfo(BUILTIN_LIB_NAME, VERSION_UNDEFINED)
+
+ assertThat(sharedLibInfo).isNotNull()
+ assertThat(sharedLibInfo!!.path).isNotEqualTo(duplicate.filename)
+ }
+
+ @Test
+ fun commitSharedLibraryInfo_withStaticSharedLib() {
+ val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L)
+ mSharedLibrariesImpl.commitSharedLibraryInfoLPw(testInfo)
+ val sharedLibInfos = mSharedLibrariesImpl
+ .getStaticLibraryInfos(testInfo.declaringPackage.packageName)
+
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(TEST_LIB_NAME))
+ .isNotNull()
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfo(testInfo.name, testInfo.longVersion))
+ .isNotNull()
+ assertThat(sharedLibInfos).isNotNull()
+ assertThat(sharedLibInfos.get(testInfo.longVersion)).isNotNull()
+ }
+
+ @Test
+ fun removeSharedLibrary() {
+ doAnswer { mutableListOf(VersionedPackage(CONSUMER_PACKAGE_NAME, 1L)) }.`when`(mPms)
+ .getPackagesUsingSharedLibrary(any(), any(), any(), any())
+ val staticInfo = mSharedLibrariesImpl
+ .getSharedLibraryInfo(STATIC_LIB_NAME, STATIC_LIB_VERSION)!!
+
+ mSharedLibrariesImpl.removeSharedLibraryLPw(STATIC_LIB_NAME, STATIC_LIB_VERSION)
+
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(STATIC_LIB_NAME)).isNull()
+ assertThat(mSharedLibrariesImpl
+ .getStaticLibraryInfos(staticInfo.declaringPackage.packageName)).isNull()
+ verify(mExistingSettings[CONSUMER_PACKAGE_NAME]!!)
+ .setOverlayPathsForLibrary(any(), nullable(), any())
+ }
+
+ @Test
+ fun pruneUnusedStaticSharedLibraries() {
+ mSharedLibrariesImpl.pruneUnusedStaticSharedLibraries(Long.MAX_VALUE, 0)
+
+ verify(mDeletePackageHelper)
+ .deletePackageX(eq(STATIC_LIB_PACKAGE_NAME), any(), any(), any(), any())
+ }
+
+ @Test
+ fun getLatestSharedLibraVersion() {
+ val newLibSetting = addPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
+ staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
+
+ val latestInfo = mSharedLibrariesImpl.getLatestSharedLibraVersionLPr(newLibSetting.pkg)!!
+
+ assertThat(latestInfo).isNotNull()
+ assertThat(latestInfo.name).isEqualTo(STATIC_LIB_NAME)
+ assertThat(latestInfo.longVersion).isEqualTo(STATIC_LIB_VERSION)
+ }
+
+ @Test
+ fun getStaticSharedLibLatestVersionSetting() {
+ val pair = createBasicAndroidPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
+ staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
+ val parsedPackage = pair.second as ParsedPackage
+ val scanRequest = ScanRequest(parsedPackage, null, null, null,
+ null, null, null, 0, 0, false, null, null)
+ val scanResult = ScanResult(scanRequest, true, null, null, false, 0, null, null, null)
+
+ val latestInfoSetting =
+ mSharedLibrariesImpl.getStaticSharedLibLatestVersionSetting(scanResult)!!
+
+ assertThat(latestInfoSetting).isNotNull()
+ assertThat(latestInfoSetting.packageName).isEqualTo(STATIC_LIB_PACKAGE_NAME)
+ }
+
+ @Test
+ fun updateSharedLibraries_withDynamicLibPackage() {
+ val testPackageSetting = mExistingSettings[DYNAMIC_LIB_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+ mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+ null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+ }
+
+ @Test
+ fun updateSharedLibraries_withStaticLibPackage() {
+ val testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+ mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+ null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+ }
+
+ @Test
+ fun updateSharedLibraries_withConsumerPackage() {
+ val testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+ mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+ null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
+ }
+
+ @Test
+ fun updateAllSharedLibraries() {
+ mExistingSettings.forEach {
+ assertThat(it.value.usesLibraryFiles).isEmpty()
+ }
+
+ mSharedLibrariesImpl.updateAllSharedLibrariesLPw(
+ null /* updatedPkg */, null /* updatedPkgSetting */, mExistingPackages)
+
+ var testPackageSetting = mExistingSettings[DYNAMIC_LIB_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+
+ testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+
+ testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(3)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
+ }
+
+ @Test
+ fun getAllowedSharedLibInfos_withStaticSharedLibInfo() {
+ val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L)
+ val scanResult = ScanResult(mock(), true, null, null,
+ false, 0, null, testInfo, null)
+
+ val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(scanResult)
+
+ assertThat(allowedInfos).hasSize(1)
+ assertThat(allowedInfos[0].name).isEqualTo(TEST_LIB_NAME)
+ }
+
+ @Test
+ fun getAllowedSharedLibInfos_withDynamicSharedLibInfo() {
+ val testInfo = libOfDynamic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME)
+ val pair = createBasicAndroidPackage(
+ TEST_LIB_PACKAGE_NAME, 10L, libraries = arrayOf(TEST_LIB_NAME))
+ val parsedPackage = pair.second.apply {
+ isSystem = true
+ } as ParsedPackage
+ val packageSetting = mRule.system()
+ .createBasicSettingBuilder(pair.first.parentFile, parsedPackage.hideAsFinal())
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM).build()
+ val scanRequest = ScanRequest(parsedPackage, null, null, null,
+ null, null, null, 0, 0, false, null, null)
+ val scanResult = ScanResult(scanRequest, true, packageSetting, null,
+ false, 0, null, null, listOf(testInfo))
+
+ val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(scanResult)
+
+ assertThat(allowedInfos).hasSize(1)
+ assertThat(allowedInfos[0].name).isEqualTo(TEST_LIB_NAME)
+ }
+
+ private fun addExistingPackages() {
+ // add a dynamic shared library that is using the builtin library
+ addPackage(DYNAMIC_LIB_PACKAGE_NAME, 1L,
+ libraries = arrayOf(DYNAMIC_LIB_NAME),
+ usesLibraries = arrayOf(BUILTIN_LIB_NAME))
+
+ // add a static shared library v7 that is using the dynamic shared library
+ addPackage(STATIC_LIB_PACKAGE_NAME, STATIC_LIB_VERSION,
+ staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = STATIC_LIB_VERSION,
+ usesLibraries = arrayOf(DYNAMIC_LIB_NAME))
+
+ // add a consumer package that is using the dynamic and static shared library
+ addPackage(CONSUMER_PACKAGE_NAME, 1L,
+ usesLibraries = arrayOf(DYNAMIC_LIB_NAME),
+ usesStaticLibraries = arrayOf(STATIC_LIB_NAME),
+ usesStaticLibraryVersions = arrayOf(STATIC_LIB_VERSION))
+ }
+
+ private fun addExistingSharedLibraries() {
+ mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(libEntry(BUILTIN_LIB_NAME))
+ mSharedLibrariesImpl.commitSharedLibraryInfoLPw(
+ libOfDynamic(DYNAMIC_LIB_PACKAGE_NAME, DYNAMIC_LIB_NAME))
+ mSharedLibrariesImpl.commitSharedLibraryInfoLPw(
+ libOfStatic(STATIC_LIB_PACKAGE_NAME, STATIC_LIB_NAME, STATIC_LIB_VERSION))
+ }
+
+ private fun addPackage(
+ packageName: String,
+ version: Long,
+ libraries: Array<String>? = null,
+ staticLibrary: String? = null,
+ staticLibraryVersion: Long = 0L,
+ usesLibraries: Array<String>? = null,
+ usesStaticLibraries: Array<String>? = null,
+ usesStaticLibraryVersions: Array<Long>? = null
+ ): PackageSetting {
+ val pair = createBasicAndroidPackage(packageName, version, libraries, staticLibrary,
+ staticLibraryVersion, usesLibraries, usesStaticLibraries, usesStaticLibraryVersions)
+ val apkPath = pair.first
+ val parsingPackage = pair.second
+ val spyPkg = spy((parsingPackage as ParsedPackage).hideAsFinal())
+ mExistingPackages[packageName] = spyPkg
+
+ val spyPackageSetting = spy(mRule.system()
+ .createBasicSettingBuilder(apkPath.parentFile, spyPkg).build())
+ mExistingSettings[spyPackageSetting.packageName] = spyPackageSetting
+
+ return spyPackageSetting
+ }
+
+ private fun createBasicAndroidPackage(
+ packageName: String,
+ version: Long,
+ libraries: Array<String>? = null,
+ staticLibrary: String? = null,
+ staticLibraryVersion: Long = 0L,
+ usesLibraries: Array<String>? = null,
+ usesStaticLibraries: Array<String>? = null,
+ usesStaticLibraryVersions: Array<Long>? = null
+ ): Pair<File, PackageImpl> {
+ assertFalse { libraries != null && staticLibrary != null }
+ assertTrue { (usesStaticLibraries?.size ?: -1) == (usesStaticLibraryVersions?.size ?: -1) }
+
+ val pair = mRule.system()
+ .createBasicAndroidPackage(mRule.system().dataAppDirectory, packageName, version)
+ pair.second.apply {
+ setTargetSdkVersion(Build.VERSION_CODES.S)
+ libraries?.forEach { addLibraryName(it) }
+ staticLibrary?.let {
+ setStaticSharedLibName(it)
+ setStaticSharedLibVersion(staticLibraryVersion)
+ setStaticSharedLibrary(true)
+ }
+ usesLibraries?.forEach { addUsesLibrary(it) }
+ usesStaticLibraries?.forEachIndexed { index, s ->
+ addUsesStaticLibrary(s,
+ usesStaticLibraryVersions?.get(index) ?: 0L,
+ arrayOf(s))
+ }
+ }
+ return pair
+ }
+
+ private fun libEntry(libName: String, path: String? = null): SharedLibraryEntry =
+ SharedLibraryEntry(libName, path ?: builtinLibPath(libName),
+ arrayOfNulls(0), false /* isNative */)
+
+ private fun libOfBuiltin(libName: String): SharedLibraryInfo =
+ SharedLibraryInfo(builtinLibPath(libName),
+ null /* packageName */,
+ null /* codePaths */,
+ libName,
+ VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_BUILTIN,
+ VersionedPackage(PLATFORM_PACKAGE_NAME, 0L /* versionCode */),
+ null /* dependentPackages */,
+ null /* dependencies */,
+ false /* isNative */)
+
+ private fun libOfStatic(
+ packageName: String,
+ libName: String,
+ version: Long
+ ): SharedLibraryInfo =
+ SharedLibraryInfo(null /* path */,
+ packageName,
+ listOf(apkPath(packageName)),
+ libName,
+ version,
+ SharedLibraryInfo.TYPE_STATIC,
+ VersionedPackage(packageName, version /* versionCode */),
+ null /* dependentPackages */,
+ null /* dependencies */,
+ false /* isNative */)
+
+ private fun libOfDynamic(packageName: String, libName: String): SharedLibraryInfo =
+ SharedLibraryInfo(null /* path */,
+ packageName,
+ listOf(apkPath(packageName)),
+ libName,
+ VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_DYNAMIC,
+ VersionedPackage(packageName, 1L /* versionCode */),
+ null /* dependentPackages */,
+ null /* dependencies */,
+ false /* isNative */)
+
+ private fun builtinLibPath(libName: String): String = "/system/app/$libName/$libName.jar"
+
+ private fun apkPath(packageName: String): String =
+ File(mRule.system().dataAppDirectory, packageName).path
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d18030f..97ebdd4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -595,6 +595,20 @@
}
@Test
+ public void getCurrentMagnificationRegion_returnRegion() {
+ final int displayId = 1;
+ final Region region = new Region(10, 20, 100, 200);
+ doAnswer((invocation) -> {
+ ((Region) invocation.getArguments()[1]).set(region);
+ return null;
+ }).when(mMockMagnificationProcessor).getCurrentMagnificationRegion(eq(displayId), any(),
+ anyBoolean());
+
+ final Region result = mServiceConnection.getCurrentMagnificationRegion(displayId);
+ assertEquals(result, region);
+ }
+
+ @Test
public void getMagnificationCenterX_serviceNotBelongCurrentUser_returnZero() {
final int displayId = 1;
final float centerX = 480.0f;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 3ade9ff..953b536 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -343,6 +343,20 @@
eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), ArgumentMatchers.isNotNull());
}
+ @Test
+ public void testFollowTypingEnabled_defaultEnabledAndThenDisable_propagateToController() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ Settings.Secure.putIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+ 0, mA11yms.getCurrentUserIdLocked());
+
+ mA11yms.readMagnificationFollowTypingLocked(userState);
+
+ verify(mMockMagnificationController).setMagnificationFollowTypingEnabled(false);
+ }
+
@SmallTest
@Test
public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b9d94ed..27637c2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -172,6 +172,7 @@
mUserState.getMagnificationModeLocked(TEST_DISPLAY));
assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked());
assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked());
+ assertTrue(mUserState.isMagnificationFollowTypingEnabled());
}
@Test
@@ -374,6 +375,15 @@
}
@Test
+ public void setMagnificationFollowTypingEnabled_defaultTrueAndThenDisable_returnFalse() {
+ assertTrue(mUserState.isMagnificationFollowTypingEnabled());
+
+ mUserState.setMagnificationFollowTypingEnabled(false);
+
+ assertFalse(mUserState.isMagnificationFollowTypingEnabled());
+ }
+
+ @Test
public void setFocusAppearanceData_returnExpectedFocusAppearanceData() {
final int focusStrokeWidthValue = 100;
final int focusColorValue = Color.BLUE;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index 99d6c2a..c4040b4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -184,6 +184,38 @@
}
@Test
+ public void getCurrentMagnificationRegion_windowModeActivated_returnRegion() {
+ final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
+ doAnswer((invocation) -> {
+ ((Region) invocation.getArguments()[1]).set(region);
+ return null;
+ }).when(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+ any());
+
+ final Region result = new Region();
+ mMagnificationProcessor.getCurrentMagnificationRegion(TEST_DISPLAY,
+ result, /* canControlMagnification= */true);
+ assertEquals(region, result);
+ }
+
+ @Test
+ public void getCurrentMagnificationRegion_fullscreenModeActivated_returnRegion() {
+ final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
+ doAnswer((invocation) -> {
+ ((Region) invocation.getArguments()[1]).set(region);
+ return null;
+ }).when(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY),
+ any());
+
+ final Region result = new Region();
+ mMagnificationProcessor.getCurrentMagnificationRegion(TEST_DISPLAY,
+ result, /* canControlMagnification= */true);
+ assertEquals(region, result);
+ }
+
+ @Test
public void getMagnificationCenterX_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_FULLSCREEN)
@@ -222,7 +254,7 @@
}
@Test
- public void reset_fullscreenMagnificationActivated() {
+ public void resetFullscreenMagnification_fullscreenMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
mMagnificationProcessor.resetFullscreenMagnification(TEST_DISPLAY, /* animate= */false);
@@ -231,7 +263,7 @@
}
@Test
- public void reset_windowMagnificationActivated() {
+ public void resetCurrentMagnification_windowMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 96af617..a9b7cfb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
+
import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import static org.junit.Assert.assertEquals;
@@ -23,10 +25,8 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -37,6 +37,7 @@
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+import android.accessibilityservice.MagnificationConfig;
import android.animation.ValueAnimator;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -105,6 +106,8 @@
private final MagnificationScaleProvider mScaleProvider = mock(
MagnificationScaleProvider.class);
+ private final ArgumentCaptor<MagnificationConfig> mConfigCaptor = ArgumentCaptor.forClass(
+ MagnificationConfig.class);
ValueAnimator mMockValueAnimator;
ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
@@ -142,13 +145,13 @@
register(DISPLAY_1);
register(INVALID_DISPLAY);
verify(mMockContext).registerReceiver(
- (BroadcastReceiver) anyObject(), (IntentFilter) anyObject());
+ any(BroadcastReceiver.class), any(IntentFilter.class));
verify(mMockWindowManager).setMagnificationCallbacks(
- eq(DISPLAY_0), (MagnificationCallbacks) anyObject());
+ eq(DISPLAY_0), any(MagnificationCallbacks.class));
verify(mMockWindowManager).setMagnificationCallbacks(
- eq(DISPLAY_1), (MagnificationCallbacks) anyObject());
+ eq(DISPLAY_1), any(MagnificationCallbacks.class));
verify(mMockWindowManager).setMagnificationCallbacks(
- eq(INVALID_DISPLAY), (MagnificationCallbacks) anyObject());
+ eq(INVALID_DISPLAY), any(MagnificationCallbacks.class));
assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
assertFalse(mFullScreenMagnificationController.isRegistered(INVALID_DISPLAY));
@@ -159,9 +162,9 @@
register(DISPLAY_0);
register(DISPLAY_1);
mFullScreenMagnificationController.unregister(DISPLAY_0);
- verify(mMockContext, times(0)).unregisterReceiver((BroadcastReceiver) anyObject());
+ verify(mMockContext, times(0)).unregisterReceiver(any(BroadcastReceiver.class));
mFullScreenMagnificationController.unregister(DISPLAY_1);
- verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject());
+ verify(mMockContext).unregisterReceiver(any(BroadcastReceiver.class));
verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_0), eq(null));
verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_1), eq(null));
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
@@ -343,6 +346,7 @@
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
float scale = 2.5f;
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+ final MagnificationConfig config = buildConfig(scale, newCenter.x, newCenter.y);
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
@@ -353,8 +357,9 @@
assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
- verify(mMockAms).notifyMagnificationChanged(displayId,
- INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
+ verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
+ mConfigCaptor.capture());
+ assertConfigEquals(config, mConfigCaptor.getValue());
verify(mMockValueAnimator).start();
verify(mRequestObserver).onRequestMagnificationSpec(displayId, SERVICE_ID_1);
@@ -494,8 +499,11 @@
MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
callbacks.onMagnificationRegionChanged(OTHER_REGION);
mMessageCapturingHandler.sendAllMessages();
- verify(mMockAms).notifyMagnificationChanged(displayId, OTHER_REGION, 1.0f,
- OTHER_MAGNIFICATION_BOUNDS.centerX(), OTHER_MAGNIFICATION_BOUNDS.centerY());
+ MagnificationConfig config = buildConfig(1.0f, OTHER_MAGNIFICATION_BOUNDS.centerX(),
+ OTHER_MAGNIFICATION_BOUNDS.centerY());
+ verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(OTHER_REGION),
+ mConfigCaptor.capture());
+ assertConfigEquals(config, mConfigCaptor.getValue());
}
@Test
@@ -650,7 +658,7 @@
reset(mMockAms);
assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
verify(mMockAms).notifyMagnificationChanged(eq(displayId),
- eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat());
+ eq(INITIAL_MAGNIFICATION_REGION), any(MagnificationConfig.class));
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
}
@@ -668,8 +676,8 @@
assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback));
mMessageCapturingHandler.sendAllMessages();
- verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId),
- any(Region.class), anyFloat(), anyFloat(), anyFloat());
+ verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class),
+ any(MagnificationConfig.class));
verify(mAnimationCallback).onResult(true);
}
@@ -726,7 +734,7 @@
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext).registerReceiver(
- broadcastReceiverCaptor.capture(), (IntentFilter) anyObject());
+ broadcastReceiverCaptor.capture(), any(IntentFilter.class));
BroadcastReceiver br = broadcastReceiverCaptor.getValue();
zoomIn2xToMiddle(DISPLAY_0);
zoomIn2xToMiddle(DISPLAY_1);
@@ -913,6 +921,22 @@
}
@Test
+ public void requestRectOnScreen_disabledByPrefSetting_doesNothing() {
+ register(DISPLAY_0);
+ zoomIn2xToMiddle(DISPLAY_0);
+ Mockito.reset(mMockWindowManager);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(DISPLAY_0);
+ MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0);
+ mFullScreenMagnificationController.setMagnificationFollowTypingEnabled(false);
+
+ mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1);
+
+ assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(startSpec));
+ verify(mMockWindowManager, never()).setMagnificationSpec(eq(DISPLAY_0),
+ argThat(closeTo(expectedEndSpec)));
+ }
+
+ @Test
public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(i);
@@ -1031,6 +1055,7 @@
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
float scale = 2.5f;
PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+ final MagnificationConfig config = buildConfig(scale, firstCenter.x, firstCenter.y);
MagnificationSpec firstEndSpec = getMagnificationSpec(
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale));
@@ -1047,8 +1072,9 @@
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
- verify(mMockAms).notifyMagnificationChanged(displayId,
- INITIAL_MAGNIFICATION_REGION, scale, firstCenter.x, firstCenter.y);
+ verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
+ mConfigCaptor.capture());
+ assertConfigEquals(config, mConfigCaptor.getValue());
Mockito.reset(mMockWindowManager);
// Intermediate point
@@ -1062,6 +1088,7 @@
Mockito.reset(mMockWindowManager);
PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
+ final MagnificationConfig newConfig = buildConfig(scale, newCenter.x, newCenter.y);
MagnificationSpec newEndSpec = getMagnificationSpec(
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale));
assertTrue(mFullScreenMagnificationController.setCenter(displayId,
@@ -1070,8 +1097,9 @@
// Animation should have been restarted
verify(mMockValueAnimator, times(2)).start();
- verify(mMockAms).notifyMagnificationChanged(displayId,
- INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
+ verify(mMockAms, times(2)).notifyMagnificationChanged(eq(displayId),
+ eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
+ assertConfigEquals(newConfig, mConfigCaptor.getValue());
// New starting point should be where we left off
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
@@ -1155,7 +1183,7 @@
Region regionArg = (Region) args[1];
regionArg.set(INITIAL_MAGNIFICATION_REGION);
return null;
- }).when(mMockWindowManager).getMagnificationRegion(anyInt(), (Region) anyObject());
+ }).when(mMockWindowManager).getMagnificationRegion(anyInt(), any(Region.class));
}
private void resetMockWindowManager() {
@@ -1201,6 +1229,19 @@
magnifiedBounds.centerY() - scale * center.y);
}
+ private MagnificationConfig buildConfig(float scale, float centerX, float centerY) {
+ return new MagnificationConfig.Builder().setMode(
+ MAGNIFICATION_MODE_FULLSCREEN).setScale(scale).setCenterX(centerX).setCenterY(
+ centerY).build();
+ }
+
+ private void assertConfigEquals(MagnificationConfig expected, MagnificationConfig result) {
+ assertEquals(expected.getMode(), result.getMode());
+ assertEquals(expected.getScale(), result.getScale(), 0f);
+ assertEquals(expected.getCenterX(), result.getCenterX(), 0f);
+ assertEquals(expected.getCenterY(), result.getCenterY(), 0f);
+ }
+
private MagnificationSpec getInterpolatedMagSpec(MagnificationSpec start, MagnificationSpec end,
float fraction) {
MagnificationSpec interpolatedSpec = new MagnificationSpec();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 0054fc3..064b762 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -430,6 +430,21 @@
}
@Test
+ public void onSourceBoundsChanged_notifyMagnificationChanged() {
+ Rect rect = new Rect(0, 0, 100, 120);
+ Region region = new Region(rect);
+
+ mMagnificationController.onSourceBoundsChanged(TEST_DISPLAY, rect);
+
+ final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
+ MagnificationConfig.class);
+ verify(mService).notifyMagnificationChanged(eq(TEST_DISPLAY), eq(region),
+ configCaptor.capture());
+ assertEquals(rect.exactCenterX(), configCaptor.getValue().getCenterX(), 0);
+ assertEquals(rect.exactCenterY(), configCaptor.getValue().getCenterY(), 0);
+ }
+
+ @Test
public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -464,6 +479,14 @@
}
@Test
+ public void setPreferenceMagnificationFollowTypingEnabled_setPrefDisabled_disableAll() {
+ mMagnificationController.setMagnificationFollowTypingEnabled(false);
+
+ verify(mWindowMagnificationManager).setMagnificationFollowTypingEnabled(eq(false));
+ verify(mScreenMagnificationController).setMagnificationFollowTypingEnabled(eq(false));
+ }
+
+ @Test
public void onRectangleOnScreenRequested_fullScreenIsActivated_fullScreenDispatchEvent() {
mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index a62c0d5..8da513b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -274,6 +274,123 @@
}
@Test
+ public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+ eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ eq(0f), eq(0f), notNull());
+ }
+
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnification()
+ throws RemoteException {
+ final float distanceX = 10f;
+ final float distanceY = 10f;
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mWindowMagnificationManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+ eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.inset(-10, -10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+ eq(3f), eq(500f), eq(500f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnification()
+ throws RemoteException {
+ final PointF initialPoint = new PointF(50f, 50f);
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f,
+ initialPoint.x, initialPoint.y);
+ mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(true);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY),
+ eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region beforeRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mWindowMagnificationManager.setMagnificationFollowTypingEnabled(false);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ final Region afterRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ assertEquals(afterRegion, beforeRegion);
+ }
+
+ @Test
public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
new file mode 100644
index 0000000..d4bac2c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import static android.testing.TestableLooper.RunWithLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class BiometricSchedulerOperationTest {
+
+ public interface FakeHal {}
+ public abstract static class InterruptableMonitor<T>
+ extends HalClientMonitor<T> implements Interruptable {
+ public InterruptableMonitor() {
+ super(null, null, null, null, 0, null, 0, 0, 0, 0, 0);
+ }
+ }
+
+ @Mock
+ private InterruptableMonitor<FakeHal> mClientMonitor;
+ @Mock
+ private BaseClientMonitor.Callback mClientCallback;
+ @Mock
+ private FakeHal mHal;
+ @Captor
+ ArgumentCaptor<BaseClientMonitor.Callback> mStartCallback;
+
+ private Handler mHandler;
+ private BiometricSchedulerOperation mOperation;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new Handler(TestableLooper.get(this).getLooper());
+ mOperation = new BiometricSchedulerOperation(mClientMonitor, mClientCallback);
+ }
+
+ @Test
+ public void testStartWithCookie() {
+ final int cookie = 200;
+ when(mClientMonitor.getCookie()).thenReturn(cookie);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ assertThat(mOperation.isReadyToStart()).isEqualTo(cookie);
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+
+ final boolean started = mOperation.startWithCookie(
+ mock(BaseClientMonitor.Callback.class), cookie);
+
+ assertThat(started).isTrue();
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ assertThat(mOperation.isStarted()).isTrue();
+ }
+
+ @Test
+ public void testNoStartWithoutCookie() {
+ final int goodCookie = 20;
+ final int badCookie = 22;
+ when(mClientMonitor.getCookie()).thenReturn(goodCookie);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie);
+ final boolean started = mOperation.startWithCookie(
+ mock(BaseClientMonitor.Callback.class), badCookie);
+
+ assertThat(started).isFalse();
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+ }
+
+ @Test
+ public void startsWhenReadyAndHalAvailable() {
+ when(mClientMonitor.getCookie()).thenReturn(0);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(cb);
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+
+ assertThat(mOperation.isStarted()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+
+ verify(mClientCallback).onClientStarted(eq(mClientMonitor));
+ verify(cb).onClientStarted(eq(mClientMonitor));
+ verify(mClientCallback, never()).onClientFinished(any(), anyBoolean());
+ verify(cb, never()).onClientFinished(any(), anyBoolean());
+
+ mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+ }
+
+ @Test
+ public void startFailsWhenReadyButHalNotAvailable() {
+ when(mClientMonitor.getCookie()).thenReturn(0);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(null);
+
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(cb);
+ verify(mClientMonitor, never()).start(any());
+
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isTrue();
+
+ verify(mClientCallback, never()).onClientStarted(eq(mClientMonitor));
+ verify(cb, never()).onClientStarted(eq(mClientMonitor));
+ verify(mClientCallback).onClientFinished(eq(mClientMonitor), eq(false));
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(false));
+ }
+
+ @Test
+ public void doesNotStartWithCookie() {
+ when(mClientMonitor.getCookie()).thenReturn(9);
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void cannotRestart() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void abortsNotRunning() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.abort();
+
+ assertThat(mOperation.isFinished()).isTrue();
+ verify(mClientMonitor).unableToStart();
+ verify(mClientMonitor).destroy();
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void cannotAbortRunning() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+
+ assertThrows(IllegalStateException.class, () -> mOperation.abort());
+ }
+
+ @Test
+ public void cancel() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback startCb = mock(BaseClientMonitor.Callback.class);
+ final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(startCb);
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ mOperation.cancel(mHandler, cancelCb);
+
+ assertThat(mOperation.isCanceling()).isTrue();
+ verify(mClientMonitor).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).destroy();
+
+ mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+
+ // should be unused since the operation was started
+ verify(cancelCb, never()).onClientStarted(any());
+ verify(cancelCb, never()).onClientFinished(any(), anyBoolean());
+ }
+
+ @Test
+ public void cancelWithoutStarting() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+ mOperation.cancel(mHandler, cancelCb);
+
+ assertThat(mOperation.isCanceling()).isTrue();
+ ArgumentCaptor<BaseClientMonitor.Callback> cbCaptor =
+ ArgumentCaptor.forClass(BaseClientMonitor.Callback.class);
+ verify(mClientMonitor).cancelWithoutStarting(cbCaptor.capture());
+
+ cbCaptor.getValue().onClientFinished(mClientMonitor, true);
+ verify(cancelCb).onClientFinished(eq(mClientMonitor), eq(true));
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor).destroy();
+ }
+
+ @Test
+ public void markCanceling() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.markCanceling();
+
+ assertThat(mOperation.isMarkedCanceling()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).unableToStart();
+ verify(mClientMonitor, never()).destroy();
+ }
+
+ @Test
+ public void cancelPendingWithCookie() {
+ markCancellingAndStart(2);
+ }
+
+ @Test
+ public void cancelPendingWithoutCookie() {
+ markCancellingAndStart(null);
+ }
+
+ private void markCancellingAndStart(Integer withCookie) {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+ if (withCookie != null) {
+ when(mClientMonitor.getCookie()).thenReturn(withCookie);
+ }
+
+ mOperation.markCanceling();
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ if (withCookie != null) {
+ mOperation.startWithCookie(cb, withCookie);
+ } else {
+ mOperation.start(cb);
+ }
+
+ assertThat(mOperation.isFinished()).isTrue();
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).unableToStart();
+ verify(mClientMonitor).destroy();
+ }
+
+ @Test
+ public void cancelWatchdogWhenStarted() {
+ cancelWatchdog(true);
+ }
+
+ @Test
+ public void cancelWatchdogWithoutStarting() {
+ cancelWatchdog(false);
+ }
+
+ private void cancelWatchdog(boolean start) {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+ if (start) {
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ }
+ mOperation.cancel(mHandler, mock(BaseClientMonitor.Callback.class));
+
+ assertThat(mOperation.isCanceling()).isTrue();
+
+ // omit call to onClientFinished and trigger watchdog
+ mOperation.mCancelWatchdog.run();
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index d192697..ac08319 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -16,10 +16,14 @@
package com.android.server.biometrics.sensors;
+import static android.testing.TestableLooper.RunWithLooper;
+
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,10 +38,13 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
+import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -46,16 +53,18 @@
import com.android.server.biometrics.nano.BiometricSchedulerProto;
import com.android.server.biometrics.nano.BiometricsProto;
-import com.android.server.biometrics.sensors.BiometricScheduler.Operation;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@Presubmit
@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
public class BiometricSchedulerTest {
private static final String TAG = "BiometricSchedulerTest";
@@ -76,8 +85,9 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mToken = new Binder();
- mScheduler = new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_UNKNOWN,
- null /* gestureAvailabilityTracker */, mBiometricService, LOG_NUM_RECENT_OPERATIONS,
+ mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
+ BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
+ mBiometricService, LOG_NUM_RECENT_OPERATIONS,
CoexCoordinator.getInstance());
}
@@ -86,9 +96,9 @@
final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
final HalClientMonitor<Object> client1 =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
final HalClientMonitor<Object> client2 =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
mScheduler.scheduleClientMonitor(client1);
mScheduler.scheduleClientMonitor(client2);
@@ -99,20 +109,17 @@
@Test
public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() {
// Even if second client has a non-null daemon, it needs to be canceled.
- Object daemon2 = mock(Object.class);
-
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
- final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon1);
- final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+ final TestHalClientMonitor client1 = new TestHalClientMonitor(
+ mContext, mToken, () -> null);
+ final TestHalClientMonitor client2 = new TestHalClientMonitor(
+ mContext, mToken, () -> mock(Object.class));
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
- mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
+ mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -122,11 +129,11 @@
mScheduler.scheduleClientMonitor(client2, callback2);
waitForIdle();
- assertTrue(client1.wasUnableToStart());
+ assertTrue(client1.mUnableToStart);
verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
verify(callback1, never()).onClientStarted(any());
- assertTrue(client2.wasUnableToStart());
+ assertTrue(client2.mUnableToStart);
verify(callback2).onClientFinished(eq(client2), eq(false) /* success */);
verify(callback2, never()).onClientStarted(any());
@@ -138,21 +145,19 @@
// Second non-BiometricPrompt client has a valid daemon
final Object daemon2 = mock(Object.class);
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
final TestAuthenticationClient client1 =
- new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
- final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+ new TestAuthenticationClient(mContext, () -> null, mToken, listener1);
+ final TestHalClientMonitor client2 =
+ new TestHalClientMonitor(mContext, mToken, () -> daemon2);
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
- mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
+ mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -172,8 +177,8 @@
verify(callback1, never()).onClientStarted(any());
// Client 2 was able to start
- assertFalse(client2.wasUnableToStart());
- assertTrue(client2.hasStarted());
+ assertFalse(client2.mUnableToStart);
+ assertTrue(client2.mStarted);
verify(callback2).onClientStarted(eq(client2));
}
@@ -187,16 +192,18 @@
// Schedule a BiometricPrompt authentication request
mScheduler.scheduleClientMonitor(client1, callback1);
- assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.mState);
- assertEquals(client1, mScheduler.mCurrentOperation.mClientMonitor);
+ assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
+ assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
assertEquals(0, mScheduler.mPendingOperations.size());
// Request it to be canceled. The operation can be canceled immediately, and the scheduler
// should go back to idle, since in this case the framework has not even requested the HAL
// to authenticate yet.
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+ waitForIdle();
assertTrue(client1.isAlreadyDone());
assertTrue(client1.mDestroyed);
+ assertFalse(client1.mStartedHal);
assertNull(mScheduler.mCurrentOperation);
}
@@ -210,8 +217,8 @@
// assertEquals(0, bsp.recentOperations.length);
// Pretend the scheduler is busy enrolling, and check the proto dump again.
- final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+ final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
mScheduler.scheduleClientMonitor(client);
waitForIdle();
bsp = getDump(true /* clearSchedulerBuffer */);
@@ -230,8 +237,8 @@
@Test
public void testProtoDump_fifo() throws Exception {
// Add the first operation
- final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+ final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
mScheduler.scheduleClientMonitor(client);
waitForIdle();
BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */);
@@ -244,8 +251,8 @@
client.getCallback().onClientFinished(client, true);
// Add another operation
- final TestClientMonitor2 client2 = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_REMOVE);
+ final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_REMOVE);
mScheduler.scheduleClientMonitor(client2);
waitForIdle();
bsp = getDump(false /* clearSchedulerBuffer */);
@@ -256,8 +263,8 @@
client2.getCallback().onClientFinished(client2, true);
// And another operation
- final TestClientMonitor2 client3 = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_AUTHENTICATE);
+ final TestHalClientMonitor client3 = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_AUTHENTICATE);
mScheduler.scheduleClientMonitor(client3);
waitForIdle();
bsp = getDump(false /* clearSchedulerBuffer */);
@@ -290,8 +297,7 @@
@Test
public void testCancelPendingAuth() throws RemoteException {
final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
-
- final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon);
+ final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
mToken, callback);
@@ -302,14 +308,12 @@
waitForIdle();
assertEquals(mScheduler.getCurrentClient(), client1);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
// Request cancel before the authentication client has started
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
waitForIdle();
- assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
// Finish the blocking client. The authentication client should send ERROR_CANCELED
client1.getCallback().onClientFinished(client1, true /* success */);
@@ -326,67 +330,109 @@
@Test
public void testCancels_whenAuthRequestIdNotSet() {
- testCancelsWhenRequestId(null /* requestId */, 2, true /* started */);
+ testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, true /* started */);
}
@Test
public void testCancels_whenAuthRequestIdNotSet_notStarted() {
- testCancelsWhenRequestId(null /* requestId */, 2, false /* started */);
+ testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, false /* started */);
}
@Test
public void testCancels_whenAuthRequestIdMatches() {
- testCancelsWhenRequestId(200L, 200, true /* started */);
+ testCancelsAuthDetectWhenRequestId(200L, 200, true /* started */);
}
@Test
public void testCancels_whenAuthRequestIdMatches_noStarted() {
- testCancelsWhenRequestId(200L, 200, false /* started */);
+ testCancelsAuthDetectWhenRequestId(200L, 200, false /* started */);
}
@Test
public void testDoesNotCancel_whenAuthRequestIdMismatched() {
- testCancelsWhenRequestId(10L, 20, true /* started */);
+ testCancelsAuthDetectWhenRequestId(10L, 20, true /* started */);
}
@Test
public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() {
- testCancelsWhenRequestId(10L, 20, false /* started */);
+ testCancelsAuthDetectWhenRequestId(10L, 20, false /* started */);
+ }
+
+ private void testCancelsAuthDetectWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+ boolean started) {
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+ final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+ testCancelsWhenRequestId(requestId, cancelRequestId, started,
+ new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback));
+ }
+
+ @Test
+ public void testCancels_whenEnrollRequestIdNotSet() {
+ testCancelsEnrollWhenRequestId(null /* requestId */, 2, false /* started */);
+ }
+
+ @Test
+ public void testCancels_whenEnrollRequestIdMatches() {
+ testCancelsEnrollWhenRequestId(200L, 200, false /* started */);
+ }
+
+ @Test
+ public void testDoesNotCancel_whenEnrollRequestIdMismatched() {
+ testCancelsEnrollWhenRequestId(10L, 20, false /* started */);
+ }
+
+ private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+ boolean started) {
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+ final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+ testCancelsWhenRequestId(requestId, cancelRequestId, started,
+ new TestEnrollClient(mContext, lazyDaemon, mToken, callback));
}
private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
- boolean started) {
+ boolean started, HalClientMonitor<?> client) {
final boolean matches = requestId == null || requestId == cancelRequestId;
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
- final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
- final TestAuthenticationClient client = new TestAuthenticationClient(
- mContext, lazyDaemon, mToken, callback);
if (requestId != null) {
client.setRequestId(requestId);
}
+ final boolean isAuth = client instanceof TestAuthenticationClient;
+ final boolean isEnroll = client instanceof TestEnrollClient;
+
mScheduler.scheduleClientMonitor(client);
if (started) {
mScheduler.startPreparedClient(client.getCookie());
}
waitForIdle();
- mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+ if (isAuth) {
+ mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+ } else if (isEnroll) {
+ mScheduler.cancelEnrollment(mToken, cancelRequestId);
+ } else {
+ fail("unexpected operation type");
+ }
waitForIdle();
- assertEquals(matches && started ? 1 : 0, client.mNumCancels);
+ if (isAuth) {
+ // auth clients that were waiting for cookie when canceled should never invoke the hal
+ final TestAuthenticationClient authClient = (TestAuthenticationClient) client;
+ assertEquals(matches && started ? 1 : 0, authClient.mNumCancels);
+ assertEquals(started, authClient.mStartedHal);
+ } else if (isEnroll) {
+ final TestEnrollClient enrollClient = (TestEnrollClient) client;
+ assertEquals(matches ? 1 : 0, enrollClient.mNumCancels);
+ assertTrue(enrollClient.mStartedHal);
+ }
if (matches) {
- if (started) {
- assertEquals(Operation.STATE_STARTED_CANCELING,
- mScheduler.mCurrentOperation.mState);
+ if (started || isEnroll) { // prep'd auth clients and enroll clients
+ assertTrue(mScheduler.mCurrentOperation.isCanceling());
}
} else {
- if (started) {
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
+ if (started || isEnroll) { // prep'd auth clients and enroll clients
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
} else {
- assertEquals(Operation.STATE_WAITING_FOR_COOKIE,
- mScheduler.mCurrentOperation.mState);
+ assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
}
}
}
@@ -411,18 +457,14 @@
mScheduler.cancelAuthenticationOrDetection(mToken, 9999);
waitForIdle();
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
+ assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
mScheduler.cancelAuthenticationOrDetection(mToken, requestId2);
waitForIdle();
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
+ assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
}
@Test
@@ -459,12 +501,12 @@
@Test
public void testClientDestroyed_afterFinish() {
final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
- final TestClientMonitor client =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ final TestHalClientMonitor client =
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
mScheduler.scheduleClientMonitor(client);
client.mCallback.onClientFinished(client, true /* success */);
waitForIdle();
- assertTrue(client.wasDestroyed());
+ assertTrue(client.mDestroyed);
}
private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
@@ -472,8 +514,10 @@
}
private static class TestAuthenticationClient extends AuthenticationClient<Object> {
- int mNumCancels = 0;
+ boolean mStartedHal = false;
+ boolean mStoppedHal = false;
boolean mDestroyed = false;
+ int mNumCancels = 0;
public TestAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -488,18 +532,16 @@
@Override
protected void stopHalOperation() {
-
+ mStoppedHal = true;
}
@Override
protected void startHalOperation() {
-
+ mStartedHal = true;
}
@Override
- protected void handleLifecycleAfterAuth(boolean authenticated) {
-
- }
+ protected void handleLifecycleAfterAuth(boolean authenticated) {}
@Override
public boolean wasUserDetected() {
@@ -519,36 +561,59 @@
}
}
- private static class TestClientMonitor2 extends TestClientMonitor {
- private final int mProtoEnum;
+ private static class TestEnrollClient extends EnrollClient<Object> {
+ boolean mStartedHal = false;
+ boolean mStoppedHal = false;
+ int mNumCancels = 0;
- public TestClientMonitor2(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, int protoEnum) {
- super(context, token, lazyDaemon);
- mProtoEnum = protoEnum;
+ TestEnrollClient(@NonNull Context context,
+ @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener) {
+ super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
+ "test" /* owner */, mock(BiometricUtils.class),
+ 5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID,
+ true /* shouldVibrate */);
}
@Override
- public int getProtoEnum() {
- return mProtoEnum;
+ protected void stopHalOperation() {
+ mStoppedHal = true;
+ }
+
+ @Override
+ protected void startHalOperation() {
+ mStartedHal = true;
+ }
+
+ @Override
+ protected boolean hasReachedEnrollmentLimit() {
+ return false;
+ }
+
+ @Override
+ public void cancel() {
+ mNumCancels++;
+ super.cancel();
}
}
- private static class TestClientMonitor extends HalClientMonitor<Object> {
+ private static class TestHalClientMonitor extends HalClientMonitor<Object> {
+ private final int mProtoEnum;
private boolean mUnableToStart;
private boolean mStarted;
private boolean mDestroyed;
- public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
@NonNull LazyDaemon<Object> lazyDaemon) {
- this(context, token, lazyDaemon, 0 /* cookie */);
+ this(context, token, lazyDaemon, 0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER);
}
- public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, int cookie) {
+ TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ @NonNull LazyDaemon<Object> lazyDaemon, int cookie, int protoEnum) {
super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
0 /* statsAction */, 0 /* statsClient */);
+ mProtoEnum = protoEnum;
}
@Override
@@ -559,9 +624,7 @@
@Override
public int getProtoEnum() {
- // Anything other than CM_NONE, which is used to represent "idle". Tests that need
- // real proto enums should use TestClientMonitor2
- return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+ return mProtoEnum;
}
@Override
@@ -573,7 +636,7 @@
@Override
protected void startHalOperation() {
-
+ mStarted = true;
}
@Override
@@ -581,22 +644,9 @@
super.destroy();
mDestroyed = true;
}
-
- public boolean wasUnableToStart() {
- return mUnableToStart;
- }
-
- public boolean hasStarted() {
- return mStarted;
- }
-
- public boolean wasDestroyed() {
- return mDestroyed;
- }
-
}
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ private void waitForIdle() {
+ TestableLooper.get(this).processAllMessages();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 7fccd49..407f5fb 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors;
+import static android.testing.TestableLooper.RunWithLooper;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -28,52 +30,53 @@
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
@SmallTest
public class UserAwareBiometricSchedulerTest {
- private static final String TAG = "BiometricSchedulerTest";
+ private static final String TAG = "UserAwareBiometricSchedulerTest";
private static final int TEST_SENSOR_ID = 0;
+ private Handler mHandler;
private UserAwareBiometricScheduler mScheduler;
- private IBinder mToken;
+ private IBinder mToken = new Binder();
@Mock
private Context mContext;
@Mock
private IBiometricService mBiometricService;
- private TestUserStartedCallback mUserStartedCallback;
- private TestUserStoppedCallback mUserStoppedCallback;
+ private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
+ private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
private int mCurrentUserId = UserHandle.USER_NULL;
- private boolean mStartOperationsFinish;
- private int mStartUserClientCount;
+ private boolean mStartOperationsFinish = true;
+ private int mStartUserClientCount = 0;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
- mToken = new Binder();
- mStartOperationsFinish = true;
- mStartUserClientCount = 0;
- mUserStartedCallback = new TestUserStartedCallback();
- mUserStoppedCallback = new TestUserStoppedCallback();
-
+ mHandler = new Handler(TestableLooper.get(this).getLooper());
mScheduler = new UserAwareBiometricScheduler(TAG,
+ mHandler,
BiometricScheduler.SENSOR_TYPE_UNKNOWN,
null /* gestureAvailabilityDispatcher */,
mBiometricService,
@@ -117,7 +120,7 @@
mCurrentUserId = UserHandle.USER_NULL;
mStartOperationsFinish = false;
- final BaseClientMonitor[] nextClients = new BaseClientMonitor[] {
+ final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{
mock(BaseClientMonitor.class),
mock(BaseClientMonitor.class),
mock(BaseClientMonitor.class)
@@ -147,11 +150,11 @@
waitForIdle();
final TestStartUserClient startUserClient =
- (TestStartUserClient) mScheduler.mCurrentOperation.mClientMonitor;
+ (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor();
mScheduler.reset();
assertNull(mScheduler.mCurrentOperation);
- final BiometricScheduler.Operation fakeOperation = new BiometricScheduler.Operation(
+ final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), new BaseClientMonitor.Callback() {});
mScheduler.mCurrentOperation = fakeOperation;
startUserClient.mCallback.onClientFinished(startUserClient, true);
@@ -194,8 +197,8 @@
verify(nextClient).start(any());
}
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ private void waitForIdle() {
+ TestableLooper.get(this).processAllMessages();
}
private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index a13dff2..0891eca 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -79,6 +79,7 @@
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
mScheduler = new UserAwareBiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityDispatcher */,
() -> USER_ID,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 39c51d5..21a7a8a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -32,7 +32,9 @@
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -69,6 +71,7 @@
@Mock
private BiometricScheduler mScheduler;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
private LockoutResetDispatcher mLockoutResetDispatcher;
private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10;
private IBinder mBinder;
@@ -97,7 +100,7 @@
resetLockoutRequiresChallenge);
Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
- mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler);
+ mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler);
mBinder = new Binder();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 0d520ca..a012b8b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -79,6 +79,7 @@
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
mScheduler = new UserAwareBiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
BiometricScheduler.SENSOR_TYPE_FP_OTHER,
null /* gestureAvailabilityDispatcher */,
() -> USER_ID,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 419dda5..5fcb802 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -22,7 +22,10 @@
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_CAN_COLORIZE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
@@ -179,6 +182,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -466,6 +470,9 @@
// Return first true for RoleObserver main-thread check
when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
+ verify(mHistoryManager, never()).onBootPhaseAppsCanStart();
+ mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
+ verify(mHistoryManager).onBootPhaseAppsCanStart();
mService.setAudioManager(mAudioManager);
@@ -1437,6 +1444,62 @@
}
@Test
+ public void testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed() throws Exception {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
+ "true",
+ false);
+ Thread.sleep(300);
+
+ final String tag = "testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed";
+
+ Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, tag, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(PKG);
+ assertThat(notifs[0].getNotification().flags).isEqualTo(
+ FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR);
+ }
+
+ @Test
+ public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
+ "false",
+ false);
+ Thread.sleep(300);
+
+ final String tag = "testEnqueueNotificationWithTag_FGSaddsNoClear";
+
+ Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(PKG);
+ assertThat(notifs[0].getNotification().flags).isEqualTo(
+ FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR | FLAG_ONGOING_EVENT);
+ }
+
+ @Test
public void testCancelNonexistentNotification() throws Exception {
mBinderService.cancelNotificationWithTag(PKG, PKG,
"testCancelNonexistentNotification", 0, 0);
@@ -1757,51 +1820,6 @@
}
@Test
- public void testCancelAllCancelNotificationsFromListener_NoClearFlag() throws Exception {
- final NotificationRecord parent = generateNotificationRecord(
- mTestNotificationChannel, 1, "group", true);
- final NotificationRecord child = generateNotificationRecord(
- mTestNotificationChannel, 2, "group", false);
- final NotificationRecord child2 = generateNotificationRecord(
- mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
- final NotificationRecord newGroup = generateNotificationRecord(
- mTestNotificationChannel, 4, "group2", false);
- mService.addNotification(parent);
- mService.addNotification(child);
- mService.addNotification(child2);
- mService.addNotification(newGroup);
- mService.getBinderService().cancelNotificationsFromListener(null, null);
- waitForIdle();
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(1, notifs.length);
- }
-
- @Test
- public void testUserInitiatedCancelAllWithGroup_NoClearFlag() throws Exception {
- final NotificationRecord parent = generateNotificationRecord(
- mTestNotificationChannel, 1, "group", true);
- final NotificationRecord child = generateNotificationRecord(
- mTestNotificationChannel, 2, "group", false);
- final NotificationRecord child2 = generateNotificationRecord(
- mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
- final NotificationRecord newGroup = generateNotificationRecord(
- mTestNotificationChannel, 4, "group2", false);
- mService.addNotification(parent);
- mService.addNotification(child);
- mService.addNotification(child2);
- mService.addNotification(newGroup);
- mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
- parent.getUserId());
- waitForIdle();
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(1, notifs.length);
- }
-
- @Test
public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
Notification n =
new Notification.Builder(mContext, mTestNotificationChannel.getId())
@@ -1838,7 +1856,268 @@
}
@Test
- public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlag()
+ public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationWithTag(
+ parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+ parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationWithTag_fromApp_cannotCancelFgsParent()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationWithTag(
+ parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+ parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(3, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationWithTag_fromApp_canCancelOngoingNoClearChild()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationWithTag(
+ parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+ parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationWithTag_fromApp_canCancelOngoingNoClearParent()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationWithTag(
+ parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+ parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllNotificationsFromApp_cannotCancelFgsChild()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelAllNotifications(
+ parent.getSbn().getPackageName(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllNotifications_fromApp_cannotCancelFgsParent()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelAllNotifications(
+ parent.getSbn().getPackageName(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllNotifications_fromApp_canCancelOngoingNoClearChild()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelAllNotifications(
+ parent.getSbn().getPackageName(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllNotifications_fromApp_canCancelOngoingNoClearParent()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelAllNotifications(
+ parent.getSbn().getPackageName(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithOngoingParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_ONGOING_EVENT;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithOngoingChild()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithFgsParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithFgsChild()
throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
@@ -1861,7 +2140,171 @@
}
@Test
- public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlagWithParameter()
+ public void testCancelNotificationsFromListener_clearAll_GroupWithNoClearParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_NO_CLEAR;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithNoClearChild()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_Ongoing()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+ mService.addNotification(child2);
+ String[] keys = {child2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_NoClear()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_NO_CLEAR;
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_Fgs()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithOngoingParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_ONGOING_EVENT;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithOngoingChild()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithFgsParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithFgsChild()
throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
@@ -1882,26 +2325,28 @@
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(1, notifs.length);
+ assertEquals(0, notifs.length);
}
@Test
- public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+ public void testCancelNotificationsFromListener_byKey_GroupWithNoClearParent()
+ throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_NO_CLEAR;
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
- parent.getUserId());
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
@@ -1909,6 +2354,76 @@
}
@Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithNoClearChild()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_Ongoing()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+ mService.addNotification(child2);
+ String[] keys = {child2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_NoClear()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_NO_CLEAR;
+ mService.addNotification(child2);
+ String[] keys = {child2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_Fgs()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ mService.addNotification(child2);
+ String[] keys = {child2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
public void testGroupInstanceIds() throws Exception {
final NotificationRecord group1 = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", true);
@@ -1973,7 +2488,7 @@
}
@Test
- public void testCancelAllNotifications_CancelsNoClearFlagOnGoing() throws Exception {
+ public void testCancelAllNotificationsInt_CancelsNoClearFlagOnGoing() throws Exception {
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
@@ -1987,32 +2502,7 @@
}
@Test
- public void testCancelAllCancelNotificationsFromListener_NoClearFlagWithParameter()
- throws Exception {
- final NotificationRecord parent = generateNotificationRecord(
- mTestNotificationChannel, 1, "group", true);
- final NotificationRecord child = generateNotificationRecord(
- mTestNotificationChannel, 2, "group", false);
- final NotificationRecord child2 = generateNotificationRecord(
- mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
- final NotificationRecord newGroup = generateNotificationRecord(
- mTestNotificationChannel, 4, "group2", false);
- mService.addNotification(parent);
- mService.addNotification(child);
- mService.addNotification(child2);
- mService.addNotification(newGroup);
- String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
- child2.getSbn().getKey(), newGroup.getSbn().getKey()};
- mService.getBinderService().cancelNotificationsFromListener(null, keys);
- waitForIdle();
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(0, notifs.length);
- }
-
- @Test
- public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+ public void testAppInitiatedCancelAllNotifications_CancelsOngoingFlag() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -2026,7 +2516,7 @@
}
@Test
- public void testCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+ public void testCancelAllNotificationsInt_CancelsOngoingFlag() throws Exception {
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
@@ -2040,69 +2530,7 @@
}
@Test
- public void testUserInitiatedCancelAllOnClearAll_OnGoingFlag() throws Exception {
- final NotificationRecord notif = generateNotificationRecord(
- mTestNotificationChannel, 1, "group", true);
- notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- mService.addNotification(notif);
-
- mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
- notif.getUserId());
- waitForIdle();
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
- assertEquals(1, notifs.length);
- }
-
- @Test
- public void testCancelAllCancelNotificationsFromListener_OnGoingFlag() throws Exception {
- final NotificationRecord parent = generateNotificationRecord(
- mTestNotificationChannel, 1, "group", true);
- final NotificationRecord child = generateNotificationRecord(
- mTestNotificationChannel, 2, "group", false);
- final NotificationRecord child2 = generateNotificationRecord(
- mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- final NotificationRecord newGroup = generateNotificationRecord(
- mTestNotificationChannel, 4, "group2", false);
- mService.addNotification(parent);
- mService.addNotification(child);
- mService.addNotification(child2);
- mService.addNotification(newGroup);
- mService.getBinderService().cancelNotificationsFromListener(null, null);
- waitForIdle();
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(1, notifs.length);
- }
-
- @Test
- public void testCancelAllCancelNotificationsFromListener_OnGoingFlagWithParameter()
- throws Exception {
- final NotificationRecord parent = generateNotificationRecord(
- mTestNotificationChannel, 1, "group", true);
- final NotificationRecord child = generateNotificationRecord(
- mTestNotificationChannel, 2, "group", false);
- final NotificationRecord child2 = generateNotificationRecord(
- mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- final NotificationRecord newGroup = generateNotificationRecord(
- mTestNotificationChannel, 4, "group2", false);
- mService.addNotification(parent);
- mService.addNotification(child);
- mService.addNotification(child2);
- mService.addNotification(newGroup);
- String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
- child2.getSbn().getKey(), newGroup.getSbn().getKey()};
- mService.getBinderService().cancelNotificationsFromListener(null, keys);
- waitForIdle();
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(0, notifs.length);
- }
-
- @Test
- public void testUserInitiatedCancelAllWithGroup_OnGoingFlag() throws Exception {
+ public void testUserInitiatedCancelAllWithGroup_OngoingFlag() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -2125,6 +2553,52 @@
}
@Test
+ public void testUserInitiatedCancelAllWithGroup_NoClearFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
public void testTvExtenderChannelOverride_onTv() throws Exception {
mService.setIsTelevision(true);
mService.setPreferencesHelper(mPreferencesHelper);
@@ -3396,7 +3870,7 @@
mTestNotificationChannel.getId())
.setContentTitle("foo")
.setColorized(true).setColor(Color.WHITE)
- .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .setFlag(FLAG_CAN_COLORIZE, true)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
"testNoFakeColorizedPermission", mUid, 0,
@@ -7469,17 +7943,6 @@
}
@Test
- public void testOnBootPhase() {
- mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
-
- verify(mHistoryManager, never()).onBootPhaseAppsCanStart();
-
- mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
-
- verify(mHistoryManager, times(1)).onBootPhaseAppsCanStart();
- }
-
- @Test
public void testHandleOnPackageChanged() {
String[] pkgs = new String[] {PKG, PKG_N_MR1};
int[] uids = new int[] {mUid, UserHandle.PER_USER_RANGE + 1};
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 07d467b..ea5bf52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -291,7 +291,8 @@
assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
.getSource(ITYPE_NAVIGATION_BAR).isVisible());
- policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+ true /* isGestureOnSystemBar */);
waitUntilWindowAnimatorIdle();
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -319,7 +320,8 @@
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
- policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+ true /* isGestureOnSystemBar */);
waitUntilWindowAnimatorIdle();
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -351,7 +353,8 @@
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
- policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+ true /* isGestureOnSystemBar */);
waitUntilWindowAnimatorIdle();
InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -402,7 +405,8 @@
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(app);
- policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+ true /* isGestureOnSystemBar */);
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
policy.updateBarControlTarget(app2);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index f8c84df..94bc7f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -332,7 +332,8 @@
assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
provider.getSource().setVisible(false);
- mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR });
+ mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR },
+ true /* isGestureOnSystemBar */);
assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR));
assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 0c65cc4..286cff9 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -688,6 +688,8 @@
String packageName,
PendingIntent pi,
int uid) {
+ boolean throwException = false;
+
// compare uid with packageName to foil apps pretending to be someone else
try {
ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
@@ -695,11 +697,13 @@
Slog.w(TAG, "package " + packageName
+ " does not match caller's uid " + uid);
EventLog.writeEvent(SNET_EVENT_LOG_ID, "180104273", -1, "");
- throw new IllegalArgumentException("package " + packageName
- + " not found");
+ throwException = true;
}
} catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("package " + packageName + " not found");
+ throwException = true;
+ } finally {
+ if (throwException)
+ throw new IllegalArgumentException("package " + packageName + " not found");
}
requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
index fa70c33..d77bf67 100644
--- a/telephony/java/android/telephony/CallQuality.java
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -81,6 +81,13 @@
private boolean mRtpInactivityDetected;
private boolean mRxSilenceDetected;
private boolean mTxSilenceDetected;
+ private int mNumVoiceFrames;
+ private int mNumNoDataFrames;
+ private int mNumDroppedRtpPackets;
+ private long mMinPlayoutDelayMillis;
+ private long mMaxPlayoutDelayMillis;
+ private int mNumRtpSidPacketsRx;
+ private int mNumRtpDuplicatePackets;
/** @hide **/
public CallQuality(Parcel in) {
@@ -98,6 +105,13 @@
mRtpInactivityDetected = in.readBoolean();
mRxSilenceDetected = in.readBoolean();
mTxSilenceDetected = in.readBoolean();
+ mNumVoiceFrames = in.readInt();
+ mNumNoDataFrames = in.readInt();
+ mNumDroppedRtpPackets = in.readInt();
+ mMinPlayoutDelayMillis = in.readLong();
+ mMaxPlayoutDelayMillis = in.readLong();
+ mNumRtpSidPacketsRx = in.readInt();
+ mNumRtpDuplicatePackets = in.readInt();
}
/** @hide **/
@@ -298,6 +312,59 @@
}
/**
+ * Returns the number of Voice frames sent by jitter buffer to audio
+ */
+ public int getNumVoiceFrames() {
+ return mNumVoiceFrames;
+ }
+
+ /**
+ * Returns the number of no-data frames sent by jitter buffer to audio
+ */
+ public int getNumNoDataFrames() {
+ return mNumNoDataFrames;
+ }
+
+ /**
+ * Returns the number of RTP voice packets dropped by jitter buffer
+ */
+ public int getNumDroppedRtpPackets() {
+ return mNumDroppedRtpPackets;
+ }
+
+ /**
+ * Returns the minimum playout delay in the reporting interval
+ * in milliseconds.
+ */
+ public long getMinPlayoutDelayMillis() {
+ return mMinPlayoutDelayMillis;
+ }
+
+ /**
+ * Returns the maximum playout delay in the reporting interval
+ * in milliseconds.
+ */
+ public long getMaxPlayoutDelayMillis() {
+ return mMaxPlayoutDelayMillis;
+ }
+
+ /**
+ * Returns the total number of RTP SID (Silence Insertion Descriptor) packets
+ * received by this device for an ongoing call
+ */
+ public int getNumRtpSidPacketsRx() {
+ return mNumRtpSidPacketsRx;
+ }
+
+ /**
+ * Returns the total number of RTP duplicate packets received by this device
+ * for an ongoing call
+ */
+ public int getNumRtpDuplicatePackets() {
+ return mNumRtpDuplicatePackets;
+ }
+
+ /**
* Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
* {@link ImsStreamMediaProfile}.
*
@@ -345,6 +412,13 @@
+ " rtpInactivityDetected=" + mRtpInactivityDetected
+ " txSilenceDetected=" + mTxSilenceDetected
+ " rxSilenceDetected=" + mRxSilenceDetected
+ + " numVoiceFrames=" + mNumVoiceFrames
+ + " numNoDataFrames=" + mNumNoDataFrames
+ + " numDroppedRtpPackets=" + mNumDroppedRtpPackets
+ + " minPlayoutDelayMillis=" + mMinPlayoutDelayMillis
+ + " maxPlayoutDelayMillis=" + mMaxPlayoutDelayMillis
+ + " numRtpSidPacketsRx=" + mNumRtpSidPacketsRx
+ + " numRtpDuplicatePackets=" + mNumRtpDuplicatePackets
+ "}";
}
@@ -364,7 +438,14 @@
mCodecType,
mRtpInactivityDetected,
mRxSilenceDetected,
- mTxSilenceDetected);
+ mTxSilenceDetected,
+ mNumVoiceFrames,
+ mNumNoDataFrames,
+ mNumDroppedRtpPackets,
+ mMinPlayoutDelayMillis,
+ mMaxPlayoutDelayMillis,
+ mNumRtpSidPacketsRx,
+ mNumRtpDuplicatePackets);
}
@Override
@@ -392,7 +473,14 @@
&& mCodecType == s.mCodecType
&& mRtpInactivityDetected == s.mRtpInactivityDetected
&& mRxSilenceDetected == s.mRxSilenceDetected
- && mTxSilenceDetected == s.mTxSilenceDetected);
+ && mTxSilenceDetected == s.mTxSilenceDetected
+ && mNumVoiceFrames == s.mNumVoiceFrames
+ && mNumNoDataFrames == s.mNumNoDataFrames
+ && mNumDroppedRtpPackets == s.mNumDroppedRtpPackets
+ && mMinPlayoutDelayMillis == s.mMinPlayoutDelayMillis
+ && mMaxPlayoutDelayMillis == s.mMaxPlayoutDelayMillis
+ && mNumRtpSidPacketsRx == s.mNumRtpSidPacketsRx
+ && mNumRtpDuplicatePackets == s.mNumRtpDuplicatePackets);
}
/**
@@ -420,6 +508,13 @@
dest.writeBoolean(mRtpInactivityDetected);
dest.writeBoolean(mRxSilenceDetected);
dest.writeBoolean(mTxSilenceDetected);
+ dest.writeInt(mNumVoiceFrames);
+ dest.writeInt(mNumNoDataFrames);
+ dest.writeInt(mNumDroppedRtpPackets);
+ dest.writeLong(mMinPlayoutDelayMillis);
+ dest.writeLong(mMaxPlayoutDelayMillis);
+ dest.writeInt(mNumRtpSidPacketsRx);
+ dest.writeInt(mNumRtpDuplicatePackets);
}
public static final @android.annotation.NonNull Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
@@ -431,4 +526,322 @@
return new CallQuality[size];
}
};
+
+ /**
+ * Provides a convenient way to set the fields of a {@link CallQuality} when creating a new
+ * instance.
+ *
+ * <p>The example below shows how you might create a new {@code CallQuality}:
+ *
+ * <pre><code>
+ *
+ * CallQuality callQuality = new CallQuality.Builder()
+ * .setNumRtpPacketsTransmitted(150)
+ * .setNumRtpPacketsReceived(200)
+ * .build();
+ * </code></pre>
+ */
+ public static final class Builder {
+
+ private int mDownlinkCallQualityLevel;
+ private int mUplinkCallQualityLevel;
+ private int mCallDuration;
+ private int mNumRtpPacketsTransmitted;
+ private int mNumRtpPacketsReceived;
+ private int mNumRtpPacketsTransmittedLost;
+ private int mNumRtpPacketsNotReceived;
+ private int mAverageRelativeJitter;
+ private int mMaxRelativeJitter;
+ private int mAverageRoundTripTime;
+ private int mCodecType;
+ private boolean mRtpInactivityDetected;
+ private boolean mRxSilenceDetected;
+ private boolean mTxSilenceDetected;
+ private int mNumVoiceFrames;
+ private int mNumNoDataFrames;
+ private int mNumDroppedRtpPackets;
+ private long mMinPlayoutDelayMillis;
+ private long mMaxPlayoutDelayMillis;
+ private int mNumRtpSidPacketsRx;
+ private int mNumRtpDuplicatePackets;
+
+ /**
+ * Set the downlink call quality level for ongoing call.
+ *
+ * @param downlinkCallQualityLevel the Downlink call quality level
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setDownlinkCallQualityLevel(
+ @CallQualityLevel int downlinkCallQualityLevel) {
+ mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+ return this;
+ }
+
+ /**
+ * Set the uplink call quality level for ongoing call.
+ *
+ * @param uplinkCallQualityLevel the Uplink call quality level
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setUplinkCallQualityLevel(
+ @CallQualityLevel int uplinkCallQualityLevel) {
+ mUplinkCallQualityLevel = uplinkCallQualityLevel;
+ return this;
+ }
+
+ /**
+ * Set the call duration in milliseconds.
+ *
+ * @param callDuration the call duration in milliseconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setCallDuration(int callDuration) {
+ mCallDuration = callDuration;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP packets sent for ongoing call.
+ *
+ * @param numRtpPacketsTransmitted RTP packets sent to network
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpPacketsTransmitted(int numRtpPacketsTransmitted) {
+ mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP packets received for ongoing call.
+ *
+ * @param numRtpPacketsReceived RTP packets received from network
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpPacketsReceived(int numRtpPacketsReceived) {
+ mNumRtpPacketsReceived = numRtpPacketsReceived;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP packets which were lost in network and never
+ * transmitted.
+ *
+ * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+ * transmitted
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpPacketsTransmittedLost(int numRtpPacketsTransmittedLost) {
+ mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP packets which were lost in network and never received.
+ *
+ * @param numRtpPacketsNotReceived RTP packets which were lost in network and
+ * never received
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpPacketsNotReceived(int numRtpPacketsNotReceived) {
+ mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+ return this;
+ }
+
+ /**
+ * Set the average relative jitter in milliseconds.
+ *
+ * @param averageRelativeJitter average relative jitter in milliseconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setAverageRelativeJitter(int averageRelativeJitter) {
+ mAverageRelativeJitter = averageRelativeJitter;
+ return this;
+ }
+
+ /**
+ * Set the maximum relative jitter in milliseconds.
+ *
+ * @param maxRelativeJitter maximum relative jitter in milliseconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setMaxRelativeJitter(int maxRelativeJitter) {
+ mMaxRelativeJitter = maxRelativeJitter;
+ return this;
+ }
+
+ /**
+ * Set the average round trip delay in milliseconds.
+ *
+ * @param averageRoundTripTime average round trip delay in milliseconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setAverageRoundTripTime(int averageRoundTripTime) {
+ mAverageRoundTripTime = averageRoundTripTime;
+ return this;
+ }
+
+ /**
+ * Set the codec type used in the ongoing call.
+ *
+ * @param codecType the codec type.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setCodecType(int codecType) {
+ mCodecType = codecType;
+ return this;
+ }
+
+ /**
+ * Set to be True if no incoming RTP is received for a continuous
+ * duration of 4 seconds.
+ *
+ * @param rtpInactivityDetected True if no incoming RTP is received for
+ * a continuous duration of 4 seconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setRtpInactivityDetected(boolean rtpInactivityDetected) {
+ mRtpInactivityDetected = rtpInactivityDetected;
+ return this;
+ }
+
+ /**
+ * Set to be True if only silence RTP packets are received for 20 seconds
+ * immediately after call is connected.
+ *
+ * @param rxSilenceDetected True if only silence RTP packets are received for 20 seconds
+ * immediately after call is connected
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setIncomingSilenceDetectedAtCallSetup(boolean rxSilenceDetected) {
+ mRxSilenceDetected = rxSilenceDetected;
+ return this;
+ }
+
+ /**
+ * Set to be True if only silence RTP packets are sent for 20 seconds immediately
+ * after call is connected.
+ *
+ * @param txSilenceDetected True if only silence RTP packets are sent for
+ * 20 seconds immediately after call is connected.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setOutgoingSilenceDetectedAtCallSetup(boolean txSilenceDetected) {
+ mTxSilenceDetected = txSilenceDetected;
+ return this;
+ }
+
+ /**
+ * Set the number of voice frames sent by jitter buffer to audio.
+ *
+ * @param numVoiceFrames Voice frames sent by jitter buffer to audio.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumVoiceFrames(int numVoiceFrames) {
+ mNumVoiceFrames = numVoiceFrames;
+ return this;
+ }
+
+ /**
+ * Set the number of no-data frames sent by jitter buffer to audio.
+ *
+ * @param numNoDataFrames no-data frames sent by jitter buffer to audio
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumNoDataFrames(int numNoDataFrames) {
+ mNumNoDataFrames = numNoDataFrames;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP Voice packets dropped by jitter buffer.
+ *
+ * @param numDroppedRtpPackets number of RTP Voice packets dropped by jitter buffer
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumDroppedRtpPackets(int numDroppedRtpPackets) {
+ mNumDroppedRtpPackets = numDroppedRtpPackets;
+ return this;
+ }
+
+ /**
+ * Set the minimum playout delay in the reporting interval in milliseconds.
+ *
+ * @param minPlayoutDelayMillis minimum playout delay in the reporting interval
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setMinPlayoutDelayMillis(long minPlayoutDelayMillis) {
+ mMinPlayoutDelayMillis = minPlayoutDelayMillis;
+ return this;
+ }
+
+ /**
+ * Set the maximum Playout delay in the reporting interval in milliseconds.
+ *
+ * @param maxPlayoutDelayMillis maximum Playout delay in the reporting interval
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setMaxPlayoutDelayMillis(long maxPlayoutDelayMillis) {
+ mMaxPlayoutDelayMillis = maxPlayoutDelayMillis;
+ return this;
+ }
+
+ /**
+ * Set the total number of RTP SID (Silence Insertion Descriptor)
+ * packets received by this device for an ongoing call.
+ *
+ * @param numRtpSidPacketsRx the total number of RTP SID packets received
+ * by this device for an ongoing call.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpSidPacketsRx(int numRtpSidPacketsRx) {
+ mNumRtpSidPacketsRx = numRtpSidPacketsRx;
+ return this;
+ }
+
+ /**
+ * Set the total number of RTP duplicate packets received by this device
+ * for an ongoing call.
+ *
+ * @param numRtpDuplicatePackets the total number of RTP duplicate packets
+ * received by this device for an ongoing call
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpDuplicatePackets(int numRtpDuplicatePackets) {
+ mNumRtpDuplicatePackets = numRtpDuplicatePackets;
+ return this;
+ }
+
+ /**
+ * Build the CallQuality.
+ *
+ * @return the CallQuality object.
+ */
+ public @NonNull CallQuality build() {
+
+ CallQuality callQuality = new CallQuality();
+ callQuality.mDownlinkCallQualityLevel = mDownlinkCallQualityLevel;
+ callQuality.mUplinkCallQualityLevel = mUplinkCallQualityLevel;
+ callQuality.mCallDuration = mCallDuration;
+ callQuality.mNumRtpPacketsTransmitted = mNumRtpPacketsTransmitted;
+ callQuality.mNumRtpPacketsReceived = mNumRtpPacketsReceived;
+ callQuality.mNumRtpPacketsTransmittedLost = mNumRtpPacketsTransmittedLost;
+ callQuality.mNumRtpPacketsNotReceived = mNumRtpPacketsNotReceived;
+ callQuality.mAverageRelativeJitter = mAverageRelativeJitter;
+ callQuality.mMaxRelativeJitter = mMaxRelativeJitter;
+ callQuality.mAverageRoundTripTime = mAverageRoundTripTime;
+ callQuality.mCodecType = mCodecType;
+ callQuality.mRtpInactivityDetected = mRtpInactivityDetected;
+ callQuality.mTxSilenceDetected = mTxSilenceDetected;
+ callQuality.mRxSilenceDetected = mRxSilenceDetected;
+ callQuality.mNumVoiceFrames = mNumVoiceFrames;
+ callQuality.mNumNoDataFrames = mNumNoDataFrames;
+ callQuality.mNumDroppedRtpPackets = mNumDroppedRtpPackets;
+ callQuality.mMinPlayoutDelayMillis = mMinPlayoutDelayMillis;
+ callQuality.mMaxPlayoutDelayMillis = mMaxPlayoutDelayMillis;
+ callQuality.mNumRtpSidPacketsRx = mNumRtpSidPacketsRx;
+ callQuality.mNumRtpDuplicatePackets = mNumRtpDuplicatePackets;
+
+ return callQuality;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8e55f75..d604dc1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5036,6 +5036,10 @@
/** Specifies the PCO id for IPv4 Epdg server address */
public static final String KEY_EPDG_PCO_ID_IPV4_INT = KEY_PREFIX + "epdg_pco_id_ipv4_int";
+ /** Controls if the IKE tunnel setup supports EAP-AKA fast reauth */
+ public static final String KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL =
+ KEY_PREFIX + "enable_support_for_eap_aka_fast_reauth_bool";
+
/** @hide */
@IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT})
public @interface AuthenticationMethodType {}
@@ -5179,6 +5183,7 @@
defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false);
defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0);
defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0);
+ defaults.putBoolean(KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL, false);
return defaults;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2295ed7..574a356 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -194,7 +194,7 @@
}
@Override
- protected T recompute(Void aVoid) {
+ public T recompute(Void aVoid) {
T result = mDefaultValue;
try {
@@ -228,7 +228,7 @@
}
@Override
- protected T recompute(Integer query) {
+ public T recompute(Integer query) {
T result = mDefaultValue;
try {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index eef7b57..e07a8f9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -99,11 +99,7 @@
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appLayerReplacesLauncher() {
- // This test doesn't work in shell transitions because of b/206094140
- assumeFalse(isShellTransitionsEnabled)
- super.appLayerReplacesLauncher()
- }
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index dfa8f8e..cd209b2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -96,20 +96,14 @@
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appWindowReplacesLauncherAsTopWindow() {
- // This test doesn't work in shell transitions because of b/206094140
- assumeFalse(isShellTransitionsEnabled)
- super.appWindowReplacesLauncherAsTopWindow()
- }
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
/** {@inheritDoc} */
@Presubmit
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- // This test doesn't work in shell transitions because of b/206094140
- assumeFalse(isShellTransitionsEnabled)
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
@FlakyTest
@@ -119,11 +113,7 @@
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appLayerReplacesLauncher() {
- // This test doesn't work in shell transitions because of b/206094140
- assumeFalse(isShellTransitionsEnabled)
- super.appLayerReplacesLauncher()
- }
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
@Presubmit
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index cb37fc7..0a88f6b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -23,6 +23,7 @@
android:supportsRtl="true">
<activity android:name=".SimpleActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SimpleApp"
android:exported="true">
<intent-filter>
@@ -32,6 +33,7 @@
</activity>
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="ImeApp"
android:exported="true">
<intent-filter>
@@ -40,6 +42,7 @@
</intent-filter>
</activity>
<activity android:name=".ImeActivityAutoFocus"
+ android:theme="@style/CutoutShortEdges"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
android:windowSoftInputMode="stateVisible"
android:label="ImeAppAutoFocus"
@@ -51,6 +54,7 @@
</activity>
<activity android:name=".SeamlessRotationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+ android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="SeamlessApp"
android:exported="true">
@@ -60,6 +64,7 @@
</intent-filter>
</activity>
<activity android:name=".NonResizeableActivity"
+ android:theme="@style/CutoutShortEdges"
android:resizeableActivity="false"
android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
android:label="NonResizeableApp"
@@ -72,6 +77,7 @@
</activity>
<activity android:name=".ButtonActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ButtonActivity"
+ android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="ButtonActivity"
android:exported="true">
@@ -82,6 +88,7 @@
</activity>
<activity android:name=".LaunchNewTaskActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewTaskActivity"
+ android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="LaunchNewTaskActivity"
android:exported="true">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
new file mode 100644
index 0000000..87a61a8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <style name="CutoutDefault">
+ <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ </style>
+
+ <style name="CutoutShortEdges">
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+
+ <style name="CutoutNever">
+ <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
similarity index 81%
rename from tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
rename to tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 476be44..d03aee2 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -15,8 +15,8 @@
*/
package android.net.vcn;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -27,13 +27,13 @@
import java.util.HashSet;
import java.util.Set;
-public class VcnCellUnderlyingNetworkPriorityTest {
+public class VcnCellUnderlyingNetworkTemplateTest {
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
// Package private for use in VcnGatewayConnectionConfigTest
- static VcnCellUnderlyingNetworkPriority getTestNetworkPriority() {
- return new VcnCellUnderlyingNetworkPriority.Builder()
+ static VcnCellUnderlyingNetworkTemplate getTestNetworkPriority() {
+ return new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS)
@@ -45,7 +45,7 @@
@Test
public void testBuilderAndGetters() {
- final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
assertTrue(networkPriority.allowMetered());
assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds());
@@ -56,8 +56,8 @@
@Test
public void testBuilderAndGettersForDefaultValues() {
- final VcnCellUnderlyingNetworkPriority networkPriority =
- new VcnCellUnderlyingNetworkPriority.Builder().build();
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
+ new VcnCellUnderlyingNetworkTemplate.Builder().build();
assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
assertFalse(networkPriority.allowMetered());
assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds());
@@ -68,10 +68,10 @@
@Test
public void testPersistableBundle() {
- final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
assertEquals(
networkPriority,
- VcnUnderlyingNetworkPriority.fromPersistableBundle(
+ VcnUnderlyingNetworkTemplate.fromPersistableBundle(
networkPriority.toPersistableBundle()));
}
}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 377f526..1f2905d 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -54,7 +54,7 @@
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
- private static final LinkedHashSet<VcnUnderlyingNetworkPriority> UNDERLYING_NETWORK_PRIORITIES =
+ private static final LinkedHashSet<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_PRIORITIES =
new LinkedHashSet();
static {
@@ -62,9 +62,9 @@
Arrays.sort(UNDERLYING_CAPS);
UNDERLYING_NETWORK_PRIORITIES.add(
- VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
UNDERLYING_NETWORK_PRIORITIES.add(
- VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
}
public static final long[] RETRY_INTERVALS_MS =
@@ -286,7 +286,7 @@
}
private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities(
- LinkedHashSet<VcnUnderlyingNetworkPriority> networkPriorities) {
+ LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPriorities) {
return buildTestConfigWithExposedCaps(
new VcnGatewayConnectionConfig.Builder(
"buildTestConfigWithVcnUnderlyingNetworkPriorities",
@@ -300,17 +300,17 @@
final VcnGatewayConnectionConfig config =
buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
- final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesEqual =
+ final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesEqual =
new LinkedHashSet();
- networkPrioritiesEqual.add(VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
- networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ networkPrioritiesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+ networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
final VcnGatewayConnectionConfig configEqual =
buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual);
- final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesNotEqual =
+ final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesNotEqual =
new LinkedHashSet();
networkPrioritiesNotEqual.add(
- VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
final VcnGatewayConnectionConfig configNotEqual =
buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual);
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
similarity index 76%
rename from tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java
rename to tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index dd272cb..652057f 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -15,8 +15,8 @@
*/
package android.net.vcn;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -26,13 +26,13 @@
import org.junit.Test;
-public class VcnWifiUnderlyingNetworkPriorityTest {
+public class VcnWifiUnderlyingNetworkTemplateTest {
private static final String SSID = "TestWifi";
private static final int INVALID_NETWORK_QUALITY = -1;
// Package private for use in VcnGatewayConnectionConfigTest
- static VcnWifiUnderlyingNetworkPriority getTestNetworkPriority() {
- return new VcnWifiUnderlyingNetworkPriority.Builder()
+ static VcnWifiUnderlyingNetworkTemplate getTestNetworkPriority() {
+ return new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setSsid(SSID)
@@ -41,7 +41,7 @@
@Test
public void testBuilderAndGetters() {
- final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
assertTrue(networkPriority.allowMetered());
assertEquals(SSID, networkPriority.getSsid());
@@ -49,8 +49,8 @@
@Test
public void testBuilderAndGettersForDefaultValues() {
- final VcnWifiUnderlyingNetworkPriority networkPriority =
- new VcnWifiUnderlyingNetworkPriority.Builder().build();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder().build();
assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
assertFalse(networkPriority.allowMetered());
assertNull(SSID, networkPriority.getSsid());
@@ -59,7 +59,7 @@
@Test
public void testBuildWithInvalidNetworkQuality() {
try {
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(INVALID_NETWORK_QUALITY);
fail("Expected to fail due to the invalid network quality");
} catch (Exception expected) {
@@ -68,10 +68,10 @@
@Test
public void testPersistableBundle() {
- final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
assertEquals(
networkPriority,
- VcnUnderlyingNetworkPriority.fromPersistableBundle(
+ VcnUnderlyingNetworkTemplate.fromPersistableBundle(
networkPriority.toPersistableBundle()));
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 46a614f..f23d5bf 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -16,7 +16,7 @@
package com.android.server.vcn.routeselection;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
@@ -39,10 +39,10 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnCellUnderlyingNetworkPriority;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager;
-import android.net.vcn.VcnWifiUnderlyingNetworkPriority;
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.test.TestLooper;
@@ -142,8 +142,8 @@
@Test
public void testMatchWithoutNotMeteredBit() {
- final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(false /* allowMetered */)
.build();
@@ -161,8 +161,8 @@
private void verifyMatchWifi(
boolean isSelectedNetwork, PersistableBundle carrierConfig, boolean expectMatch) {
- final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.build();
@@ -211,8 +211,8 @@
private void verifyMatchWifiWithSsid(boolean useMatchedSsid, boolean expectMatch) {
final String nwPrioritySsid = useMatchedSsid ? SSID : SSID_OTHER;
- final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setSsid(nwPrioritySsid)
@@ -237,8 +237,8 @@
verifyMatchWifiWithSsid(false /* useMatchedSsid */, false /* expectMatch */);
}
- private static VcnCellUnderlyingNetworkPriority.Builder getCellNetworkPriorityBuilder() {
- return new VcnCellUnderlyingNetworkPriority.Builder()
+ private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
+ return new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setAllowRoaming(true /* allowRoaming */);
@@ -257,7 +257,7 @@
@Test
public void testMatchOpportunisticCell() {
- final VcnCellUnderlyingNetworkPriority opportunisticCellNetworkPriority =
+ final VcnCellUnderlyingNetworkTemplate opportunisticCellNetworkPriority =
getCellNetworkPriorityBuilder()
.setRequireOpportunistic(true /* requireOpportunistic */)
.build();
@@ -277,7 +277,7 @@
private void verifyMatchMacroCellWithAllowedPlmnIds(
boolean useMatchedPlmnId, boolean expectMatch) {
final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
- final VcnCellUnderlyingNetworkPriority networkPriority =
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder()
.setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId))
.build();
@@ -306,7 +306,7 @@
private void verifyMatchMacroCellWithAllowedSpecificCarrierIds(
boolean useMatchedCarrierId, boolean expectMatch) {
final int networkPriorityCarrierId = useMatchedCarrierId ? CARRIER_ID : CARRIER_ID_OTHER;
- final VcnCellUnderlyingNetworkPriority networkPriority =
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder()
.setAllowedSpecificCarrierIds(Set.of(networkPriorityCarrierId))
.build();
@@ -335,7 +335,7 @@
@Test
public void testMatchWifiFailWithoutNotRoamingBit() {
- final VcnCellUnderlyingNetworkPriority networkPriority =
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder().setAllowRoaming(false /* allowRoaming */).build();
assertFalse(