Merge "Temporarily disable AppOpsServiceTest#testShutdown, remove unused imports" into sc-dev
diff --git a/Android.bp b/Android.bp
index 77044d6..f9320eb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -510,15 +510,6 @@
],
}
-filegroup {
- name: "framework-all-sources",
- srcs: [
- ":framework-mime-sources",
- ":framework-non-updatable-sources",
- ":framework-updatable-sources",
- ],
-}
-
// AIDL files under these paths are mixture of public and private ones.
// They shouldn't be exported across module boundaries.
java_defaults {
@@ -1391,14 +1382,6 @@
],
}
-// Creates an index of AIDL methods; used for adding UnsupportedAppUsage
-// annotations to private apis
-aidl_mapping {
- name: "framework-aidl-mappings",
- srcs: [":framework-all-sources"],
- output: "framework-aidl-mappings.txt",
-}
-
// Avoid including Parcelable classes as we don't want to have two copies of
// Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
// and TeleService app (packages/services/Telephony).
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 547aa16..4dd1b79 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -587,8 +587,28 @@
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
- // TODO(b/182909475): Implement getStorageInfo
- throw new UnsupportedOperationException();
+ try {
+ mService.getStorageInfo(
+ mPackageName,
+ mDatabaseName,
+ mUserId,
+ new IAppSearchResultCallback.Stub() {
+ public void onResult(AppSearchResult result) {
+ executor.execute(() -> {
+ if (result.isSuccess()) {
+ Bundle responseBundle = (Bundle) result.getResultValue();
+ StorageInfo response =
+ new StorageInfo(responseBundle);
+ callback.accept(AppSearchResult.newSuccessfulResult(response));
+ } else {
+ callback.accept(result);
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 48c397f..a8ac27c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -307,6 +307,22 @@
in IAppSearchResultCallback callback);
/**
+ * Gets the storage info.
+ *
+ * @param packageName The name of the package to get the storage info for.
+ * @param databaseName The databaseName to get the storage info for.
+ * @param userId Id of the calling user
+ * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+ * {@link AppSearchResult}<{@link Bundle}>, where the value is a
+ * {@link StorageInfo}.
+ */
+ void getStorageInfo(
+ in String packageName,
+ in String databaseName,
+ in int userId,
+ in IAppSearchResultCallback callback);
+
+ /**
* Persists all update/delete requests to the disk.
*
* @param userId Id of the calling user
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 91ed6cd..991dda7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -33,6 +33,7 @@
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.SetSchemaResponse;
+import android.app.appsearch.StorageInfo;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
@@ -610,6 +611,34 @@
}
@Override
+ public void getStorageInfo(
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @UserIdInt int userId,
+ @NonNull IAppSearchResultCallback callback) {
+ Preconditions.checkNotNull(packageName);
+ Preconditions.checkNotNull(databaseName);
+ Preconditions.checkNotNull(callback);
+ int callingUid = Binder.getCallingUid();
+ int callingUserId = handleIncomingUser(userId, callingUid);
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName, databaseName);
+ Bundle storageInfoBundle = storageInfo.getBundle();
+ invokeCallbackOnResult(
+ callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
public void persistToDisk(@UserIdInt int userId) {
int callingUid = Binder.getCallingUidOrThrow();
int callingUserId = handleIncomingUser(userId, callingUid);
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index ad68169..8d83309 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -25,7 +25,9 @@
}
public static final class MediaTranscodeManager.TranscodingSession {
+ method public void addClientUid(int);
method public void cancel();
+ method @NonNull public java.util.List<java.lang.Integer> getClientUids();
method public int getErrorCode();
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index 7f4685e..fef4865 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1352,6 +1352,8 @@
private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
@GuardedBy("mLock")
private boolean mHasRetried = false;
+ @GuardedBy("mLock")
+ private @NonNull List<Integer> mClientUidList = new ArrayList<>();
// The original request that associated with this session.
private final TranscodingRequest mRequest;
@@ -1370,6 +1372,7 @@
mListenerExecutor = executor;
mListener = listener;
mRequest = request;
+ mClientUidList.add(request.getClientUid());
}
/**
@@ -1515,6 +1518,36 @@
}
/**
+ * Adds a client uid that is also waiting for this transcoding session.
+ * <p>
+ * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
+ * uid. Note that the permission check happens on the service side upon starting the
+ * transcoding. If the client does not have the permission, the transcoding will fail.
+ */
+ public void addClientUid(int uid) {
+ if (uid < 0) {
+ throw new IllegalArgumentException("Invalid Uid");
+ }
+ synchronized (mLock) {
+ if (!mClientUidList.contains(uid)) {
+ // see ag/14023202 for implementation
+ mClientUidList.add(uid);
+ }
+ }
+ }
+
+ /**
+ * Query all the client that waiting for this transcoding session
+ * @return a list containing all the client uids.
+ */
+ @NonNull
+ public List<Integer> getClientUids() {
+ synchronized (mLock) {
+ return mClientUidList;
+ }
+ }
+
+ /**
* Gets sessionId of the transcoding session.
* @return session id.
*/
diff --git a/core/api/current.txt b/core/api/current.txt
index fb32055..f8654478 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18842,9 +18842,14 @@
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
method public android.view.Display[] getDisplays(String);
+ method public int getMatchContentFrameRateUserPreference();
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+ field public static final int MATCH_CONTENT_FRAMERATE_ALWAYS = 2; // 0x2
+ field public static final int MATCH_CONTENT_FRAMERATE_NEVER = 0; // 0x0
+ field public static final int MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY = 1; // 0x1
+ field public static final int MATCH_CONTENT_FRAMERATE_UNKNOWN = -1; // 0xffffffff
field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
@@ -51545,6 +51550,7 @@
field public static final int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5; // 0xfffffffb
field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
+ field public static final int DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS = -6; // 0xfffffffa
field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
}
@@ -52775,11 +52781,16 @@
}
public final class TranslationManager {
- method public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
- method @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext);
- method @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getTranslationCapabilities(int, int);
- method @Nullable public android.app.PendingIntent getTranslationSettingsActivityIntent();
- method public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+ method public void addOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+ method @Deprecated public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+ method @Nullable @WorkerThread public android.view.translation.Translator createOnDeviceTranslator(@NonNull android.view.translation.TranslationContext);
+ method @Deprecated @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext);
+ method @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getOnDeviceTranslationCapabilities(int, int);
+ method @Nullable public android.app.PendingIntent getOnDeviceTranslationSettingsActivityIntent();
+ method @Deprecated @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getTranslationCapabilities(int, int);
+ method @Deprecated @Nullable public android.app.PendingIntent getTranslationSettingsActivityIntent();
+ method public void removeOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+ method @Deprecated public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
}
public final class TranslationRequest implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a9413f5..d0f9094 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -180,6 +180,7 @@
field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String NETWORK_STATS_PROVIDER = "android.permission.NETWORK_STATS_PROVIDER";
+ field public static final String NFC_SET_CONTROLLER_ALWAYS_ON = "android.permission.NFC_SET_CONTROLLER_ALWAYS_ON";
field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
@@ -320,6 +321,7 @@
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
field public static final int sdkVersion = 16844304; // 0x1010610
field public static final int supportsAmbientMode = 16844173; // 0x101058d
+ field public static final int throttleDurationMillis = 16844360; // 0x1010648
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -7906,10 +7908,10 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnEnabled();
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnSupported();
+ method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
+ method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setAlwaysOn(boolean);
+ method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
}
@@ -9793,6 +9795,7 @@
method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
+ field public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
}
}
@@ -13668,13 +13671,13 @@
ctor @Deprecated public RcsFeature();
ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
- method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+ method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+ method public void destroyCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
method public boolean queryCapabilityConfiguration(int, int);
method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
- method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
}
public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
@@ -13887,7 +13890,7 @@
}
public class RcsCapabilityExchangeImplBase {
- ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
+ ctor public RcsCapabilityExchangeImplBase();
method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
method public void subscribeForCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 561d838d..56b0a9d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1155,7 +1155,6 @@
public final class DisplayManager {
method public boolean areUserDisabledHdrTypesAllowed();
- method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public int getRefreshRateSwitchingType();
method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
@@ -1657,6 +1656,7 @@
field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
field public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; // 0x182b7
field public static final int LAST_ISOLATED_UID = 99999; // 0x1869f
+ field public static final int NFC_UID = 1027; // 0x403
field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64
}
@@ -2877,6 +2877,14 @@
}
+package android.view.displayhash {
+
+ public final class DisplayHashManager {
+ method @RequiresPermission("android.permission.READ_FRAME_BUFFER") public void setDisplayHashThrottlingEnabled(boolean);
+ }
+
+}
+
package android.view.inputmethod {
public final class InlineSuggestion implements android.os.Parcelable {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d47c3ca..2fbea28 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5360,6 +5360,11 @@
// Use different highlighted colors except when low-priority mode prevents that
if (!p.mReduceHighlights) {
pillColor = getAccentTertiaryColor(p);
+ // TODO(b/183710694): The accent tertiary is currently too bright in dark mode, so
+ // we need to pick a contrasting color.
+ textColor = ColorUtils.setAlphaComponent(
+ ContrastColorUtil.resolvePrimaryColor(mContext, pillColor, mInNightMode),
+ 0xFF);
}
contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 216e7b0..6c2d140 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -38,6 +38,7 @@
import android.os.Build;
import android.os.Handler;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.Surface;
@@ -348,6 +349,37 @@
/** @hide */
+ @IntDef(prefix = {"MATCH_CONTENT_FRAMERATE_"}, value = {
+ MATCH_CONTENT_FRAMERATE_UNKNOWN,
+ MATCH_CONTENT_FRAMERATE_NEVER,
+ MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY,
+ MATCH_CONTENT_FRAMERATE_ALWAYS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MatchContentFrameRateType {}
+
+ /**
+ * Match content frame rate user preference is unknown.
+ */
+ public static final int MATCH_CONTENT_FRAMERATE_UNKNOWN = -1;
+
+ /**
+ * No mode switching is allowed.
+ */
+ public static final int MATCH_CONTENT_FRAMERATE_NEVER = 0;
+
+ /**
+ * Only refresh rate switches without visual interruptions are allowed.
+ */
+ public static final int MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY = 1;
+
+ /**
+ * Refresh rate switches between all refresh rates are allowed even if they have visual
+ * interruptions for the user.
+ */
+ public static final int MATCH_CONTENT_FRAMERATE_ALWAYS = 2;
+
+ /** @hide */
@IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
SWITCHING_TYPE_NONE,
SWITCHING_TYPE_WITHIN_GROUPS,
@@ -1076,14 +1108,36 @@
}
/**
- * Returns the refresh rate switching type.
- *
- * @hide
+ * 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
+ * original frame rate of the content.
+ * </p><p>
+ * Seamless: If the app requests it, the device will match its output to the original frame
+ * rate of the content, ONLY if the display can transition seamlessly.
+ * </p><p>
+ * Always: If the app requests it, the device will match its output to the original
+ * frame rate of the content. This may cause the screen to go blank for a
+ * second when exiting or entering a video playback.
+ * </p>
*/
- @TestApi
- @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
- @SwitchingType public int getRefreshRateSwitchingType() {
- return mGlobal.getRefreshRateSwitchingType();
+ @MatchContentFrameRateType public int getMatchContentFrameRateUserPreference() {
+ return toMatchContentFrameRateSetting(mGlobal.getRefreshRateSwitchingType());
+ }
+
+ @MatchContentFrameRateType
+ private int toMatchContentFrameRateSetting(@SwitchingType int switchingType) {
+ switch (switchingType) {
+ case SWITCHING_TYPE_NONE:
+ return MATCH_CONTENT_FRAMERATE_NEVER;
+ case SWITCHING_TYPE_WITHIN_GROUPS:
+ return MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY;
+ case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
+ return MATCH_CONTENT_FRAMERATE_ALWAYS;
+ default:
+ Slog.e(TAG, switchingType + " is not a valid value of switching type.");
+ return MATCH_CONTENT_FRAMERATE_UNKNOWN;
+ }
}
/**
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index bc3d5c4..11445e9 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -72,7 +72,7 @@
boolean deviceSupportsNfcSecure();
boolean setNfcSecure(boolean enable);
- boolean setAlwaysOn(boolean value);
- boolean isAlwaysOnEnabled();
- boolean isAlwaysOnSupported();
+ boolean setControllerAlwaysOn(boolean value);
+ boolean isControllerAlwaysOn();
+ boolean isControllerAlwaysOnSupported();
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index e85eb93..eed2c77 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -2254,13 +2254,13 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public boolean setAlwaysOn(boolean value) {
+ @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+ public boolean setControllerAlwaysOn(boolean value) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
try {
- return sService.setAlwaysOn(value);
+ return sService.setControllerAlwaysOn(value);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -2269,7 +2269,7 @@
return false;
}
try {
- return sService.setAlwaysOn(value);
+ return sService.setControllerAlwaysOn(value);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
@@ -2286,10 +2286,10 @@
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public boolean isAlwaysOnEnabled() {
+ @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+ public boolean isControllerAlwaysOn() {
try {
- return sService.isAlwaysOnEnabled();
+ return sService.isControllerAlwaysOn();
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -2298,7 +2298,7 @@
return false;
}
try {
- return sService.isAlwaysOnEnabled();
+ return sService.isControllerAlwaysOn();
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
@@ -2315,13 +2315,13 @@
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public boolean isAlwaysOnSupported() {
+ @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+ public boolean isControllerAlwaysOnSupported() {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
try {
- return sService.isAlwaysOnSupported();
+ return sService.isControllerAlwaysOnSupported();
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -2330,7 +2330,7 @@
return false;
}
try {
- return sService.isAlwaysOnSupported();
+ return sService.isControllerAlwaysOnSupported();
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index bf859d4..f9e4f73 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -131,6 +131,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @TestApi
public static final int NFC_UID = 1027;
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 12ff640..22d74ca 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9176,6 +9176,22 @@
public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
/**
+ * Whether the assistant can be triggered by a touch gesture.
+ *
+ * @hide
+ */
+ public static final String ASSIST_TOUCH_GESTURE_ENABLED =
+ "assist_touch_gesture_enabled";
+
+ /**
+ * Whether the assistant can be triggered by long-pressing the home button
+ *
+ * @hide
+ */
+ public static final String ASSIST_LONG_PRESS_HOME_ENABLED =
+ "assist_long_press_home_enabled";
+
+ /**
* Control whether Trust Agents are in active unlock or extend unlock mode.
* @hide
*/
@@ -9740,6 +9756,12 @@
"accessibility_magnification_mode";
/**
+ * Magnification mode value that is a default value for the magnification logging feature.
+ * @hide
+ */
+ public static final int ACCESSIBILITY_MAGNIFICATION_MODE_NONE = 0x0;
+
+ /**
* Magnification mode value that magnifies whole display.
* @hide
*/
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java
index 2105d84..d300cf1 100644
--- a/core/java/android/service/displayhash/DisplayHasherService.java
+++ b/core/java/android/service/displayhash/DisplayHasherService.java
@@ -52,6 +52,16 @@
"android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
/**
+ * Name under which a DisplayHasherService component publishes information
+ * about itself. This meta-data must reference an XML resource containing a
+ * {@link com.android.internal.R.styleable#DisplayHasherService} tag.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
+
+ /**
* The {@link Intent} action that must be declared as handled by a service in its manifest
* for the system to recognize it as a DisplayHash providing service.
*
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 572e50a..913ceae 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -70,7 +70,7 @@
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
DEFAULT_FLAGS.put("settings_silky_home", "true");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
- DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true");
+ DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "false");
}
diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java
index 547bc9d..b55d9b3 100644
--- a/core/java/android/view/ContentInfo.java
+++ b/core/java/android/view/ContentInfo.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.util.Pair;
+import android.view.inputmethod.InputContentInfo;
import com.android.internal.util.Preconditions;
@@ -141,6 +142,10 @@
private final Uri mLinkUri;
@Nullable
private final Bundle mExtras;
+ @Nullable
+ private final InputContentInfo mInputContentInfo;
+ @Nullable
+ private final DragAndDropPermissions mDragAndDropPermissions;
private ContentInfo(Builder b) {
this.mClip = Objects.requireNonNull(b.mClip);
@@ -149,6 +154,23 @@
this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
this.mLinkUri = b.mLinkUri;
this.mExtras = b.mExtras;
+ this.mInputContentInfo = b.mInputContentInfo;
+ this.mDragAndDropPermissions = b.mDragAndDropPermissions;
+ }
+
+ /**
+ * If the content came from a source that supports proactive release of URI permissions
+ * (e.g. IME), releases permissions; otherwise a no-op.
+ *
+ * @hide
+ */
+ public void releasePermissions() {
+ if (mInputContentInfo != null) {
+ mInputContentInfo.releasePermission();
+ }
+ if (mDragAndDropPermissions != null) {
+ mDragAndDropPermissions.release();
+ }
}
@NonNull
@@ -275,6 +297,10 @@
private Uri mLinkUri;
@Nullable
private Bundle mExtras;
+ @Nullable
+ private InputContentInfo mInputContentInfo;
+ @Nullable
+ private DragAndDropPermissions mDragAndDropPermissions;
/**
* Creates a new builder initialized with the data from the given builder.
@@ -285,6 +311,8 @@
mFlags = other.mFlags;
mLinkUri = other.mLinkUri;
mExtras = other.mExtras;
+ mInputContentInfo = other.mInputContentInfo;
+ mDragAndDropPermissions = other.mDragAndDropPermissions;
}
/**
@@ -355,6 +383,31 @@
}
/**
+ * Set the {@link InputContentInfo} object if the content is coming from the IME. This can
+ * be used for proactive cleanup of permissions.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setInputContentInfo(@Nullable InputContentInfo inputContentInfo) {
+ mInputContentInfo = inputContentInfo;
+ return this;
+ }
+
+ /**
+ * Set the {@link DragAndDropPermissions} object if the content is coming via drag-and-drop.
+ * This can be used for proactive cleanup of permissions.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setDragAndDropPermissions(@Nullable DragAndDropPermissions permissions) {
+ mDragAndDropPermissions = permissions;
+ return this;
+ }
+
+
+ /**
* @return A new {@link ContentInfo} instance with the data from this builder.
*/
@NonNull
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e1f13f2..d0a3e4b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -777,6 +777,15 @@
VerifiedDisplayHash verifyDisplayHash(in DisplayHash displayHash);
/**
+ * Call to enable or disable the throttling when generating a display hash. This should only be
+ * used for testing. Throttling is enabled by default.
+ *
+ * Must be called from a process that has {@link android.Manifest.permission#READ_FRAME_BUFFER}
+ * permission.
+ */
+ void setDisplayHashThrottlingEnabled(boolean enable);
+
+ /**
* Registers a listener for a {@link android.window.WindowContext} to handle configuration
* changes from the server side.
* <p>
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 8db6456..0686104 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -51,8 +51,8 @@
}
@Override
- public void onWindowFocusGained() {
- super.onWindowFocusGained();
+ public void onWindowFocusGained(boolean hasViewFocus) {
+ super.onWindowFocusGained(hasViewFocus);
getImm().registerImeConsumer(this);
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index a68f528..c001ec9 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1302,8 +1302,8 @@
/**
* Called when current window gains focus.
*/
- public void onWindowFocusGained() {
- getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+ public void onWindowFocusGained(boolean hasViewFocused) {
+ getSourceConsumer(ITYPE_IME).onWindowFocusGained(hasViewFocused);
}
/**
@@ -1366,8 +1366,9 @@
final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
// Skip showing animation once that made by system for some reason.
// (e.g. starting window with IME snapshot)
- if (imeControl != null && show) {
- skipAnim = imeControl.getAndClearSkipAnimationOnce();
+ if (imeControl != null) {
+ skipAnim = imeControl.getAndClearSkipAnimationOnce() && show
+ && consumer.hasViewFocusWhenWindowFocusGain();
}
}
applyAnimation(types, show, fromIme, skipAnim);
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index fd1c3b8..bc50dbe 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -81,6 +81,11 @@
private final Supplier<Transaction> mTransactionSupplier;
private @Nullable InsetsSourceControl mSourceControl;
private boolean mHasWindowFocus;
+
+ /**
+ * Whether the view has focus returned by {@link #onWindowFocusGained(boolean)}.
+ */
+ private boolean mHasViewFocusWhenWindowFocusGain;
private Rect mPendingFrame;
private Rect mPendingVisibleFrame;
@@ -223,8 +228,9 @@
/**
* Called when current window gains focus
*/
- public void onWindowFocusGained() {
+ public void onWindowFocusGained(boolean hasViewFocus) {
mHasWindowFocus = true;
+ mHasViewFocusWhenWindowFocusGain = hasViewFocus;
}
/**
@@ -238,6 +244,10 @@
return mHasWindowFocus;
}
+ boolean hasViewFocusWhenWindowFocusGain() {
+ return mHasViewFocusWhenWindowFocusGain;
+ }
+
boolean applyLocalVisibilityOverride() {
final InsetsSource source = mState.peekSource(mType);
final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType);
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
index 56b4383..d10fc44 100644
--- a/core/java/android/view/RoundedCorner.java
+++ b/core/java/android/view/RoundedCorner.java
@@ -29,16 +29,10 @@
/**
* Represents a rounded corner of the display.
- *
- * <code>
- * ________
- * / ^
- * / | Radius
- * | v
- * | X <- Center point
- * |<----->
- * Radius
- * </code>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/rounded_corner/rounded-corner-info.png" height="120"
+ * alt="A figure to describe what the rounded corner radius and the center point are. "/>
+ * </p>
*
* <p>Note: The rounded corner formed by the radius and the center is an approximation.</p>
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 615dd82..63a7dea 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -26801,8 +26801,10 @@
if (permissions != null) {
permissions.takeTransient();
}
- final ContentInfo payload = new ContentInfo.Builder(
- event.getClipData(), SOURCE_DRAG_AND_DROP).build();
+ final ContentInfo payload =
+ new ContentInfo.Builder(event.getClipData(), SOURCE_DRAG_AND_DROP)
+ .setDragAndDropPermissions(permissions)
+ .build();
ContentInfo remainingPayload = performReceiveContent(payload);
// Return true unless none of the payload was consumed.
return remainingPayload != payload;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0a246a6..426c950 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3344,8 +3344,9 @@
}
// TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
// config changes.
+ final View focusedView = mView != null ? mView.findFocus() : null;
if (hasWindowFocus) {
- mInsetsController.onWindowFocusGained();
+ mInsetsController.onWindowFocusGained(focusedView != null /* hasViewFocused */);
} else {
mInsetsController.onWindowFocusLost();
}
@@ -3394,8 +3395,7 @@
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
- mImeFocusController.onPostWindowFocus(mView != null ? mView.findFocus() : null,
- hasWindowFocus, mWindowAttributes);
+ mImeFocusController.onPostWindowFocus(focusedView, hasWindowFocus, mWindowAttributes);
if (hasWindowFocus) {
// Clear the forward bit. We can just do this directly, since
diff --git a/core/java/android/view/displayhash/DisplayHashManager.java b/core/java/android/view/displayhash/DisplayHashManager.java
index 6b0c1a6..69dfc38 100644
--- a/core/java/android/view/displayhash/DisplayHashManager.java
+++ b/core/java/android/view/displayhash/DisplayHashManager.java
@@ -21,7 +21,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
@@ -94,4 +96,19 @@
return null;
}
}
+
+ /**
+ * Call to enable or disable the throttling when generating a display hash. This should only be
+ * used for testing. Throttling is enabled by default.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
+ public void setDisplayHashThrottlingEnabled(boolean enable) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setDisplayHashThrottlingEnabled(enable);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java
index 04d29ee..6e3d9a8 100644
--- a/core/java/android/view/displayhash/DisplayHashResultCallback.java
+++ b/core/java/android/view/displayhash/DisplayHashResultCallback.java
@@ -72,13 +72,20 @@
*/
int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5;
+ /**
+ * The caller requested to generate the hash too frequently. The caller should try again at a
+ * after some time has passed to ensure the system isn't overloaded.
+ */
+ int DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS = -6;
+
/** @hide */
@IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = {
DISPLAY_HASH_ERROR_UNKNOWN,
DISPLAY_HASH_ERROR_INVALID_BOUNDS,
DISPLAY_HASH_ERROR_MISSING_WINDOW,
DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN,
- DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM
+ DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM,
+ DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS
})
@Retention(RetentionPolicy.SOURCE)
@interface DisplayHashErrorCode {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index c5bce28..9f796c0 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -959,10 +959,10 @@
}
final ClipData clip = new ClipData(inputContentInfo.getDescription(),
new ClipData.Item(inputContentInfo.getContentUri()));
- final ContentInfo payload =
- new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
+ final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
.setLinkUri(inputContentInfo.getLinkUri())
.setExtras(opts)
+ .setInputContentInfo(inputContentInfo)
.build();
return mTargetView.performReceiveContent(payload) == null;
}
diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java
index dfa7095..b89488b 100644
--- a/core/java/android/view/translation/TranslationManager.java
+++ b/core/java/android/view/translation/TranslationManager.java
@@ -121,17 +121,16 @@
}
/**
- * Create a Translator for translation.
+ * Creates an on-device Translator for natural language translation.
*
* <p><strong>NOTE: </strong>Call on a worker thread.
*
* @param translationContext {@link TranslationContext} containing the specs for creating the
* Translator.
- * @return a {@link Translator} to be used for calling translation APIs.
*/
@Nullable
@WorkerThread
- public Translator createTranslator(@NonNull TranslationContext translationContext) {
+ public Translator createOnDeviceTranslator(@NonNull TranslationContext translationContext) {
Objects.requireNonNull(translationContext, "translationContext cannot be null");
synchronized (mLock) {
@@ -166,8 +165,16 @@
}
}
+ /** @deprecated Use {@link #createOnDeviceTranslator(TranslationContext)} */
+ @Deprecated
+ @Nullable
+ @WorkerThread
+ public Translator createTranslator(@NonNull TranslationContext translationContext) {
+ return createOnDeviceTranslator(translationContext);
+ }
+
/**
- * Returns a set of {@link TranslationCapability}s describing the capabilities for
+ * Returns a set of {@link TranslationCapability}s describing the capabilities for on-device
* {@link Translator}s.
*
* <p>These translation capabilities contains a source and target {@link TranslationSpec}
@@ -184,7 +191,7 @@
*/
@NonNull
@WorkerThread
- public Set<TranslationCapability> getTranslationCapabilities(
+ public Set<TranslationCapability> getOnDeviceTranslationCapabilities(
@TranslationSpec.DataFormat int sourceFormat,
@TranslationSpec.DataFormat int targetFormat) {
try {
@@ -206,8 +213,18 @@
}
}
+ /** @deprecated Use {@link #getOnDeviceTranslationCapabilities(int, int)} */
+ @Deprecated
+ @NonNull
+ @WorkerThread
+ public Set<TranslationCapability> getTranslationCapabilities(
+ @TranslationSpec.DataFormat int sourceFormat,
+ @TranslationSpec.DataFormat int targetFormat) {
+ return getOnDeviceTranslationCapabilities(sourceFormat, targetFormat);
+ }
+
/**
- * Registers a {@link PendingIntent} to listen for updates on states of
+ * Registers a {@link PendingIntent} to listen for updates on states of on-device
* {@link TranslationCapability}s.
*
* <p>IMPORTANT: the pending intent must be called to start a service, or a broadcast if it is
@@ -217,7 +234,7 @@
* @param targetFormat data format for the expected translated output data.
* @param pendingIntent the pending intent to invoke when updates are received.
*/
- public void addTranslationCapabilityUpdateListener(
+ public void addOnDeviceTranslationCapabilityUpdateListener(
@TranslationSpec.DataFormat int sourceFormat,
@TranslationSpec.DataFormat int targetFormat,
@NonNull PendingIntent pendingIntent) {
@@ -231,14 +248,26 @@
}
/**
- * Unregisters a {@link PendingIntent} to listen for updates on states of
+ * @deprecated Use {@link #addOnDeviceTranslationCapabilityUpdateListener(int, int,
+ * PendingIntent)}
+ */
+ @Deprecated
+ public void addTranslationCapabilityUpdateListener(
+ @TranslationSpec.DataFormat int sourceFormat,
+ @TranslationSpec.DataFormat int targetFormat,
+ @NonNull PendingIntent pendingIntent) {
+ addOnDeviceTranslationCapabilityUpdateListener(sourceFormat, targetFormat, pendingIntent);
+ }
+
+ /**
+ * Unregisters a {@link PendingIntent} to listen for updates on states of on-device
* {@link TranslationCapability}s.
*
* @param sourceFormat data format for the input data to be translated.
* @param targetFormat data format for the expected translated output data.
* @param pendingIntent the pending intent to unregister
*/
- public void removeTranslationCapabilityUpdateListener(
+ public void removeOnDeviceTranslationCapabilityUpdateListener(
@TranslationSpec.DataFormat int sourceFormat,
@TranslationSpec.DataFormat int targetFormat,
@NonNull PendingIntent pendingIntent) {
@@ -262,10 +291,24 @@
}
}
+ /**
+ * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener(int, int,
+ * PendingIntent)}
+ */
+ @Deprecated
+ public void removeTranslationCapabilityUpdateListener(
+ @TranslationSpec.DataFormat int sourceFormat,
+ @TranslationSpec.DataFormat int targetFormat,
+ @NonNull PendingIntent pendingIntent) {
+ removeOnDeviceTranslationCapabilityUpdateListener(
+ sourceFormat, targetFormat, pendingIntent);
+ }
+
//TODO: Add method to propagate updates to mTCapabilityUpdateListeners
/**
- * Returns an immutable PendingIntent which can used by apps to launch translation settings.
+ * Returns an immutable PendingIntent which can be used to launch an activity to view/edit
+ * on-device translation settings.
*
* @return An immutable PendingIntent or {@code null} if one of reason met:
* <ul>
@@ -274,7 +317,7 @@
* </ul>
**/
@Nullable
- public PendingIntent getTranslationSettingsActivityIntent() {
+ public PendingIntent getOnDeviceTranslationSettingsActivityIntent() {
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.getServiceSettingsActivity(resultReceiver, mContext.getUserId());
@@ -289,6 +332,13 @@
}
}
+ /** @deprecated Use {@link #getOnDeviceTranslationSettingsActivityIntent()} */
+ @Deprecated
+ @Nullable
+ public PendingIntent getTranslationSettingsActivityIntent() {
+ return getOnDeviceTranslationSettingsActivityIntent();
+ }
+
void removeTranslator(int id) {
synchronized (mLock) {
mTranslators.remove(id);
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 6ba40c3..0fa6e16 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -254,7 +254,7 @@
viewsResult.put(translatedResult.keyAt(i), result);
} else {
final boolean isVirtualViewAdded =
- virtualViewsResult.indexOfKey(autofillId.getViewId()) > 0;
+ virtualViewsResult.indexOfKey(autofillId.getViewId()) >= 0;
final LongSparseArray<ViewTranslationResponse> childIds =
isVirtualViewAdded ? virtualViewsResult.get(autofillId.getViewId())
: new LongSparseArray<>();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 238ce85..48e25c51 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2894,8 +2894,8 @@
final int originalLength = mTextView.getText().length();
Selection.setSelection((Spannable) mTextView.getText(), offset);
final ClipData clip = event.getClipData();
- final ContentInfo payload =
- new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
+ final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
+ .setDragAndDropPermissions(permissions)
.build();
mTextView.performReceiveContent(payload);
if (dragDropIntoItself) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index cb2bba1..29c78b5 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6342,6 +6342,9 @@
/**
* Set the ID of the top-level view of the XML layout.
*
+ * The view's ID is changed right after inflation, before it gets added to its parent. The ID
+ * of a given view can never change after the initial inflation.
+ *
* Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
*
* @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 7baa53b..a600a94 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -133,6 +133,17 @@
duration);
}
+ /**
+ * Logs the activated mode of the magnification when the IME window is shown on the screen.
+ * Calls this when the magnification is enabled and the IME window is shown on the screen.
+ *
+ * @param mode The activated magnification mode.
+ */
+ public static void logMagnificationModeWithImeOn(int mode) {
+ FrameworkStatsLog.write(FrameworkStatsLog.MAGNIFICATION_MODE_WITH_IME_ON_REPORTED,
+ convertToLoggingMagnificationMode(mode));
+ }
+
private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) {
switch (shortcutType) {
case ACCESSIBILITY_BUTTON:
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index f0badbe..530cb44 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -123,6 +123,8 @@
optional SettingProto gesture_silence_alerts_enabled = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto gesture_wake_enabled = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Assist assist = 7;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 994fe6b..00b165e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2016,6 +2016,12 @@
android:label="@string/permlab_preferredPaymentInfo"
android:protectionLevel="normal" />
+ <!-- @SystemApi Allows access to set NFC controller always on states.
+ <p>Protection level: signature|privileged
+ @hide -->
+ <permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
Applications holding this permission can access OMAPI reset system API
and bypass OMAPI AccessControlEnforcer.
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
index 16ffaa4..def429e 100644
--- a/core/res/res/layout/chooser_action_button.xml
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -19,13 +19,13 @@
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:drawablePadding="8dp"
- android:textColor="?android:textColorPrimary"
+ android:textColor="@color/text_color_primary_device_default_light"
android:textSize="12sp"
android:maxWidth="192dp"
android:singleLine="true"
android:clickable="true"
android:background="@drawable/chooser_action_button_bg"
- android:drawableTint="?android:textColorPrimary"
+ android:drawableTint="@color/text_color_primary_device_default_light"
android:drawableTintMode="src_in"
style="?android:attr/borderlessButtonStyle"
/>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 42c2c69..ca549ae 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -30,9 +30,7 @@
<!-- Height of the status bar -->
<dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
<!-- Height of area above QQS where battery/time go -->
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">152dp</dimen>
+ <dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Default height of an action bar. -->
<dimen name="action_bar_default_height">40dip</dimen>
<!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 74f8ff6..eea3799 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9539,4 +9539,10 @@
<attr name="iconfactoryBadgeSize" format="dimension"/>
<!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->
<attr name="lStar" format="float"/>
+
+ <declare-styleable name="DisplayHasherService">
+ <!-- The throttle duration for display hash requests
+ @hide @SystemApi -->
+ <attr name="throttleDurationMillis" format="integer" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index afbbe46..cf5b4e1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -47,10 +47,6 @@
<dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
<!-- Height of area above QQS where battery/time go -->
<dimen name="quick_qs_offset_height">48dp</dimen>
- <!-- Total height of QQS (quick_qs_offset_height + 128) -->
- <dimen name="quick_qs_total_height">176dp</dimen>
- <!-- Total height of QQS with two rows to fit media player (quick_qs_offset_height + 176) -->
- <dimen name="quick_qs_total_height_with_media">224dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_height">48dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 49039f9..aea9d60 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3096,6 +3096,8 @@
<!-- @hide @SystemApi -->
<public name="playHomeTransitionSound" />
<public name="lStar" />
+ <!-- @hide @SystemApi -->
+ <public name="throttleDurationMillis" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b834a06..ce60633 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -745,7 +745,7 @@
<!-- Text shown when viewing channel settings for notifications related to accessibility
security policy. [CHAR_LIMIT=NONE]-->
- <string name="notification_channel_accessibility_security_policy">Accessibility security policy</string>
+ <string name="notification_channel_accessibility_security_policy">Accessibility usage</string>
<!-- Label for foreground service notification when one app is running.
[CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8621147..beed9a3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1716,8 +1716,6 @@
<java-symbol type="dimen" name="status_bar_height" />
<java-symbol type="dimen" name="display_cutout_touchable_region_size" />
<java-symbol type="dimen" name="quick_qs_offset_height" />
- <java-symbol type="dimen" name="quick_qs_total_height" />
- <java-symbol type="dimen" name="quick_qs_total_height_with_media" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 47556c3..34a1fd8 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -97,7 +97,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// test if setVisibility can show IME
- mImeConsumer.onWindowFocusGained();
+ mImeConsumer.onWindowFocusGained(true);
mController.show(WindowInsets.Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -116,7 +116,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
- mImeConsumer.onWindowFocusGained();
+ mImeConsumer.onWindowFocusGained(true);
mController.show(WindowInsets.Type.ime(), true /* fromIme */);
// set control and verify visibility is applied.
@@ -132,24 +132,58 @@
}
@Test
- public void testImeGetAndClearSkipAnimationOnce() {
+ public void testImeGetAndClearSkipAnimationOnce_expectSkip() {
+ // Expect IME animation will skipped when the IME is visible at first place.
+ verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, true /* hasViewFocus */,
+ true /* expectSkipAnim */);
+ }
+
+ @Test
+ public void testImeGetAndClearSkipAnimationOnce_expectNoSkip() {
+ // Expect IME animation will not skipped if previously no view focused when gained the
+ // window focus and requesting the IME visible next time.
+ verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, false /* hasViewFocus */,
+ false /* expectSkipAnim */);
+ }
+
+ private void verifyImeGetAndClearSkipAnimationOnce(boolean hasWindowFocus, boolean hasViewFocus,
+ boolean expectSkipAnim) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
- mImeConsumer.onWindowFocusGained();
- mController.show(WindowInsets.Type.ime(), true /* fromIme */);
+ mImeConsumer.onWindowFocusGained(hasWindowFocus);
+ final boolean imeVisible = hasWindowFocus && hasViewFocus;
+ if (imeVisible) {
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */);
+ }
// set control and verify visibility is applied.
InsetsSourceControl control = Mockito.spy(
new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE));
// Simulate IME source control set this flag when the target has starting window.
control.setSkipAnimationOnce(true);
- mController.onControlsChanged(new InsetsSourceControl[] { control });
- // Verify IME show animation should be triggered when control becomes available and
- // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
- verify(control).getAndClearSkipAnimationOnce();
- verify(mController).applyAnimation(
- eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
- eq(true) /* skipAnim */);
+
+ if (imeVisible) {
+ // Verify IME applyAnimation should be triggered when control becomes available,
+ // and expect skip animation state after getAndClearSkipAnimationOnce invoked.
+ mController.onControlsChanged(new InsetsSourceControl[]{ control });
+ verify(control).getAndClearSkipAnimationOnce();
+ verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+ eq(true) /* show */, eq(false) /* fromIme */,
+ eq(expectSkipAnim) /* skipAnim */);
+ }
+
+ // If previously hasViewFocus is false, verify when requesting the IME visible next
+ // time will not skip animation.
+ if (!hasViewFocus) {
+ mController.show(WindowInsets.Type.ime(), true);
+ mController.onControlsChanged(new InsetsSourceControl[]{ control });
+ // Verify IME show animation should be triggered when control becomes available and
+ // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
+ verify(control).getAndClearSkipAnimationOnce();
+ verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+ eq(true) /* show */, eq(true) /* fromIme */,
+ eq(false) /* skipAnim */);
+ }
});
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 4390546..6301f32 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -234,7 +234,7 @@
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+ mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
mController.show(Type.ime(), true /* fromIme */);
mController.show(Type.all());
@@ -260,7 +260,7 @@
InsetsSourceControl ime = createControl(ITYPE_IME);
mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+ mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
mController.show(Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 549e074..5aacfdd 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -13,3 +13,4 @@
yamasani@google.com
per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
+per-file services.core.protolog.json = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/docs/html/reference/images/rounded_corner/rounded-corner-info.png b/docs/html/reference/images/rounded_corner/rounded-corner-info.png
new file mode 100644
index 0000000..312d935
--- /dev/null
+++ b/docs/html/reference/images/rounded_corner/rounded-corner-info.png
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index bacff78..b64c796 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -27,9 +27,10 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Rect;
import android.view.SurfaceControl;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import androidx.annotation.Nullable;
@@ -75,7 +76,7 @@
mDividerSize = mDividerWindowWidth - mDividerInsets * 2;
mRootBounds.set(configuration.windowConfiguration.getBounds());
- mDividerSnapAlgorithm = getSnapAlgorithm(context.getResources(), mRootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
resetDividerPosition();
}
@@ -114,7 +115,7 @@
mContext = mContext.createConfigurationContext(configuration);
mSplitWindowManager.setConfiguration(configuration);
mRootBounds.set(rootBounds);
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext.getResources(), mRootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
resetDividerPosition();
// Don't inflate divider bar if it is not initialized.
@@ -217,15 +218,15 @@
return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss);
}
- private DividerSnapAlgorithm getSnapAlgorithm(Resources resources, Rect rootBounds) {
+ private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) {
final boolean isLandscape = isLandscape(rootBounds);
return new DividerSnapAlgorithm(
- resources,
+ context.getResources(),
rootBounds.width(),
rootBounds.height(),
mDividerSize,
!isLandscape,
- new Rect() /* insets */,
+ getDisplayInsets(context),
isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}
@@ -250,6 +251,15 @@
animator.start();
}
+ private static Rect getDisplayInsets(Context context) {
+ return context.getSystemService(WindowManager.class)
+ .getMaximumWindowMetrics()
+ .getWindowInsets()
+ .getInsets(WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.statusBars()
+ | WindowInsets.Type.displayCutout()).toRect();
+ }
+
private static boolean isLandscape(Rect bounds) {
return bounds.width() > bounds.height();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index e7cd38f..01a81d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -40,15 +40,17 @@
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
WindowContainerTransaction wct) {
final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setHidden(rootToken, false)
- .setBounds(rootToken, rootBounds)
+ wct.setBounds(rootToken, rootBounds)
.reparent(task.token, rootToken, true /* onTop*/)
// Moving the root task to top after the child tasks were repareted , or the root
// task cannot be visible and focused.
- .reorder(rootToken, true);
+ .reorder(rootToken, true /* onTop */);
}
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+ // No matter if the root task is empty or not, moving the root to bottom because it no
+ // longer preserves visible child task.
+ wct.reorder(mRootTaskInfo.token, false /* onTop */);
if (mChildrenTaskInfo.size() == 0) return false;
wct.reparentTasks(
mRootTaskInfo.token,
@@ -62,10 +64,7 @@
boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
if (task == null) return false;
-
- wct.setHidden(mRootTaskInfo.token, true)
- .reorder(mRootTaskInfo.token, false)
- .reparent(task.token, newParent, false /* onTop */);
+ wct.reparent(task.token, newParent, false /* onTop */);
return true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 55d0b7d..1302314 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -126,6 +126,7 @@
labelRes = app.labelRes;
}
+ final int taskId = taskInfo.taskId;
Context context = mContext;
// replace with the default theme if the application didn't set
final int theme = windowInfo.splashScreenThemeResId != 0
@@ -153,6 +154,7 @@
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Failed creating package context with package name "
+ activityInfo.packageName + " for user " + taskInfo.userId, e);
+ return;
}
}
@@ -167,15 +169,21 @@
final TypedArray typedArray = overrideContext.obtainStyledAttributes(
com.android.internal.R.styleable.Window);
final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
- if (resId != 0 && overrideContext.getDrawable(resId) != null) {
- // We want to use the windowBackground for the override context if it is
- // available, otherwise we use the default one to make sure a themed starting
- // window is displayed for the app.
- if (DEBUG_SPLASH_SCREEN) {
- Slog.d(TAG, "addSplashScreen: apply overrideConfig"
- + taskConfig + " to starting window resId=" + resId);
+ try {
+ if (resId != 0 && overrideContext.getDrawable(resId) != null) {
+ // We want to use the windowBackground for the override context if it is
+ // available, otherwise we use the default one to make sure a themed starting
+ // window is displayed for the app.
+ if (DEBUG_SPLASH_SCREEN) {
+ Slog.d(TAG, "addSplashScreen: apply overrideConfig"
+ + taskConfig + " to starting window resId=" + resId);
+ }
+ context = overrideContext;
}
- context = overrideContext;
+ } catch (Resources.NotFoundException e) {
+ Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: "
+ + taskId, e);
+ return;
}
typedArray.recycle();
}
@@ -258,7 +266,6 @@
params.setTitle("Splash Screen " + activityInfo.packageName);
// TODO(b/173975965) tracking performance
- final int taskId = taskInfo.taskId;
SplashScreenView sView = null;
try {
final View view = win.getDecorView();
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 35bab7a..fcd333f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -73,7 +73,8 @@
fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName)
+ visibleRegion(primaryLayerName)
+ .coversExactly(getPrimaryRegion(dividerRegion, rotation))
}
}
@@ -83,7 +84,8 @@
) {
assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName)
+ visibleRegion(primaryLayerName)
+ .coversExactly(getPrimaryRegion(dividerRegion, rotation))
}
}
@@ -93,7 +95,8 @@
) {
assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName)
+ visibleRegion(secondaryLayerName)
+ .coversExactly(getSecondaryRegion(dividerRegion, rotation))
}
}
@@ -103,7 +106,8 @@
) {
assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName)
+ visibleRegion(secondaryLayerName)
+ .coversExactly(getSecondaryRegion(dividerRegion, rotation))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 63e9a78..614530b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -82,10 +82,10 @@
fun appsEndingBounds() {
testSpec.assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion),
- primaryApp.defaultWindowName)
- .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion),
- secondaryApp.defaultWindowName)
+ visibleRegion(primaryApp.defaultWindowName)
+ .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
+ visibleRegion(secondaryApp.defaultWindowName)
+ .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 234dda4..ef68ed6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -87,10 +87,10 @@
fun appsStartingBounds() {
testSpec.assertLayersStart {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion),
- primaryApp.defaultWindowName)
- coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion),
- secondaryApp.defaultWindowName)
+ visibleRegion(primaryApp.defaultWindowName)
+ .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
+ visibleRegion(secondaryApp.defaultWindowName)
+ .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index f40a08c..33ade38 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -183,8 +183,8 @@
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- this.coversExactly(topAppBounds, "SimpleActivity")
- .coversExactly(bottomAppBounds, "ImeActivity")
+ visibleRegion("SimpleActivity").coversExactly(topAppBounds)
+ visibleRegion("ImeActivity").coversExactly(bottomAppBounds)
}
}
@@ -203,8 +203,8 @@
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- this.coversExactly(topAppBounds, sSimpleActivity)
- .coversExactly(bottomAppBounds, sImeActivity)
+ visibleRegion(sSimpleActivity).coversExactly(topAppBounds)
+ visibleRegion(sImeActivity).coversExactly(bottomAppBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 0333227..1ba1f3b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -90,8 +90,8 @@
@Test
fun testAppCoversFullScreenWithPipOnDisplay() {
testSpec.assertLayersStart {
- coversExactly(displayBounds, testApp.defaultWindowName)
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ visibleRegion(testApp.defaultWindowName).coversExactly(displayBounds)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
}
}
@@ -99,7 +99,7 @@
@Test
fun pipAppCoversFullScreen() {
testSpec.assertLayersEnd {
- coversExactly(displayBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index ba88ee5..dd6195c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -116,7 +116,7 @@
@Test
fun pipAppLayerHidesTestApp() {
testSpec.assertLayersStart {
- coversExactly(startingBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversExactly(startingBounds)
isInvisible(testApp.defaultWindowName)
}
}
@@ -125,7 +125,7 @@
@Test
fun testAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
- coversExactly(endingBounds, testApp.defaultWindowName)
+ visibleRegion(testApp.defaultWindowName).coversExactly(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index bf148bc..6166721 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -116,8 +116,8 @@
@Test
fun bothAppLayersVisible() {
testSpec.assertLayersEnd {
- coversAtMost(displayBounds, testApp.defaultWindowName)
- coversAtMost(displayBounds, imeApp.defaultWindowName)
+ visibleRegion(testApp.defaultWindowName).coversAtMost(displayBounds)
+ visibleRegion(imeApp.defaultWindowName).coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
index f554ca3..acc013a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -63,7 +63,7 @@
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 8ceef8a..852ee47 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -26,11 +26,11 @@
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -91,8 +91,8 @@
@Test
fun appLayerRotates_StartingBounds() {
testSpec.assertLayersStart {
- coversExactly(startingBounds, fixedApp.defaultWindowName)
- coversAtMost(startingBounds, pipApp.defaultWindowName)
+ visibleRegion(fixedApp.defaultWindowName).coversExactly(startingBounds)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
}
}
@@ -100,8 +100,8 @@
@Test
fun appLayerRotates_EndingBounds() {
testSpec.assertLayersEnd {
- coversExactly(endingBounds, fixedApp.defaultWindowName)
- coversAtMost(endingBounds, pipApp.defaultWindowName)
+ visibleRegion(fixedApp.defaultWindowName).coversExactly(endingBounds)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 945a20b..6f17a2c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -83,7 +83,7 @@
@Test
fun testAppCoversFullScreen() {
testSpec.assertLayersStart {
- coversExactly(displayBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
}
}
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 102af92..9aab7f3 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
@@ -91,7 +91,7 @@
@Test
fun pipWindowInsideDisplay() {
testSpec.assertWmStart {
- coversAtMost(startingBounds, pipApp.defaultWindowName)
+ frameRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
}
}
@@ -107,7 +107,7 @@
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
- coversAtMost(startingBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
}
}
@@ -121,7 +121,7 @@
@Test
fun pipAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
- coversExactly(endingBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversExactly(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 8ae0a73..78b3d4e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -112,7 +112,8 @@
spyOn(context);
spyOn(realWindowManager);
try {
- doReturn(context).when(context).createPackageContext(anyString(), anyInt());
+ doReturn(context).when(context)
+ .createPackageContextAsUser(anyString(), anyInt(), any());
} catch (PackageManager.NameNotFoundException e) {
//
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 168a863..1e90b7c 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1379,6 +1379,11 @@
enum : uint32_t {
// Additional flag indicating an entry is public.
SPEC_PUBLIC = 0x40000000u,
+
+ // Additional flag indicating the resource id for this resource may change in a future
+ // build. If this flag is set, the SPEC_PUBLIC flag is also set since the resource must be
+ // public to be exposed as an API to other applications.
+ SPEC_STAGED_API = 0x20000000u,
};
};
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 0a9560a..7428c6e 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -311,6 +311,7 @@
field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
+ field public static final int NET_CAPABILITY_HEAD_UNIT = 32; // 0x20
field public static final int NET_CAPABILITY_IA = 7; // 0x7
field public static final int NET_CAPABILITY_IMS = 4; // 0x4
field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
@@ -334,6 +335,7 @@
field public static final int TRANSPORT_CELLULAR = 0; // 0x0
field public static final int TRANSPORT_ETHERNET = 3; // 0x3
field public static final int TRANSPORT_LOWPAN = 6; // 0x6
+ field public static final int TRANSPORT_USB = 8; // 0x8
field public static final int TRANSPORT_VPN = 4; // 0x4
field public static final int TRANSPORT_WIFI = 1; // 0x1
field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 970a236..2065559 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -179,10 +179,12 @@
}
public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
- ctor public VpnTransportInfo(int);
+ ctor public VpnTransportInfo(int, @Nullable String);
method public int describeContents();
+ method @NonNull public android.net.VpnTransportInfo makeCopy(long);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+ field @Nullable public final String sessionId;
field public final int type;
}
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 95df8b8..6c3b620 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -219,7 +219,7 @@
method public void onAutomaticReconnectDisabled();
method public void onBandwidthUpdateRequested();
method public void onNetworkCreated();
- method public void onNetworkDisconnected();
+ method public void onNetworkDestroyed();
method public void onNetworkUnwanted();
method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
method public void onQosCallbackUnregistered(int);
@@ -238,6 +238,7 @@
method public final void sendQosSessionLost(int, int, int);
method public final void sendSocketKeepaliveEvent(int, int);
method @Deprecated public void setLegacySubtype(int, @NonNull String);
+ method public void setTeardownDelayMs(@IntRange(from=0, to=0x1388) int);
method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method public void unregister();
field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 3939c7f..350b47f 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -924,6 +924,7 @@
BLOCKED_REASON_DOZE,
BLOCKED_REASON_APP_STANDBY,
BLOCKED_REASON_RESTRICTED_MODE,
+ BLOCKED_REASON_LOCKDOWN_VPN,
BLOCKED_METERED_REASON_DATA_SAVER,
BLOCKED_METERED_REASON_USER_RESTRICTED,
BLOCKED_METERED_REASON_ADMIN_DISABLED,
@@ -3715,7 +3716,8 @@
public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
/**
- * Called when access to the specified network is blocked or unblocked.
+ * Called when access to the specified network is blocked or unblocked, or the reason for
+ * access being blocked changes.
*
* If a NetworkCallback object implements this method,
* {@link #onBlockedStatusChanged(Network, boolean)} will not be called.
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
index f9d3994..d941d4b 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
@@ -47,5 +47,5 @@
void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
void onQosCallbackUnregistered(int qosCallbackId);
void onNetworkCreated();
- void onNetworkDisconnected();
+ void onNetworkDestroyed();
}
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
index cbd6193..26cb1ed 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
@@ -41,4 +41,5 @@
void sendNrQosSessionAvailable(int callbackId, in QosSession session, in NrQosSessionAttributes attributes);
void sendQosSessionLost(int qosCallbackId, in QosSession session);
void sendQosCallbackError(int qosCallbackId, int exceptionType);
+ void sendTeardownDelayMs(int teardownDelayMs);
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index 6b55bb7..c57da53 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -185,6 +185,20 @@
public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5;
/**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current value of the teardown
+ * delay.
+ * arg1 = teardown delay in milliseconds
+ * @hide
+ */
+ public static final int EVENT_TEARDOWN_DELAY_CHANGED = BASE + 6;
+
+ /**
+ * The maximum value for the teardown delay, in milliseconds.
+ * @hide
+ */
+ public static final int MAX_TEARDOWN_DELAY_MS = 5000;
+
+ /**
* Sent by ConnectivityService to the NetworkAgent to inform the agent of the
* networks status - whether we could use the network or could not, due to
* either a bad network configuration (no internet link) or captive portal.
@@ -197,7 +211,6 @@
*/
public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
-
/**
* Network validation suceeded.
* Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}.
@@ -376,7 +389,7 @@
*
* @hide
*/
- public static final int CMD_NETWORK_DISCONNECTED = BASE + 23;
+ public static final int CMD_NETWORK_DESTROYED = BASE + 23;
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
@@ -581,8 +594,8 @@
onNetworkCreated();
break;
}
- case CMD_NETWORK_DISCONNECTED: {
- onNetworkDisconnected();
+ case CMD_NETWORK_DESTROYED: {
+ onNetworkDestroyed();
break;
}
}
@@ -732,8 +745,8 @@
}
@Override
- public void onNetworkDisconnected() {
- mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DISCONNECTED));
+ public void onNetworkDestroyed() {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DESTROYED));
}
}
@@ -851,6 +864,29 @@
}
/**
+ * Sets the value of the teardown delay.
+ *
+ * The teardown delay is the time between when the network disconnects and when the native
+ * network corresponding to this {@code NetworkAgent} is destroyed. By default, the native
+ * network is destroyed immediately. If {@code teardownDelayMs} is non-zero, then when this
+ * network disconnects, the system will instead immediately mark the network as restricted
+ * and unavailable to unprivileged apps, but will defer destroying the native network until the
+ * teardown delay timer expires.
+ *
+ * The interfaces in use by this network will remain in use until the native network is
+ * destroyed and cannot be reused until {@link #onNetworkDestroyed()} is called.
+ *
+ * This method may be called at any time while the network is connected. It has no effect if
+ * the network is already disconnected and the teardown delay timer is running.
+ *
+ * @param teardownDelayMs the teardown delay to set, or 0 to disable teardown delay.
+ */
+ public void setTeardownDelayMs(
+ @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int teardownDelayMs) {
+ queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMs));
+ }
+
+ /**
* Change the legacy subtype of this network agent.
*
* This is only for backward compatibility and should not be used by non-legacy network agents,
@@ -1053,7 +1089,7 @@
/**
* Called when ConnectivityService has successfully destroy this NetworkAgent's native network.
*/
- public void onNetworkDisconnected() {}
+ public void onNetworkDestroyed() {}
/**
* Requests that the network hardware send the specified packet at the specified interval.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index ca69f16..4f95ccc 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -275,6 +275,7 @@
NET_CAPABILITY_ENTERPRISE,
NET_CAPABILITY_VSIM,
NET_CAPABILITY_BIP,
+ NET_CAPABILITY_HEAD_UNIT,
})
public @interface NetCapability { }
@@ -508,8 +509,13 @@
@SystemApi
public static final int NET_CAPABILITY_BIP = 31;
+ /**
+ * Indicates that this network is connected to an automotive head unit.
+ */
+ public static final int NET_CAPABILITY_HEAD_UNIT = 32;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_BIP;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_HEAD_UNIT;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -527,7 +533,10 @@
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
| (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
- | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED)
+ // The value of NET_CAPABILITY_HEAD_UNIT is 32, which cannot use int to do bit shift,
+ // otherwise there will be an overflow. Use long to do bit shift instead.
+ | (1L << NET_CAPABILITY_HEAD_UNIT);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -867,6 +876,7 @@
TRANSPORT_WIFI_AWARE,
TRANSPORT_LOWPAN,
TRANSPORT_TEST,
+ TRANSPORT_USB,
})
public @interface Transport { }
@@ -913,10 +923,15 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int TRANSPORT_TEST = 7;
+ /**
+ * Indicates this network uses a USB transport.
+ */
+ public static final int TRANSPORT_USB = 8;
+
/** @hide */
public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
/** @hide */
- public static final int MAX_TRANSPORT = TRANSPORT_TEST;
+ public static final int MAX_TRANSPORT = TRANSPORT_USB;
/** @hide */
public static boolean isValidTransport(@Transport int transportType) {
@@ -931,7 +946,8 @@
"VPN",
"WIFI_AWARE",
"LOWPAN",
- "TEST"
+ "TEST",
+ "USB"
};
/**
@@ -2107,6 +2123,7 @@
case NET_CAPABILITY_ENTERPRISE: return "ENTERPRISE";
case NET_CAPABILITY_VSIM: return "VSIM";
case NET_CAPABILITY_BIP: return "BIP";
+ case NET_CAPABILITY_HEAD_UNIT: return "HEAD_UNIT";
default: return Integer.toString(capability);
}
}
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index cd8f4c0..ba83a44 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -17,11 +17,14 @@
package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.Objects;
@@ -38,8 +41,26 @@
/** Type of this VPN. */
public final int type;
- public VpnTransportInfo(int type) {
+ @Nullable
+ public final String sessionId;
+
+ @Override
+ public long getApplicableRedactions() {
+ return REDACT_FOR_NETWORK_SETTINGS;
+ }
+
+ /**
+ * Create a copy of a {@link VpnTransportInfo} with the sessionId redacted if necessary.
+ */
+ @NonNull
+ public VpnTransportInfo makeCopy(long redactions) {
+ return new VpnTransportInfo(type,
+ ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : sessionId);
+ }
+
+ public VpnTransportInfo(int type, @Nullable String sessionId) {
this.type = type;
+ this.sessionId = sessionId;
}
@Override
@@ -47,17 +68,17 @@
if (!(o instanceof VpnTransportInfo)) return false;
VpnTransportInfo that = (VpnTransportInfo) o;
- return this.type == that.type;
+ return (this.type == that.type) && TextUtils.equals(this.sessionId, that.sessionId);
}
@Override
public int hashCode() {
- return Objects.hash(type);
+ return Objects.hash(type, sessionId);
}
@Override
public String toString() {
- return String.format("VpnTransportInfo{type=%d}", type);
+ return String.format("VpnTransportInfo{type=%d, sessionId=%s}", type, sessionId);
}
@Override
@@ -68,12 +89,13 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(type);
+ dest.writeString(sessionId);
}
public static final @NonNull Creator<VpnTransportInfo> CREATOR =
new Creator<VpnTransportInfo>() {
public VpnTransportInfo createFromParcel(Parcel in) {
- return new VpnTransportInfo(in.readInt());
+ return new VpnTransportInfo(in.readInt(), in.readString());
}
public VpnTransportInfo[] newArray(int size) {
return new VpnTransportInfo[size];
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index b44128b..518f198 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -72,7 +72,7 @@
"ServiceConnectivityResources",
],
static_libs: [
- "dnsresolver_aidl_interface-V7-java",
+ "dnsresolver_aidl_interface-V8-java",
"modules-utils-os",
"net-utils-device-common",
"net-utils-framework-common",
diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
index f45105d..31b3fe5 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
+++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
@@ -70,4 +70,13 @@
android:scaleY="2"
android:layout_marginTop="4dp"
android:max="100"/>
+
+ <TextView
+ android:id="@+id/bottom_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:ellipsize="marquee"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
+ android:textSize="14sp"/>
</LinearLayout>
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index 2185950..a2b1de2 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -44,6 +44,7 @@
private CharSequence mUsageSummary;
private CharSequence mTotalSummary;
+ private CharSequence mBottomSummary;
private ImageView mCustomImageView;
private int mPercent = -1;
@@ -101,6 +102,15 @@
notifyChanged();
}
+ /** Set bottom summary. */
+ public void setBottomSummary(CharSequence bottomSummary) {
+ if (TextUtils.equals(mBottomSummary, bottomSummary)) {
+ return;
+ }
+ mBottomSummary = bottomSummary;
+ notifyChanged();
+ }
+
/** Set percentage of the progress bar. */
public void setPercent(long usage, long total) {
if (total == 0L || usage > total) {
@@ -147,6 +157,14 @@
totalSummary.setText(mTotalSummary);
}
+ final TextView bottomSummary = (TextView) holder.findViewById(R.id.bottom_summary);
+ if (TextUtils.isEmpty(mBottomSummary)) {
+ bottomSummary.setVisibility(View.GONE);
+ } else {
+ bottomSummary.setVisibility(View.VISIBLE);
+ bottomSummary.setText(mBottomSummary);
+ }
+
final ProgressBar progressBar = (ProgressBar) holder.findViewById(android.R.id.progress);
if (mPercent < 0) {
progressBar.setIndeterminate(true);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
index fe76b06..cd78add 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
@@ -92,6 +92,28 @@
}
@Test
+ public void setBottomSummary_getCorrectSummary() {
+ final String expectedText = "Should last until about 7:45 PM";
+ mUsageProgressBarPreference.setBottomSummary(expectedText);
+
+ mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+ final TextView bottomSummary = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
+ assertThat(bottomSummary.getText()).isEqualTo(expectedText);
+ assertThat(bottomSummary.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void setBottomSummary_emptyText_isGone() {
+ mUsageProgressBarPreference.setBottomSummary(null);
+
+ mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+ final TextView bottomSummary = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
+ assertThat(bottomSummary.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void setPercent_getCorrectProgress() {
mUsageProgressBarPreference.setPercent(31, 80);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index db9b83e..53920f0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -165,6 +165,8 @@
VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 9c67e9c..4119dc9f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1881,6 +1881,12 @@
dumpSetting(s, p,
Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED,
+ SecureSettingsProto.Assist.TOUCH_GESTURE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
+ SecureSettingsProto.Assist.LONG_PRESS_HOME_ENABLED);
p.end(assistToken);
final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
diff --git a/packages/SystemUI/docs/falsing.md b/packages/SystemUI/docs/falsing.md
new file mode 100644
index 0000000..e224ca8
--- /dev/null
+++ b/packages/SystemUI/docs/falsing.md
@@ -0,0 +1,240 @@
+# Falsing in SystemUI
+
+Phones are easily and often accidentally-activated in owners' pockets ("falsing" or "pocket
+dialing"). Because a phone's screen can be turned on with a single tap, and because we have further
+actions that be activated with basic tapping and swiping, it is critical that we
+analyze touch events on the screen for intentional vs accidental behavior. With analysis,
+features within SystemUI have an opportunity to ignore or even undo accidental interactions as they
+are occurring.
+
+## Technical Details
+
+The `FalsingManager` tracks all touch interactions happening on a phone's lock screen.
+
+If you support any sort of touch gestures on the lock screen, you **must**, at a
+minimum, inform the `FalsingManager` of what touches are on touch targets vs not (things that may be
+ intentional). If you do not tell the `FalsingManager`, it will assume touches on your feature are
+always accidental and penalize the session accordingly.
+
+Individual touch targets do not _have_ to be separated out; it's acceptable to
+wrap your whole feature in one virtual block that reports touches to the
+`FalsingManager`, however more granular tracking will result in better results
+across the whole lock screen.
+
+You can _act_ on the results of the `FalsingManager`. Instead of only telling
+the `FalsingManager` that touch events were on touch targets, you can further use the
+returned results to decide if you want to respond to an owner's touch, if you
+want to prompt them to confirm their action, or if you simply want to ignore the
+touch.
+
+The flow through the system looks like such:
+
+1. Gesture on the screen.
+2. The `FalsingManager` makes a note of all of the `MotionEvents`.
+ * If no feature/touch target receives the `MotionEvents`, skip to 4.
+3. Your touch target receives the `MotionEvents`.
+ * Once your feature is ready to respond to the gesture in a substantive manner, it queries
+ the `FalsingManager`.
+ - Dragging animations, touch ripples, and other purely visual effects should not query.
+ - Query once you are ready to launch a new feature or dialogue, or are otherwise going to
+ change the state of the UI.
+ - Generally, wait until `MotionEvent.ACTION_UP` to query or `View.OnClickListener#onClick`.
+ - Only query once per gesture, at the end.
+ * If the `FalsingManager` says it looks good, respond to the touch.
+4. The `FalsingManager` checks to see if anyone queried about the gesture. If not, mark it as
+ accidental.
+
+There is also an event fired by the `FalsingManager` that can be listened to by anyone, that
+indicates that the the `FalsingManager` believes the phone is actively being pocket-dialed. When
+fired, modal features, such as quick settings, keyguard bouncer, and others should retract
+themselves to prevent further pocket-dialing.
+
+## Falsing "Belief" and History
+
+The `FalsingManager` maintains a recent history of false analyses. Using
+Bayesian statistics, it updates a "belief" in whether recent
+gestures are intentional or not. Any gesture that it is not explicitly queried about is treated as
+accidental, increasing the overall belief in
+false-iness. Gestures that are explicitly queried and that pass the relevant heuristics
+reduce belief that falsing is occurring. This information is tracked within the `HistoryTracker`.
+
+Changes in belief may influence internal heurstics within the `FalsingManager`,
+making it easier or harder for an owner to interact with their device. (An owner
+will always be able to interact with their device, but we may require double
+taps, or more deliberate swipes.)
+
+## Responding to Touch Events
+
+The methods below inform the `FalsingManager` that a tap is occurring within an expected touch
+target. Match the methods with the gesture you expect the device owner to use.
+
+### Single Tap
+
+`FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`. This
+method tells the `FalsingManager` that you want to validate a single tap. It
+returns true if it thinks the tap should be rejected (i.e. the tap looks more
+like a swipe) and false otherwise.
+
+`robustCheck` determines what heuristics are used. If set to false, the method
+performs a only very basic checking, checking that observed `MotionEvent`s are
+all within some small x & y region ("touch slop").
+
+When `robustCheck` is set to true, several more advanced rules are additionally
+applied:
+
+1. If the device recognizes a face (i.e. face-auth) the tap is **accepted**.
+2. If the tap is the _second_ tap in recent history and looks like a valid Double Tap
+ the tap is **accepted**. This works exactly like `FalsingManager#isFalseDoubleTap`.
+3. If the `HistoryTracker` reports strong belief in recent falsing, the tap is
+ **rejected**.
+4. Otherwise the tap is **accepted**.
+
+All the above rules are applied only after first confirming the gesture does
+in fact look like a basic tap.
+
+`falsePenalty` is a measure of how much the `HistoryTracker`'s belief should be
+penalized in the event that the tap is rejected. This value is only used if
+`robustCheck` is set to true.
+
+A value of `0` means no change in belief. A value of `1` means a _very_ strong
+confidence in a false tap. In general, as a single tap on the screen is not
+verifiable, a small value should be supplied - on the order of `0.1`. Pass `0`
+if you don't want to penalize belief at all. Pass a higher value
+the earlier in the UX flow your interaction occurs. Once an owner is farther
+along in a UX flow (multiple taps or swipes), its safer to assume that a single
+accidental tap should cause less of a penalty.
+
+### Double Tap
+
+`FalsingManager#isFalseDoubleTap()`. This method tells the `FalsingManager` that
+your UI wants to validate a double tap. There are no parameters to pass to this method.
+Call this when you explicitly receive and want to verify a double tap, _not_ a single tap.
+
+Note that `FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`
+will also check for double taps when `robustCheck` is set to true. If you are
+willing to use single taps, use that instead.
+
+### Swipes and Other Gestures
+
+`FalsingManager#isFalseTouch(@Classifier.InteractionType int interactionType)`.
+Use this for any non-tap interactions. This includes expanding notifications,
+expanding quick settings, pulling up the bouncer, and more. You must pass
+the type of interaction you are evaluating when calling it. A large set of
+heuristics will be applied to analyze the gesture, and the exact rules vary depending upon
+the `InteractionType`.
+
+### Ignoring A Gesture
+
+`FalsingCollector#avoidGesture()`. Tell the `FalsingManager` to pretend like the
+observed gesture never happened. **This method must be called when the observed
+`MotionEvent` is `MotionEvent.ACTION_DOWN`.** Attempting to call this method
+later in a gesture will not work.
+
+Notice that this method is actually a method on `FalsingCollector`. It is
+forcefully telling the `FalsingManager` to wholly pretend the gesture never
+happened. This is intended for security and PII sensitive gestures, such as
+password inputs. Please don't use this as a shortcut for avoiding the
+FalsingManager. Falsing works better the more behavior it is told about.
+
+### Other Considerations
+
+Please try to call the `FalsingManager` only once per gesture. Wait until you
+are ready to act on the owner's action, and then query the `FalsingManager`. The `FalsingManager`
+will update its belief in pocket dialing based only on the last call made, so multiple calls per
+gesture are not well defined.
+
+The `FalsingManager` does not update its belief in pocket-dialing until a new
+gesture starts. That is to say, if the owner makes a bad tap on your feature,
+the belief in pocket dialing will not incorporate this new data until the
+following gesture begins.
+
+If you expect a mix of taps, double taps, and swipes on your feature, segment them
+accordingly. Figure out which `FalsingManager` method you need to call first, rather than relying
+on multiple calls to the `FalsingManager` to act as a sieve.
+
+Don't:
+```
+if (!mFalsingManager.isFalseTap(false, 0)) {
+ // its a tap
+} else if (!mFalsingManager.isFalseTouch(GESTURE_A) {
+ // do thing a
+} else if (!mFalsingManager.isFalseTouch(GESTURE_B) {
+ // do thing b
+} else {
+ // must be a false.
+}
+```
+
+Do:
+```
+void onTap() {
+ if (!mFalsingManager.isFalseTap(false, 0)) {
+ // its a tap
+}
+
+void onGestureA() {
+ if (!mFalsingManager.isFalseTouch(GESTURE_A) {
+ // do thing a
+ }
+}
+
+void onGestureB() {
+ if (!mFalsingManager.isFalseTouch(GESTURE_B) {
+ // do thing b
+ }
+}
+```
+
+
+## Influencing Belief
+
+`FalsingCollector#updateFalseConfidence(FalsingClassifier.Result result)`. This
+method allows you to directly change the `FalsingManager`'s belief in the state
+of pocket dialing. If the owner does something unusual with their phone that you
+think indicates pocket dialing, you can call:
+
+```
+ mFalsingCollector.updateFalseConfidence(
+ FalsingClassifier.Result.falsed(0.6, "Owner is doing something fishy"));
+```
+
+A belief value of `1` indicates a 100% confidence of false behavior. A belief
+value of `0` would make no change in the `FalsingManager` and should be avoided
+as it simply creates noise in the logs. Generally, a middle value between the
+two extremes makes sense.
+
+A good example of where this is used is in the "Pattern" password input. We
+avoid recording those gestures in the `FalsingManager`, but we have the pattern input update
+the `FalsingManager` directly in some cases. If the owner simply taps on the pattern input, we
+record it as a false, (patterns are always 4 "cells" long, so single "cell" inputs are penalized).
+
+Conversely, if you think the owner does something that deserves a nice reward:
+
+```
+ mFalsingCollector.updateFalseConfidence(
+ FalsingClassifier.Result.passed(0.6));
+```
+
+Again, useful on password inputs where the FalsingManager is avoiding recording
+the gesture. This is used on the "pin" password input, to recognize successful
+taps on the input buttons.
+
+## Global Falsing Event
+
+If the `FalsingManager`'s belief in falsing crosses some internally defined
+threshold, it will fire an event that other parts of the system can listen for.
+This even indicates that the owner is likely actively pocket-dialing, and any
+currently open activities on the phone should retract themselves.
+
+To subscribe to this event, call
+`FalsingManager#addFalsingBeliefListener(FalsingBeliefListener listener)`.
+`FalsingBeliefListener` is a simple one method interface that will be called
+after when activities should retract themselves.
+
+**Do Listen For This**. Your code will work without it, but it is a handy,
+universal signal that will save the phone owner a lot of accidents. A simple
+implementation looks like:
+
+```
+ mFalsingManager.addFalsingBeliefListener(MyFeatureClass::hide);
+```
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 44f52ef..bbf69a9 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -18,32 +18,52 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_qs_status_icons"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/qs_header_top_padding"
- android:paddingBottom="@dimen/qs_header_bottom_padding"
- android:layout_below="@id/quick_status_bar_system_icons"
+ android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:minHeight="20dp"
+ android:minHeight="48dp"
android:clickable="false"
android:focusable="true"
android:theme="@style/QSHeaderTheme">
- <com.android.systemui.statusbar.policy.DateView
- android:id="@+id/date"
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:gravity="center_vertical"
+ android:layout_height="match_parent"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:gravity="center_vertical|start"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.QS.Status"
- systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+
+ <View
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ />
+
+ <!-- Will hold security footer in landscape with media -->
+ <FrameLayout
+ android:id="@+id/header_text_container"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="center"
+ />
+
+ <include layout="@layout/qs_carrier_group"
+ android:id="@+id/carrier_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minHeight="48dp"
+ android:layout_gravity="end|center_vertical"
+ android:focusable="false"/>
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_weight="1"
android:paddingEnd="@dimen/signal_cluster_battery_padding" />
<com.android.systemui.BatteryMeterView
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
deleted file mode 100644
index fb82304..0000000
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 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
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/header_text_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_header_tooltip_height"
- android:layout_below="@id/quick_status_bar_system_icons"
- android:visibility="invisible"
- android:theme="@style/QSHeaderTheme"
- android:forceHasOverlappingRendering="false">
-
- <com.android.systemui.qs.QSHeaderInfoLayout
- android:id="@+id/status_container"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:id = "@+id/alarm_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:focusable="true"
- android:clickable="true">
-
- <ImageView
- android:id="@+id/next_alarm_icon"
- android:layout_width="@dimen/qs_header_alarm_icon_size"
- android:layout_height="@dimen/qs_header_alarm_icon_size"
- android:src="@drawable/ic_alarm"
- android:contentDescription="@string/accessibility_quick_settings_alarm_set"
- android:visibility="gone"/>
-
- <com.android.systemui.util.AutoMarqueeTextView
- android:id="@+id/next_alarm_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
- android:textAppearance="@style/TextAppearance.QS.Status"
- android:visibility="gone"/>
- </LinearLayout>
-
- <View
- android:id="@+id/status_separator"
- android:layout_width="@dimen/qs_header_separator_width"
- android:layout_height="match_parent"
- android:visibility="gone"/>
-
- <LinearLayout
- android:id = "@+id/ringer_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:focusable="true"
- android:clickable="true">
-
- <ImageView
- android:id="@+id/ringer_mode_icon"
- android:layout_width="@dimen/qs_header_alarm_icon_size"
- android:layout_height="@dimen/qs_header_alarm_icon_size"
- android:visibility="gone"/>
-
- <com.android.systemui.util.AutoMarqueeTextView
- android:id="@+id/ringer_mode_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
- android:textAppearance="@style/TextAppearance.QS.Status"
- android:visibility="gone"/>
- </LinearLayout>
- </com.android.systemui.qs.QSHeaderInfoLayout>
-
- <include layout="@layout/qs_carrier_group"
- android:id="@+id/carrier_group"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/qs_status_separator"
- android:layout_gravity="end|center_vertical"
- android:focusable="false"/>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 059bda3..5bf6919 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -32,26 +32,32 @@
android:paddingStart="0dp"
android:elevation="4dp" >
- <!-- The clock -->
- <include layout="@layout/quick_status_bar_header_system_icons" />
+ <!-- Date and privacy. Only visible in QS -->
+ <include layout="@layout/quick_status_bar_header_date_privacy"/>
- <!-- Status icons within the panel itself (and not in the top-most status bar) -->
- <include layout="@layout/quick_qs_status_icons" />
-
- <!-- Layout containing tooltips, alarm text, etc. -->
- <include layout="@layout/quick_settings_header_info" />
+ <RelativeLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <!-- Time, icons and Carrier (only in QS) -->
+ <include layout="@layout/quick_qs_status_icons"/>
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/quick_qs_status_icons"
- android:accessibilityTraversalAfter="@+id/date_time_group"
+ android:layout_marginTop="8dp"
+ android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
android:paddingBottom="10dp"
android:importantForAccessibility="yes" />
+ </RelativeLayout>
</com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
similarity index 72%
rename from packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
rename to packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index f663ab4e..22cf2cb 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -17,33 +17,35 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/quick_status_bar_system_icons"
+ android:id="@+id/quick_status_bar_date_privacy"
android:layout_width="match_parent"
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
+ android:layout_gravity="top"
android:orientation="horizontal"
android:clickable="true"
- android:paddingTop="@dimen/status_bar_padding_top" >
+ android:paddingTop="@dimen/status_bar_padding_top"
+ android:minHeight="48dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:minHeight="48dp"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|start" >
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="48dp"
- android:gravity="center_vertical|start"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+ <com.android.systemui.statusbar.policy.DateView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
</LinearLayout>
<android.widget.Space
@@ -56,6 +58,7 @@
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:minHeight="48dp"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|end" >
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index c7ad3e8..47b41f5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -37,6 +37,7 @@
public static final int GENERIC = 7;
public static final int BOUNCER_UNLOCK = 8;
public static final int PULSE_EXPAND = 9;
+ public static final int BRIGHTNESS_SLIDER = 10;
@IntDef({
QUICK_SETTINGS,
@@ -48,7 +49,8 @@
RIGHT_AFFORDANCE,
GENERIC,
BOUNCER_UNLOCK,
- PULSE_EXPAND
+ PULSE_EXPAND,
+ BRIGHTNESS_SLIDER
})
@Retention(RetentionPolicy.SOURCE)
public @interface InteractionType {}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 5a9c386..80d7863 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -148,6 +148,10 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
+ if (interactionType == Classifier.BRIGHTNESS_SLIDER) {
+ return Result.passed(0);
+ }
+
return !getPassedFlingThreshold() ? falsed(0.5, getReason()) : Result.passed(0.5);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index ac330f0..6f80010 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -17,6 +17,7 @@
package com.android.systemui.classifier;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import android.provider.DeviceConfig;
@@ -115,7 +116,7 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
- if (interactionType == QUICK_SETTINGS) {
+ if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index 427c2ef..f665565 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -18,6 +18,7 @@
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
@@ -45,6 +46,7 @@
boolean up = isUp();
boolean right = isRight();
+ double confidence = 1;
boolean wrongDirection = true;
switch (interactionType) {
case QUICK_SETTINGS:
@@ -52,6 +54,11 @@
case NOTIFICATION_DRAG_DOWN:
wrongDirection = !vertical || up;
break;
+ case BRIGHTNESS_SLIDER:
+ confidence = 0; // Owners may return to original brightness.
+ // A more sophisticated thing to do here would be to look at the size of the
+ // vertical change relative to the screen size. _Some_ amount of vertical
+ // change should be expected.
case NOTIFICATION_DISMISS:
wrongDirection = vertical;
break;
@@ -70,7 +77,7 @@
break;
}
- return wrongDirection ? falsed(1, getReason(interactionType)) : Result.passed(0.5);
+ return wrongDirection ? falsed(confidence, getReason(interactionType)) : Result.passed(0.5);
}
private String getReason(int interactionType) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 1d413af..d9197ef 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -20,6 +20,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import android.graphics.Point;
import android.provider.DeviceConfig;
@@ -87,6 +88,10 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
+ if (interactionType == BRIGHTNESS_SLIDER) {
+ return Result.passed(0);
+ }
+
List<MotionEvent> motionEvents = getRecentMotionEvents();
// Rotate horizontal gestures to be horizontal between their first and last point.
// Rotate vertical gestures to be vertical between their first and last point.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9d43e0c..c8dfde1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -209,6 +209,8 @@
private @TransitionMode int mNavigationBarMode;
private ContentResolver mContentResolver;
private boolean mAssistantAvailable;
+ private boolean mLongPressHomeEnabled;
+ private boolean mAssistantTouchGestureEnabled;
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -309,7 +311,7 @@
// Send the assistant availability upon connection
if (isConnected) {
- sendAssistantAvailability(mAssistantAvailable);
+ updateAssistantEntrypoints();
}
}
@@ -404,12 +406,7 @@
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
- boolean available = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
- if (mAssistantAvailable != available) {
- sendAssistantAvailability(available);
- mAssistantAvailable = available;
- }
+ updateAssistantEntrypoints();
}
};
@@ -531,6 +528,13 @@
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ updateAssistantEntrypoints();
if (savedState != null) {
mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -823,7 +827,7 @@
|| mNavigationBarView.getHomeButton().getCurrentView() == null) {
return;
}
- if (mHomeButtonLongPressDurationMs.isPresent()) {
+ if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
mNavigationBarView.getHomeButton().setOnLongClickListener(null);
@@ -845,6 +849,8 @@
pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
+ pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
+ pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
if (mNavigationBarView != null) {
pw.println(" mNavigationBarWindowState="
@@ -1206,9 +1212,11 @@
return true;
}
}
- mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
- mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
- });
+ if (mLongPressHomeEnabled) {
+ mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+ mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
+ });
+ }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
@@ -1480,15 +1488,23 @@
| (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
}
- private void sendAssistantAvailability(boolean available) {
+ private void updateAssistantEntrypoints() {
+ mAssistantAvailable = mAssistManagerLazy.get()
+ .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, 1) != 0;
+ mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, 1) != 0;
if (mOverviewProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(available
+ mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
+ && mAssistantTouchGestureEnabled
&& QuickStepContract.isGesturalMode(mNavBarMode));
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
}
}
+ reconfigureHomeLongClick();
}
// ----- Methods that DisplayNavigationBarController talks to -----
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index b8823e1..ee0b0c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -65,12 +65,14 @@
private final QuickQSPanel mQuickQsPanel;
private final QSPanelController mQsPanelController;
private final QuickQSPanelController mQuickQSPanelController;
+ private final QuickStatusBarHeader mQuickStatusBarHeader;
private final QSSecurityFooter mSecurityFooter;
private final QS mQs;
private PagedTileLayout mPagedLayout;
private boolean mOnFirstPage = true;
+ private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private TouchAnimator mFirstPageAnimator;
private TouchAnimator mFirstPageDelayedAnimator;
private TouchAnimator mTranslationXAnimator;
@@ -98,19 +100,22 @@
private final FeatureFlags mFeatureFlags;
@Inject
- public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanelController qsPanelController,
+ public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
+ QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mQs = qs;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
+ mQuickStatusBarHeader = quickStatusBarHeader;
mSecurityFooter = securityFooter;
mHost = qsTileHost;
mExecutor = executor;
mTunerService = tunerService;
mFeatureFlags = featureFlags;
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
qs.getView().addOnLayoutChangeListener(this);
@@ -252,7 +257,9 @@
if (count < tileLayout.getNumVisibleTiles()) {
getRelativePosition(loc1, quickTileView, view);
getRelativePosition(loc2, tileView, view);
- int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0;
+ int yOffset = qsSideLabelsEnabled
+ ? loc2[1] - loc1[1]
+ : mQuickStatusBarHeader.getOffsetTranslation();
// Move the quick tile right from its location to the new one.
View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
translationXBuilder.addFloat(v, "translationX", 0, xDiff);
@@ -266,8 +273,14 @@
translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
if (qsSideLabelsEnabled) {
- translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
- translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
+ // Offset the translation animation on the views
+ // (that goes from 0 to getOffsetTranslation)
+ int offsetWithQSBHTranslation =
+ yOffset - mQuickStatusBarHeader.getOffsetTranslation();
+ translationYBuilder.addFloat(quickTileView, "translationY", 0,
+ offsetWithQSBHTranslation);
+ translationYBuilder.addFloat(tileView, "translationY",
+ -offsetWithQSBHTranslation, 0);
if (mQQSTileHeightAnimator == null) {
mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
@@ -375,22 +388,23 @@
}
float px = 0;
- float py = 1;
if (tiles.size() <= 3) {
px = 1;
} else if (tiles.size() <= 6) {
px = .4f;
}
- PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, px, py);
- translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
- translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mQSExpansionPathInterpolator.setControlX2(px);
+ translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
+ translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
mTranslationXAnimator = translationXBuilder.build();
mTranslationYAnimator = translationYBuilder.build();
if (mQQSTileHeightAnimator != null) {
- mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mQQSTileHeightAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
}
if (mOtherTilesExpandAnimator != null) {
- mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mOtherTilesExpandAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
}
}
mNonfirstPageAnimator = new TouchAnimator.Builder()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
new file mode 100644
index 0000000..d351b89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.systemui.qs
+
+import android.view.animation.Interpolator
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class QSExpansionPathInterpolator @Inject constructor() {
+
+ private var pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, 0f, 1f)
+ private var lastX = 0f
+ val xInterpolator: Interpolator
+ get() = pathInterpolatorBuilder.xInterpolator
+
+ val yInterpolator: Interpolator
+ get() = pathInterpolatorBuilder.yInterpolator
+
+ fun setControlX2(value: Float) {
+ if (value != lastX) {
+ lastX = value
+ pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, lastX, 1f)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
deleted file mode 100644
index c654621..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2019 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.qs
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
-import com.android.systemui.R
-
-/**
- * Container for the Next Alarm and Ringer status texts in [QuickStatusBarHeader].
- *
- * If both elements are visible, it splits the available space according to the following rules:
- * * If both views add up to less than the total space, they take all the space they need.
- * * If both views are larger than half the space, each view takes half the space.
- * * Otherwise, the smaller view takes the space it needs and the larger one takes all remaining
- * space.
- */
-class QSHeaderInfoLayout @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0,
- defStyleRes: Int = 0
-) : FrameLayout(context, attrs, defStyle, defStyleRes) {
-
- private lateinit var alarmContainer: View
- private lateinit var ringerContainer: View
- private lateinit var statusSeparator: View
- private val location = Location(0, 0)
-
- override fun onFinishInflate() {
- super.onFinishInflate()
- alarmContainer = findViewById(R.id.alarm_container)
- ringerContainer = findViewById(R.id.ringer_container)
- statusSeparator = findViewById(R.id.status_separator)
- }
-
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
- // At most one view is there
- if (statusSeparator.visibility == View.GONE) super.onLayout(changed, l, t, r, b)
- else {
- val layoutRTL = isLayoutRtl
- val width = r - l
- val height = b - t
- var offset = 0
-
- offset += alarmContainer.layoutView(width, height, offset, layoutRTL)
- offset += statusSeparator.layoutView(width, height, offset, layoutRTL)
- ringerContainer.layoutView(width, height, offset, layoutRTL)
- }
- }
-
- private fun View.layoutView(pWidth: Int, pHeight: Int, offset: Int, RTL: Boolean): Int {
- location.setLocationFromOffset(pWidth, offset, this.measuredWidth, RTL)
- layout(location.left, 0, location.right, pHeight)
- return this.measuredWidth
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(
- MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST),
- heightMeasureSpec)
- val width = MeasureSpec.getSize(widthMeasureSpec)
- // Once we measure the views, using as much space as they need, we need to remeasure them
- // assigning them their final width. This is because TextViews decide whether to MARQUEE
- // after onMeasure.
- if (statusSeparator.visibility != View.GONE) {
- val alarmWidth = alarmContainer.measuredWidth
- val separatorWidth = statusSeparator.measuredWidth
- val ringerWidth = ringerContainer.measuredWidth
- val availableSpace = MeasureSpec.getSize(width) - separatorWidth
- if (alarmWidth < availableSpace / 2) {
- measureChild(
- ringerContainer,
- MeasureSpec.makeMeasureSpec(
- Math.min(ringerWidth, availableSpace - alarmWidth),
- MeasureSpec.AT_MOST),
- heightMeasureSpec)
- } else if (ringerWidth < availableSpace / 2) {
- measureChild(alarmContainer,
- MeasureSpec.makeMeasureSpec(
- Math.min(alarmWidth, availableSpace - ringerWidth),
- MeasureSpec.AT_MOST),
- heightMeasureSpec)
- } else {
- measureChild(
- alarmContainer,
- MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
- heightMeasureSpec)
- measureChild(
- ringerContainer,
- MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
- heightMeasureSpec)
- }
- }
- setMeasuredDimension(width, measuredHeight)
- }
-
- private data class Location(var left: Int, var right: Int) {
- /**
- * Sets the [left] and [right] with the correct values for laying out the child, respecting
- * RTL. Only set the variable through here to prevent concurrency issues.
- * This is done to prevent allocation of [Pair] in [onLayout].
- */
- fun setLocationFromOffset(parentWidth: Int, offset: Int, width: Int, RTL: Boolean) {
- if (RTL) {
- left = parentWidth - offset - width
- right = parentWidth - offset
- } else {
- left = offset
- right = offset + width
- }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index c794a21..95f7e20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -391,20 +391,13 @@
index++;
if (mSecurityFooter != null) {
- LinearLayout.LayoutParams layoutParams =
- (LayoutParams) mSecurityFooter.getLayoutParams();
if (mUsingHorizontalLayout && mHeaderContainer != null) {
// Adding the security view to the header, that enables us to avoid scrolling
- layoutParams.width = 0;
- layoutParams.weight = 1.6f;
- switchToParent(mSecurityFooter, mHeaderContainer, 1 /* always in second place */);
+ switchToParent(mSecurityFooter, mHeaderContainer, 0);
} else {
- layoutParams.width = LayoutParams.WRAP_CONTENT;
- layoutParams.weight = 0;
switchToParent(mSecurityFooter, parent, index);
index++;
}
- mSecurityFooter.setLayoutParams(layoutParams);
}
if (mFooter != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a0bf584..82ae2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,15 +17,14 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.annotation.ColorInt;
-import android.app.AlarmManager.AlarmClockInfo;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
+
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.media.AudioManager;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.Pair;
@@ -34,66 +33,46 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
import android.widget.Space;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
-import java.util.Locale;
-import java.util.Objects;
-
/**
- * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
- * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
- * contents.
+ * View that contains the top-most bits of the QS panel (primarily the status bar with date, time,
+ * battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}.
*/
-public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwner {
-
- public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
+public class QuickStatusBarHeader extends FrameLayout {
private boolean mExpanded;
private boolean mQsDisabled;
+ private TouchAnimator mAlphaAnimator;
+ private TouchAnimator mTranslationAnimator;
+
protected QuickQSPanel mHeaderQsPanel;
- private TouchAnimator mStatusIconsAlphaAnimator;
- private TouchAnimator mHeaderTextContainerAlphaAnimator;
+ private View mDatePrivacyView;
+ private View mClockIconsView;
+ private View mContainer;
- private View mSystemIconsView;
- private View mQuickQsStatusIcons;
- private View mHeaderTextContainerView;
-
- private ImageView mNextAlarmIcon;
- /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
- private TextView mNextAlarmTextView;
- private View mNextAlarmContainer;
- private View mStatusSeparator;
- private ImageView mRingerModeIcon;
- private TextView mRingerModeTextView;
- private View mRingerContainer;
+ private View mQSCarriers;
private Clock mClockView;
- private OngoingPrivacyChip mPrivacyChip;
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
- private TintedIconManager mTintedIconManager;
+ private StatusIconContainer mIconContainer;
- // Used for RingerModeTracker
- private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+ private TintedIconManager mTintedIconManager;
+ private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private int mStatusBarPaddingTop = 0;
private int mRoundedCornerPadding = 0;
@@ -102,12 +81,26 @@
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
- private float mExpandedHeaderAlpha = 1.0f;
+ private float mClockIconsAlpha = 1.0f;
+ private float mDatePrivacyAlpha = 1.0f;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
+ private int mTopViewMeasureHeight;
+
+ private final String mMobileSlotName;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
+ mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile);
+ }
+
+ /**
+ * How much the view containing the clock and QQS will translate down when QS is fully expanded.
+ *
+ * This matches the measured height of the view containing the date and privacy icons.
+ */
+ public int getOffsetTranslation() {
+ return mTopViewMeasureHeight;
}
@Override
@@ -115,19 +108,12 @@
super.onFinishInflate();
mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
- mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
- mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
+ mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
+ mClockIconsView = findViewById(R.id.quick_qs_status_icons);
+ mQSCarriers = findViewById(R.id.carrier_group);
+ mContainer = findViewById(R.id.container);
+ mIconContainer = findViewById(R.id.statusIcons);
- // Views corresponding to the header info section (e.g. ringer and next alarm).
- mHeaderTextContainerView = findViewById(R.id.header_text_container);
- mStatusSeparator = findViewById(R.id.status_separator);
- mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
- mNextAlarmTextView = findViewById(R.id.next_alarm_text);
- mNextAlarmContainer = findViewById(R.id.alarm_container);
- mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
- mRingerModeTextView = findViewById(R.id.ringer_mode_text);
- mRingerContainer = findViewById(R.id.ringer_container);
- mPrivacyChip = findViewById(R.id.privacy_chip);
mClockView = findViewById(R.id.clock);
mSpace = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
@@ -140,79 +126,34 @@
// QS will always show the estimate, and BatteryMeterView handles the case where
// it's unavailable or charging
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
- mRingerModeTextView.setSelected(true);
- mNextAlarmTextView.setSelected(true);
}
- void onAttach(TintedIconManager iconManager) {
+ void onAttach(TintedIconManager iconManager,
+ QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mTintedIconManager = iconManager;
int fillColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
// Set the correct tint for the status icons so they contrast
iconManager.setTint(fillColor);
- mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor));
- mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor));
+
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
+ updateAnimators();
}
public QuickQSPanel getHeaderQsPanel() {
return mHeaderQsPanel;
}
- void updateStatusText(int ringerMode, AlarmClockInfo nextAlarm, boolean zenOverridingRinger,
- boolean use24HourFormat) {
- boolean changed = updateRingerStatus(ringerMode, zenOverridingRinger)
- || updateAlarmStatus(nextAlarm, use24HourFormat);
-
- if (changed) {
- boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
- boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
- mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE
- : View.GONE);
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
+ mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
+ updateAnimators();
}
}
- private boolean updateRingerStatus(int ringerMode, boolean zenOverridingRinger) {
- boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
- CharSequence originalRingerText = mRingerModeTextView.getText();
-
- boolean ringerVisible = false;
- if (!zenOverridingRinger) {
- if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
- mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
- mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
- ringerVisible = true;
- } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
- mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
- mRingerModeTextView.setText(R.string.qs_status_phone_muted);
- ringerVisible = true;
- }
- }
- mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
- mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
- mRingerContainer.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
-
- return isOriginalVisible != ringerVisible ||
- !Objects.equals(originalRingerText, mRingerModeTextView.getText());
- }
-
- private boolean updateAlarmStatus(AlarmClockInfo nextAlarm, boolean use24HourFormat) {
- boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
- CharSequence originalAlarmText = mNextAlarmTextView.getText();
-
- boolean alarmVisible = false;
- if (nextAlarm != null) {
- alarmVisible = true;
- mNextAlarmTextView.setText(formatNextAlarm(nextAlarm, use24HourFormat));
- }
- mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
- mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
- mNextAlarmContainer.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
-
- return isOriginalVisible != alarmVisible ||
- !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -225,40 +166,27 @@
updateResources();
}
- /**
- * The height of QQS should always be the status bar height + 128dp. This is normally easy, but
- * when there is a notch involved the status bar can remain a fixed pixel size.
- */
- private void updateMinimumHeight() {
- int sbHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- int qqsHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_quick_header_panel_height);
-
- setMinimumHeight(sbHeight + qqsHeight);
- }
-
void updateResources() {
Resources resources = mContext.getResources();
- updateMinimumHeight();
mRoundedCornerPadding = resources.getDimensionPixelSize(
R.dimen.rounded_corner_content_padding);
mStatusBarPaddingTop = resources.getDimensionPixelSize(R.dimen.status_bar_padding_top);
- // Update height for a few views, especially due to landscape mode restricting space.
- mHeaderTextContainerView.getLayoutParams().height =
- resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
- mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
-
- mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
+ int qsOffsetHeight = resources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
+
+ mDatePrivacyView.getLayoutParams().height =
+ Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
+ mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
+
+ mClockIconsView.getLayoutParams().height =
+ Math.max(qsOffsetHeight, mClockIconsView.getMinimumHeight());
+ mClockIconsView.setLayoutParams(mClockIconsView.getLayoutParams());
ViewGroup.LayoutParams lp = getLayoutParams();
if (mQsDisabled) {
- lp.height = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height);
+ lp.height = mClockIconsView.getLayoutParams().height;
} else {
lp.height = WRAP_CONTENT;
}
@@ -276,21 +204,45 @@
mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary,
mTextColorPrimary);
}
-
- updateStatusIconAlphaAnimator();
- updateHeaderTextContainerAlphaAnimator();
+ updateHeadersPadding();
+ updateAnimators();
}
- private void updateStatusIconAlphaAnimator() {
- mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mQuickQsStatusIcons, "alpha", 1, 0, 0)
+ private void updateAnimators() {
+ updateAlphaAnimator();
+ int offset = mTopViewMeasureHeight;
+
+ mTranslationAnimator = new TouchAnimator.Builder()
+ .addFloat(mContainer, "translationY", 0, offset)
+ .setInterpolator(mQSExpansionPathInterpolator != null
+ ? mQSExpansionPathInterpolator.getYInterpolator()
+ : null)
.build();
}
- private void updateHeaderTextContainerAlphaAnimator() {
- mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mHeaderTextContainerView, "alpha", 0, 0, mExpandedHeaderAlpha)
- .build();
+ private void updateAlphaAnimator() {
+ StatusBarMobileView icon =
+ ((StatusBarMobileView) mIconContainer.getViewForSlot(mMobileSlotName));
+ TouchAnimator.Builder builder = new TouchAnimator.Builder()
+ .addFloat(mQSCarriers, "alpha", 0, 1)
+ .addFloat(mDatePrivacyView, "alpha", 0, mDatePrivacyAlpha);
+ if (icon != null) {
+ builder.addFloat(icon, "alpha", 1, 0);
+ builder.setListener(new TouchAnimator.ListenerAdapter() {
+ @Override
+ public void onAnimationAtEnd() {
+ icon.forceHidden(true);
+ icon.setVisibleState(STATE_HIDDEN);
+ }
+
+ @Override
+ public void onAnimationStarted() {
+ icon.forceHidden(false);
+ icon.setVisibleState(STATE_ICON);
+ }
+ });
+ }
+ mAlphaAnimator = builder.build();
}
/** */
@@ -312,25 +264,19 @@
public void setExpansion(boolean forceExpanded, float expansionFraction,
float panelTranslationY) {
final float keyguardExpansionFraction = forceExpanded ? 1f : expansionFraction;
- if (mStatusIconsAlphaAnimator != null) {
- mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
- }
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.setPosition(keyguardExpansionFraction);
+ }
+ if (mTranslationAnimator != null) {
+ mTranslationAnimator.setPosition(keyguardExpansionFraction);
+ }
+ // If forceExpanded (we are opening QS from lockscreen), the animators have been set to
+ // position = 1f.
if (forceExpanded) {
- // If the keyguard is showing, we want to offset the text so that it comes in at the
- // same time as the panel as it slides down.
- mHeaderTextContainerView.setTranslationY(panelTranslationY);
+ setTranslationY(panelTranslationY);
} else {
- mHeaderTextContainerView.setTranslationY(0f);
- }
-
- if (mHeaderTextContainerAlphaAnimator != null) {
- mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
- if (keyguardExpansionFraction > 0) {
- mHeaderTextContainerView.setVisibility(VISIBLE);
- } else {
- mHeaderTextContainerView.setVisibility(INVISIBLE);
- }
+ setTranslationY(0);
}
mKeyguardExpansionFraction = keyguardExpansionFraction;
@@ -341,28 +287,21 @@
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
mHeaderQsPanel.setDisabledByPolicy(disabled);
- mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
- mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+ mClockIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
updateResources();
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- // Handle padding of the clock
+ // Handle padding of the views
DisplayCutout cutout = insets.getDisplayCutout();
Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
cutout, getDisplay());
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, -1);
- if (padding == null) {
- mSystemIconsView.setPaddingRelative(
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
- } else {
- mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
-
- }
+ mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
+ mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
boolean cornerCutout = cornerCutoutPadding != null
&& (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
@@ -380,13 +319,13 @@
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
- updateClockPadding();
+ updateHeadersPadding();
return super.onApplyWindowInsets(insets);
}
- private void updateClockPadding() {
- int clockPaddingLeft = 0;
- int clockPaddingRight = 0;
+ private void updateHeadersPadding() {
+ int paddingLeft = 0;
+ int paddingRight = 0;
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
int leftMargin = lp.leftMargin;
@@ -399,18 +338,22 @@
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
int contentMarginLeft = isLayoutRtl() ? mContentMarginEnd : mContentMarginStart;
- clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
+ paddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
}
if (mCutOutPaddingRight > 0) {
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
int contentMarginRight = isLayoutRtl() ? mContentMarginStart : mContentMarginEnd;
- clockPaddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
+ paddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
}
- mSystemIconsView.setPadding(clockPaddingLeft,
+ mDatePrivacyView.setPadding(paddingLeft,
mWaterfallTopInset + mStatusBarPaddingTop,
- clockPaddingRight,
+ paddingRight,
+ 0);
+ mClockIconsView.setPadding(paddingLeft,
+ mWaterfallTopInset + mStatusBarPaddingTop,
+ paddingRight,
0);
}
@@ -422,26 +365,6 @@
mHeaderQsPanel.setCallback(qsPanelCallback);
}
- private String formatNextAlarm(AlarmClockInfo info, boolean use24HourFormat) {
- if (info == null) {
- return "";
- }
- String skeleton = use24HourFormat ? "EHm" : "Ehma";
- String pattern = android.text.format.DateFormat
- .getBestDateTimePattern(Locale.getDefault(), skeleton);
- return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
- }
-
- public static float getColorIntensity(@ColorInt int color) {
- return color == Color.WHITE ? 0 : 1;
- }
-
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
-
/** */
public void setContentMargins(int marginStart, int marginEnd,
QuickQSPanelController quickQSPanelController) {
@@ -459,24 +382,39 @@
view.setLayoutParams(lp);
}
}
- updateClockPadding();
+ updateHeadersPadding();
}
+ /**
+ * When QS is scrolling, mClockIconsAlpha should scroll away and fade out.
+ *
+ * For a given scroll level, this method does the following:
+ * <ol>
+ * <li>Determine the alpha that {@code mClockIconsView} should have when the panel is fully
+ * expanded.</li>
+ * <li>Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.</li>
+ * <li>Set the alpha of {@code mClockIconsView} to that determined by the expansion of
+ * the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully
+ * expanded), matching the animator.</li>
+ * </ol>
+ *
+ * @param scrollY the scroll of the QSPanel container
+ */
public void setExpandedScrollAmount(int scrollY) {
// The scrolling of the expanded qs has changed. Since the header text isn't part of it,
// but would overlap content, we're fading it out.
float newAlpha = 1.0f;
- if (mHeaderTextContainerView.getHeight() > 0) {
- newAlpha = MathUtils.map(0, mHeaderTextContainerView.getHeight() / 2.0f, 1.0f, 0.0f,
+ if (mClockIconsView.getHeight() > 0) {
+ newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f,
scrollY);
newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
}
- mHeaderTextContainerView.setScrollY(scrollY);
- if (newAlpha != mExpandedHeaderAlpha) {
- mExpandedHeaderAlpha = newAlpha;
- mHeaderTextContainerView.setAlpha(MathUtils.lerp(0.0f, mExpandedHeaderAlpha,
+ mClockIconsView.setScrollY(scrollY);
+ if (newAlpha != mClockIconsAlpha) {
+ mClockIconsAlpha = newAlpha;
+ mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha,
mKeyguardExpansionFraction));
- updateHeaderTextContainerAlphaAnimator();
+ updateAlphaAnimator();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index b1689f6..3aafea98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -16,21 +16,13 @@
package com.android.systemui.qs;
-import android.app.AlarmManager.AlarmClockInfo;
import android.content.Intent;
-import android.media.AudioManager;
import android.os.Bundle;
import android.provider.AlarmClock;
-import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
-import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
@@ -47,15 +39,9 @@
import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeController.Callback;
-import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.ViewController;
import java.util.ArrayList;
@@ -70,75 +56,29 @@
class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
private static final String TAG = "QuickStatusBarHeader";
- private final ZenModeController mZenModeController;
- private final NextAlarmController mNextAlarmController;
private final PrivacyItemController mPrivacyItemController;
- private final RingerModeTracker mRingerModeTracker;
private final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final QSCarrierGroupController mQSCarrierGroupController;
private final QuickQSPanelController mHeaderQsPanelController;
- private final LifecycleRegistry mLifecycle;
private final OngoingPrivacyChip mPrivacyChip;
private final Clock mClockView;
- private final View mNextAlarmContainer;
- private final View mRingerContainer;
- private final QSTileHost mQSTileHost;
private final StatusBarIconController mStatusBarIconController;
private final DemoModeController mDemoModeController;
- private final UserTracker mUserTracker;
private final StatusIconContainer mIconContainer;
private final StatusBarIconController.TintedIconManager mIconManager;
private final DemoMode mDemoModeReceiver;
private final PrivacyLogger mPrivacyLogger;
private final PrivacyDialogController mPrivacyDialogController;
+ private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private boolean mListening;
- private AlarmClockInfo mNextAlarm;
private boolean mMicCameraIndicatorsEnabled;
private boolean mLocationIndicatorsEnabled;
private boolean mPrivacyChipLogged;
private SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
- private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
-
- private final ZenModeController.Callback mZenModeControllerCallback = new Callback() {
- @Override
- public void onZenChanged(int zen) {
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
-
- @Override
- public void onConfigChanged(ZenModeConfig config) {
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
- };
-
- private boolean use24HourFormat() {
- return android.text.format.DateFormat.is24HourFormat(
- mView.getContext(), mUserTracker.getUserId());
-
- }
-
- private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
- @Override
- public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
- mNextAlarm = nextAlarm;
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
- };
-
- private final LifecycleOwner mLifecycleOwner = new LifecycleOwner() {
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
- };
private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
@Override
@@ -176,63 +116,43 @@
if (v == mClockView) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
- } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) {
- if (mNextAlarm.getShowIntent() != null) {
- mActivityStarter.postStartActivityDismissingKeyguard(
- mNextAlarm.getShowIntent());
- } else {
- Log.d(TAG, "No PendingIntent for next alarm. Using default intent");
- mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
- AlarmClock.ACTION_SHOW_ALARMS), 0);
- }
} else if (v == mPrivacyChip) {
// If the privacy chip is visible, it means there were some indicators
mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
mPrivacyDialogController.showDialog(getContext());
- } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
- mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
- Settings.ACTION_SOUND_SETTINGS), 0);
}
}
};
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
- ZenModeController zenModeController, NextAlarmController nextAlarmController,
- PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
+ PrivacyItemController privacyItemController,
ActivityStarter activityStarter, UiEventLogger uiEventLogger,
- QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
+ StatusBarIconController statusBarIconController,
DemoModeController demoModeController,
- UserTracker userTracker, QuickQSPanelController quickQSPanelController,
+ QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
PrivacyLogger privacyLogger,
SysuiColorExtractor colorExtractor,
- PrivacyDialogController privacyDialogController) {
+ PrivacyDialogController privacyDialogController,
+ QSExpansionPathInterpolator qsExpansionPathInterpolator) {
super(view);
- mZenModeController = zenModeController;
- mNextAlarmController = nextAlarmController;
mPrivacyItemController = privacyItemController;
- mRingerModeTracker = ringerModeTracker;
mActivityStarter = activityStarter;
mUiEventLogger = uiEventLogger;
- mQSTileHost = qsTileHost;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
- mUserTracker = userTracker;
- mLifecycle = new LifecycleRegistry(mLifecycleOwner);
mHeaderQsPanelController = quickQSPanelController;
mPrivacyLogger = privacyLogger;
mPrivacyDialogController = privacyDialogController;
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
.build();
-
mPrivacyChip = mView.findViewById(R.id.privacy_chip);
- mNextAlarmContainer = mView.findViewById(R.id.alarm_container);
mClockView = mView.findViewById(R.id.clock);
- mRingerContainer = mView.findViewById(R.id.ringer_container);
mIconContainer = mView.findViewById(R.id.statusIcons);
mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer);
@@ -247,15 +167,7 @@
@Override
protected void onViewAttached() {
- mRingerModeTracker.getRingerModeInternal().observe(mLifecycleOwner, ringer -> {
- mRingerMode = ringer;
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- });
-
mClockView.setOnClickListener(mOnClickListener);
- mNextAlarmContainer.setOnClickListener(mOnClickListener);
- mRingerContainer.setOnClickListener(mOnClickListener);
mPrivacyChip.setOnClickListener(mOnClickListener);
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
@@ -268,18 +180,15 @@
setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
- mView.onAttach(mIconManager);
+ mView.onAttach(mIconManager, mQSExpansionPathInterpolator);
mDemoModeController.addCallback(mDemoModeReceiver);
}
@Override
protected void onViewDetached() {
- mRingerModeTracker.getRingerModeInternal().removeObservers(mLifecycleOwner);
mClockView.setOnClickListener(null);
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
- mNextAlarmContainer.setOnClickListener(null);
- mRingerContainer.setOnClickListener(null);
mPrivacyChip.setOnClickListener(null);
mStatusBarIconController.removeIconGroup(mIconManager);
mDemoModeController.removeCallback(mDemoModeReceiver);
@@ -304,17 +213,11 @@
}
if (listening) {
- mZenModeController.addCallback(mZenModeControllerCallback);
- mNextAlarmController.addCallback(mNextAlarmChangeCallback);
- mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
// Get the most up to date info
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
mPrivacyItemController.addCallback(mPICCallback);
} else {
- mZenModeController.removeCallback(mZenModeControllerCallback);
- mNextAlarmController.removeCallback(mNextAlarmChangeCallback);
- mLifecycle.setCurrentState(Lifecycle.State.CREATED);
mPrivacyItemController.removeCallback(mPICCallback);
mPrivacyChipLogged = false;
}
@@ -357,11 +260,6 @@
return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
}
- private boolean isZenOverridingRinger() {
- return ZenModeConfig.isZenOverridingRinger(mZenModeController.getZen(),
- mZenModeController.getConsolidatedPolicy());
- }
-
public void setContentMargins(int contentPaddingStart, int contentPaddingEnd) {
mView.setContentMargins(contentPaddingStart, contentPaddingEnd, mHeaderQsPanelController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 0ebadfd..d1deeca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.util.AttributeSet
+import com.android.systemui.R
open class SideLabelTileLayout(
context: Context,
@@ -26,7 +27,7 @@
override fun updateResources(): Boolean {
return super.updateResources().also {
- mMaxAllowedRows = 4
+ mMaxAllowedRows = context.resources.getInteger(R.integer.quick_settings_max_rows)
}
}
@@ -44,4 +45,19 @@
val row = index / mColumns
return getRowTop(row)
}
+
+ override fun updateMaxRows(allowedHeight: Int, tilesCount: Int): Boolean {
+ val previousRows = mRows
+ mRows = mMaxAllowedRows
+ // We want at most mMaxAllowedRows, but it could be that we don't have enough tiles to fit
+ // that many rows. In that case, we want
+ // `tilesCount = (mRows - 1) * mColumns + X`
+ // where X is some remainder between 1 and `mColumns - 1`
+ // Adding `mColumns - 1` will guarantee that the final value F will satisfy
+ // `mRows * mColumns <= F < (mRows + 1) * mColumns
+ if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+ mRows = (tilesCount + mColumns - 1) / mColumns
+ }
+ return previousRows != mRows
+ }
}
\ No newline at end of file
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 f9d1def..a17aeba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -56,7 +56,6 @@
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
-import com.android.systemui.Prefs;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
@@ -67,7 +66,6 @@
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSEvent;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.logging.QSLogger;
import java.io.FileDescriptor;
@@ -300,11 +298,6 @@
getInstanceId());
mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
mHandler.sendEmptyMessage(H.LONG_CLICK);
-
- Prefs.putInt(
- mContext,
- Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
- QuickStatusBarHeader.MAX_TOOLTIP_SHOWN_COUNT);
}
public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 0b40e22..5d964a4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -27,7 +27,10 @@
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -42,9 +45,7 @@
*
* @see BrightnessMirrorController
*/
-public class BrightnessSlider
- extends ViewController<View>
- implements ToggleSlider {
+public class BrightnessSlider extends ViewController<View> implements ToggleSlider {
private Listener mListener;
private ToggleSlider mMirror;
@@ -52,15 +53,34 @@
private BrightnessMirrorController mMirrorController;
private boolean mTracking;
private final boolean mUseMirror;
+ private final FalsingManager mFalsingManager;
+
+ private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mFalsingManager.isFalseTouch(Classifier.BRIGHTNESS_SLIDER);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return false;
+ }
+ };
BrightnessSlider(
View rootView,
BrightnessSliderView brightnessSliderView,
- boolean useMirror
- ) {
+ boolean useMirror,
+ FalsingManager falsingManager) {
super(rootView);
mBrightnessSliderView = brightnessSliderView;
mUseMirror = useMirror;
+ mFalsingManager = falsingManager;
}
/**
@@ -78,6 +98,7 @@
protected void onViewAttached() {
mBrightnessSliderView.setOnSeekBarChangeListener(mSeekListener);
mBrightnessSliderView.setOnCheckedChangeListener(mCheckListener);
+ mBrightnessSliderView.setOnInterceptListener(mOnInterceptListener);
}
@Override
@@ -85,6 +106,7 @@
mBrightnessSliderView.setOnSeekBarChangeListener(null);
mBrightnessSliderView.setOnCheckedChangeListener(null);
mBrightnessSliderView.setOnDispatchTouchEventListener(null);
+ mBrightnessSliderView.setOnInterceptListener(null);
}
@Override
@@ -247,10 +269,12 @@
public static class Factory {
BrightnessControllerSettings mSettings;
+ private final FalsingManager mFalsingManager;
@Inject
- public Factory(BrightnessControllerSettings settings) {
+ public Factory(BrightnessControllerSettings settings, FalsingManager falsingManager) {
mSettings = settings;
+ mFalsingManager = falsingManager;
}
/**
@@ -270,7 +294,7 @@
private BrightnessSlider fromTree(ViewGroup root, boolean useMirror) {
BrightnessSliderView v = root.requireViewById(R.id.brightness_slider);
- return new BrightnessSlider(root, v, useMirror);
+ return new BrightnessSlider(root, v, useMirror, mFalsingManager);
}
/** Get the layout to inflate based on what slider to use */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index cbf4e88..5b71c62 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -31,6 +31,7 @@
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
/**
@@ -54,6 +55,7 @@
private TextView mLabel;
private final CharSequence mText;
private DispatchTouchEventListener mListener;
+ private Gefingerpoken mOnInterceptListener;
public BrightnessSliderView(Context context) {
this(context, null);
@@ -105,6 +107,15 @@
return super.dispatchTouchEvent(ev);
}
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ // We prevent disallowing on this view, but bubble it up to our parents.
+ // We need interception to handle falsing.
+ if (mParent != null) {
+ mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+ }
+
/**
* Attaches a listener to the {@link ToggleSeekBar} in the view so changes can be observed
* @param seekListener use {@code null} to remove listener
@@ -195,6 +206,18 @@
return mSlider.getProgress();
}
+ public void setOnInterceptListener(Gefingerpoken onInterceptListener) {
+ mOnInterceptListener = onInterceptListener;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mOnInterceptListener != null) {
+ return mOnInterceptListener.onInterceptTouchEvent(ev);
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
/**
* Interface to attach a listener for {@link View#dispatchTouchEvent}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 138c811..ab17ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -59,6 +59,7 @@
private View mMobileRoamingSpace;
private int mVisibleState = -1;
private DualToneHandler mDualToneHandler;
+ private boolean mForceHidden;
public static StatusBarMobileView fromContext(Context context, String slot) {
LayoutInflater inflater = LayoutInflater.from(context);
@@ -150,7 +151,7 @@
private void initViewState() {
setContentDescription(mState.contentDescription);
- if (!mState.visible) {
+ if (!mState.visible || mForceHidden) {
mMobileGroup.setVisibility(View.GONE);
} else {
mMobileGroup.setVisibility(View.VISIBLE);
@@ -176,8 +177,9 @@
boolean needsLayout = false;
setContentDescription(state.contentDescription);
- if (mState.visible != state.visible) {
- mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
+ int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE;
+ if (newVisibility != mMobileGroup.getVisibility()) {
+ mMobileGroup.setVisibility(newVisibility);
needsLayout = true;
}
if (mState.strengthId != state.strengthId) {
@@ -252,7 +254,7 @@
@Override
public boolean isIconVisible() {
- return mState.visible;
+ return mState.visible && !mForceHidden;
}
@Override
@@ -279,6 +281,23 @@
}
}
+ /**
+ * Forces the state to be hidden (views will be GONE) and if necessary updates the layout.
+ *
+ * Makes sure that the {@link StatusBarIconController} cannot make it visible while this flag
+ * is enabled.
+ * @param forceHidden {@code true} if the icon should be GONE in its view regardless of its
+ * state.
+ * {@code false} if the icon should show as determined by its controller.
+ */
+ public void forceHidden(boolean forceHidden) {
+ if (mForceHidden != forceHidden) {
+ mForceHidden = forceHidden;
+ updateState(mState);
+ requestLayout();
+ }
+ }
+
@Override
public int getVisibleState() {
return mVisibleState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index c6f7983..779ba1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -33,6 +33,8 @@
import android.view.WindowInsets;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+
import com.android.systemui.util.leak.RotationUtils;
/**
@@ -91,6 +93,7 @@
* @param roundedCornerContentPadding
* @return
*/
+ @NonNull
public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner(
DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding,
int roundedCornerContentPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index f65f97a..64a497d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -262,6 +262,25 @@
}
/**
+ * Returns the view corresponding to a particular slot.
+ *
+ * Use it solely to manipulate how it is presented.
+ * @param slot name of the slot to find. Names are defined in
+ * {@link com.android.internal.R.config_statusBarIcons}
+ * @return a view for the slot if this container has it, else {@code null}
+ */
+ public View getViewForSlot(String slot) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof StatusIconDisplayable
+ && ((StatusIconDisplayable) child).getSlot().equals(slot)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
* Layout is happening from end -> start
*/
private void calculateIconTranslations() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 6e78059..5a78ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -921,13 +921,18 @@
if (clip.getItemCount() > 1
|| description.getMimeTypeCount() < 1
|| remainingItems != null) {
- // TODO(b/172363500): Update to loop over all the items
+ // Direct-reply in notifications currently only supports only one uri item
+ // at a time and requires the MIME type to be set.
+ Log.w(TAG, "Invalid payload: " + payload);
return payload;
}
Uri contentUri = clip.getItemAt(0).getUri();
String mimeType = description.getMimeType(0);
Intent dataIntent =
mRemoteInputView.prepareRemoteInputFromData(mimeType, contentUri);
+ // We can release the uri permissions granted to us as soon as we've created the
+ // grant for the target app in the call above.
+ payload.releasePermissions();
mRemoteInputView.sendRemoteInput(dataIntent);
}
return remainingItems;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
index db06199..c912419 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.classifier;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+
import static com.google.common.truth.Truth.assertThat;
import android.testing.AndroidTestingRunner;
@@ -96,13 +98,9 @@
}
@Test
- public void testPass_swipe() {
-
+ public void testPass_BrightnessSliderAlwaysPasses() {
mClassifier.onTouchEvent(appendDownEvent(1, 1));
- assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue();
-
- mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3));
- mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300));
- assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse())
+ .isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
index 52423bd..60786f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
@@ -16,11 +16,12 @@
package com.android.systemui.classifier;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -72,7 +73,7 @@
public void testPass_uncovered() {
touchDown();
touchUp(10);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
}
@Test
@@ -81,7 +82,7 @@
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 2));
touchUp(20);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
}
@Test
@@ -90,7 +91,17 @@
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
- assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse(), is(false));
+ assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isFalse();
+ }
+
+ @Test
+ public void testPass_brightnessSlider() {
+ touchDown();
+ mClassifier.onProximityEvent(createSensorEvent(true, 1));
+ mClassifier.onProximityEvent(createSensorEvent(false, 11));
+ touchUp(10);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse())
+ .isFalse();
}
@Test
@@ -99,7 +110,7 @@
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue();
}
@Test
@@ -110,7 +121,7 @@
mClassifier.onProximityEvent(createSensorEvent(true, 96));
mClassifier.onProximityEvent(createSensorEvent(false, 100));
touchUp(100);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue();
}
@Test
@@ -120,7 +131,7 @@
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
when(mDistanceClassifier.isLongSwipe()).thenReturn(mPassedResult);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
}
private void touchDown() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
index 068a1e5..32537b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.classifier;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
@@ -277,4 +278,46 @@
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isTrue();
}
+
+ @Test
+ public void testPass_BrightnessSlider() {
+ when(mDataProvider.isVertical()).thenReturn(false);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+ }
+
+ @Test
+ public void testFalse_BrightnessSlider() {
+ when(mDataProvider.isVertical()).thenReturn(true);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
index e004c30..c343c20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.classifier;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+
import static com.google.common.truth.Truth.assertThat;
import android.testing.AndroidTestingRunner;
@@ -82,6 +84,13 @@
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
}
+ @Test
+ public void testPass_brightnessSliderAlwaysPasses() {
+ appendMoveEvent(0, 0);
+ appendMoveEvent(0, 100);
+ appendMoveEvent(0, 1);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse()).isFalse();
+ }
@Test
public void testFail_minimumTouchesVertical() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 3595095..3c1b36e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -21,9 +21,9 @@
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
@@ -32,16 +32,12 @@
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
-import com.android.systemui.statusbar.policy.NextAlarmController
-import com.android.systemui.util.RingerModeTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
-import com.android.systemui.utils.leaks.FakeZenModeController
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -61,26 +57,16 @@
@Mock
private lateinit var view: QuickStatusBarHeader
@Mock
- private lateinit var zenModeController: FakeZenModeController
- @Mock
- private lateinit var nextAlarmController: NextAlarmController
- @Mock
private lateinit var privacyItemController: PrivacyItemController
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private lateinit var ringerModeTracker: RingerModeTracker
@Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var uiEventLogger: UiEventLogger
@Mock
- private lateinit var qsTileHost: QSTileHost
- @Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
private lateinit var demoModeController: DemoModeController
@Mock
- private lateinit var userTracker: UserTracker
- @Mock
private lateinit var quickQSPanelController: QuickQSPanelController
@Mock(answer = Answers.RETURNS_SELF)
private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@@ -105,6 +91,8 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var context: Context
+ private val qsExpansionPathInterpolator = QSExpansionPathInterpolator()
+
private lateinit var controller: QuickStatusBarHeaderController
@Before
@@ -119,21 +107,17 @@
controller = QuickStatusBarHeaderController(
view,
- zenModeController,
- nextAlarmController,
privacyItemController,
- ringerModeTracker,
activityStarter,
uiEventLogger,
- qsTileHost,
statusBarIconController,
demoModeController,
- userTracker,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
privacyLogger,
colorExtractor,
- privacyDialogController
+ privacyDialogController,
+ qsExpansionPathInterpolator
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 022dc84..104b625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -21,10 +21,13 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.FeatureFlagUtils;
import android.view.LayoutInflater;
import androidx.test.filters.SmallTest;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -39,6 +42,7 @@
private QSCarrier mQSCarrier;
private TestableLooper mTestableLooper;
+ private int mSignalIconId;
@Before
public void setUp() throws Exception {
@@ -46,18 +50,26 @@
LayoutInflater inflater = LayoutInflater.from(mContext);
mTestableLooper.runWithLooper(() ->
mQSCarrier = (QSCarrier) inflater.inflate(R.layout.qs_carrier, null));
+
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ // In this case, the id is an actual drawable id
+ mSignalIconId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
+ } else {
+ // In this case, the id is a level
+ mSignalIconId = SignalDrawable.getEmptyState(5);
+ }
}
@Test
public void testUpdateState_first() {
- CellSignalState c = new CellSignalState(true, 0, "", "", false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c));
}
@Test
public void testUpdateState_same() {
- CellSignalState c = new CellSignalState(true, 0, "", "", false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c));
assertFalse(mQSCarrier.updateState(c));
@@ -65,7 +77,7 @@
@Test
public void testUpdateState_changed() {
- CellSignalState c = new CellSignalState(true, 0, "", "", false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
index 0cc2072..95e9d1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
@@ -25,6 +25,7 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.RestrictedLockUtils
import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.statusbar.policy.BrightnessMirrorController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
@@ -75,6 +76,7 @@
private lateinit var checkedChangeCaptor: ArgumentCaptor<CompoundButton.OnCheckedChangeListener>
@Mock
private lateinit var compoundButton: CompoundButton
+ private var mFalsingManager: FalsingManagerFake = FalsingManagerFake()
private lateinit var mController: BrightnessSlider
@@ -85,7 +87,7 @@
whenever(mirrorController.toggleSlider).thenReturn(mirror)
whenever(motionEvent.copy()).thenReturn(motionEvent)
- mController = BrightnessSlider(rootView, brightnessSliderView, true)
+ mController = BrightnessSlider(rootView, brightnessSliderView, true, mFalsingManager)
mController.init()
mController.setOnChangedListener(listener)
}
@@ -160,7 +162,8 @@
@Test
fun testSettingMirrorWhenNotUseMirrorIsNoOp() {
- val otherController = BrightnessSlider(rootView, brightnessSliderView, false)
+ val otherController = BrightnessSlider(
+ rootView, brightnessSliderView, false, mFalsingManager)
otherController.init()
otherController.setMirrorControllerAndMirror(mirrorController)
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 5b74cbd..1f66bfd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -79,7 +79,7 @@
private final ScreenStateObserver mScreenStateObserver;
- private final MagnificationRequestObserver mMagnificationRequestObserver;
+ private final MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
private int mUserId;
@@ -284,6 +284,14 @@
mControllerCtx.getHandler().sendMessage(m);
}
+ @Override
+ public void onImeWindowVisibilityChanged(boolean shown) {
+ final Message m = PooledLambda.obtainMessage(
+ FullScreenMagnificationController::notifyImeWindowVisibilityChanged,
+ FullScreenMagnificationController.this, shown);
+ mControllerCtx.getHandler().sendMessage(m);
+ }
+
/**
* Update our copy of the current magnification region
*
@@ -329,7 +337,7 @@
final boolean lastMagnificationActivated = mMagnificationActivated;
mMagnificationActivated = spec.scale > 1.0f;
if (mMagnificationActivated != lastMagnificationActivated) {
- mMagnificationRequestObserver.onFullScreenMagnificationActivationState(
+ mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState(
mMagnificationActivated);
}
}
@@ -498,7 +506,7 @@
sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
if (isMagnifying() && (id != INVALID_ID)) {
mIdOfLastServiceToMagnify = id;
- mMagnificationRequestObserver.onRequestMagnificationSpec(mDisplayId,
+ mMagnificationInfoChangedCallback.onRequestMagnificationSpec(mDisplayId,
mIdOfLastServiceToMagnify);
}
return changed;
@@ -631,12 +639,12 @@
*/
public FullScreenMagnificationController(@NonNull Context context,
@NonNull AccessibilityManagerService ams, @NonNull Object lock,
- @NonNull MagnificationRequestObserver magnificationRequestObserver) {
+ @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
this(new ControllerContext(context, ams,
LocalServices.getService(WindowManagerInternal.class),
new Handler(context.getMainLooper()),
context.getResources().getInteger(R.integer.config_longAnimTime)), lock,
- magnificationRequestObserver);
+ magnificationInfoChangedCallback);
}
/**
@@ -645,12 +653,12 @@
@VisibleForTesting
public FullScreenMagnificationController(@NonNull ControllerContext ctx,
@NonNull Object lock,
- @NonNull MagnificationRequestObserver magnificationRequestObserver) {
+ @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
mControllerCtx = ctx;
mLock = lock;
mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
- mMagnificationRequestObserver = magnificationRequestObserver;
+ mMagnificationInfoChangedCallback = magnificationInfoChangedCallback;
}
/**
@@ -1168,6 +1176,16 @@
}
/**
+ * Notifies that the IME window visibility changed.
+ *
+ * @param shown {@code true} means the IME window shows on the screen. Otherwise it's
+ * hidden.
+ */
+ void notifyImeWindowVisibilityChanged(boolean shown) {
+ mMagnificationInfoChangedCallback.onImeWindowVisibilityChanged(shown);
+ }
+
+ /**
* Returns {@code true} if the magnifiable regions of the display is forced to be shown.
*
* @param displayId The logical display id.
@@ -1528,7 +1546,7 @@
return animate ? STUB_ANIMATION_CALLBACK : null;
}
- interface MagnificationRequestObserver {
+ interface MagnificationInfoChangedCallback {
/**
* Called when the {@link MagnificationSpec} is changed with non-default
@@ -1545,7 +1563,13 @@
*
* @param activated {@code true} if the magnification is activated, otherwise {@code false}.
*/
- @GuardedBy("mLock")
void onFullScreenMagnificationActivationState(boolean activated);
+
+ /**
+ * 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);
}
}
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 2073c70..878ebc5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -18,6 +18,7 @@
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;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import android.annotation.NonNull;
@@ -33,6 +34,7 @@
import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
@@ -58,7 +60,7 @@
*/
public class MagnificationController implements WindowMagnificationManager.Callback,
MagnificationGestureHandler.Callback,
- FullScreenMagnificationController.MagnificationRequestObserver {
+ FullScreenMagnificationController.MagnificationInfoChangedCallback {
private static final boolean DEBUG = false;
private static final String TAG = "MagnificationController";
@@ -73,6 +75,10 @@
private WindowMagnificationManager mWindowMagnificationMgr;
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ @GuardedBy("mLock")
+ private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ @GuardedBy("mLock")
+ private boolean mImeWindowVisible = false;
private long mWindowModeEnabledTime = 0;
private long mFullScreenModeEnabledTime = 0;
@@ -216,9 +222,18 @@
public void onWindowMagnificationActivationState(boolean activated) {
if (activated) {
mWindowModeEnabledTime = SystemClock.uptimeMillis();
+
+ synchronized (mLock) {
+ mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ }
+ logMagnificationModeWithImeOnIfNeeded();
} else {
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
SystemClock.uptimeMillis() - mWindowModeEnabledTime);
+
+ synchronized (mLock) {
+ mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ }
}
}
@@ -226,12 +241,29 @@
public void onFullScreenMagnificationActivationState(boolean activated) {
if (activated) {
mFullScreenModeEnabledTime = SystemClock.uptimeMillis();
+
+ synchronized (mLock) {
+ mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ }
+ logMagnificationModeWithImeOnIfNeeded();
} else {
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
SystemClock.uptimeMillis() - mFullScreenModeEnabledTime);
+
+ synchronized (mLock) {
+ mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ }
}
}
+ @Override
+ public void onImeWindowVisibilityChanged(boolean shown) {
+ synchronized (mLock) {
+ mImeWindowVisible = shown;
+ }
+ logMagnificationModeWithImeOnIfNeeded();
+ }
+
/**
* Wrapper method of logging the magnification activated mode and its duration of the usage
* when the magnification is disabled.
@@ -245,6 +277,17 @@
}
/**
+ * Wrapper method of logging the activated mode of the magnification when the IME window
+ * is shown on the screen.
+ *
+ * @param mode The activated magnification mode.
+ */
+ @VisibleForTesting
+ public void logMagnificationModeWithIme(int mode) {
+ AccessibilityStatsLogUtils.logMagnificationModeWithImeOn(mode);
+ }
+
+ /**
* Updates the active user ID of {@link FullScreenMagnificationController} and {@link
* WindowMagnificationManager}.
*
@@ -295,6 +338,18 @@
}
}
+ private void logMagnificationModeWithImeOnIfNeeded() {
+ final int mode;
+
+ synchronized (mLock) {
+ if (!mImeWindowVisible || mActivatedMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
+ return;
+ }
+ mode = mActivatedMode;
+ }
+ logMagnificationModeWithIme(mode);
+ }
+
/**
* Getter of {@link FullScreenMagnificationController}.
*
diff --git a/services/api/current.txt b/services/api/current.txt
index a3e6715..a0b1e33 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -105,6 +105,14 @@
}
+package com.android.server.stats {
+
+ public final class StatsHelper {
+ method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context);
+ }
+
+}
+
package com.android.server.wifi {
public class SupplicantManager {
diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt
index f01c182..475dcf5 100644
--- a/services/api/non-updatable-current.txt
+++ b/services/api/non-updatable-current.txt
@@ -52,6 +52,14 @@
}
+package com.android.server.stats {
+
+ public final class StatsHelper {
+ method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context);
+ }
+
+}
+
package com.android.server.wifi {
public class SupplicantManager {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 641b38d..1e3f12a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -158,7 +158,7 @@
"android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
- "dnsresolver_aidl_interface-V7-java",
+ "dnsresolver_aidl_interface-V8-java",
"icu4j_calendar_astronomer",
"netd-client",
"overlayable_policy_aidl-java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bbe59c9..ca01c8e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1570,16 +1570,16 @@
mNetworkInfoBlockingLogs.log(action + " " + uid);
}
- private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
- boolean blocked) {
+ private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net, int blocked) {
if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
return;
}
- final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ final String action = (blocked != 0) ? "BLOCKED" : "UNBLOCKED";
final int requestId = nri.getActiveRequest() != null
? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
- "%s %d(%d) on netId %d", action, nri.mAsUid, requestId, net.getNetId()));
+ "%s %d(%d) on netId %d: %s", action, nri.mAsUid, requestId, net.getNetId(),
+ blockedReasonsToString(blocked)));
}
/**
@@ -3137,6 +3137,13 @@
}
break;
}
+ case NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED: {
+ if (msg.arg1 >= 0 && msg.arg1 <= NetworkAgent.MAX_TEARDOWN_DELAY_MS) {
+ nai.teardownDelayMs = msg.arg1;
+ } else {
+ logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1);
+ }
+ }
}
}
@@ -3708,6 +3715,23 @@
mLegacyTypeTracker.remove(nai, wasDefault);
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
+
+ // Immediate teardown.
+ if (nai.teardownDelayMs == 0) {
+ destroyNetwork(nai);
+ return;
+ }
+
+ // Delayed teardown.
+ try {
+ mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error marking network restricted during teardown: " + e);
+ }
+ mHandler.postDelayed(() -> destroyNetwork(nai), nai.teardownDelayMs);
+ }
+
+ private void destroyNetwork(NetworkAgentInfo nai) {
if (nai.created) {
// Tell netd to clean up the configuration for this network
// (routing rules, DNS, etc).
@@ -3720,7 +3744,7 @@
mDnsManager.removeNetwork(nai.network);
}
mNetIdManager.releaseNetId(nai.network.getNetId());
- nai.onNetworkDisconnected();
+ nai.onNetworkDestroyed();
}
private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
@@ -7354,7 +7378,7 @@
break;
}
case ConnectivityManager.CALLBACK_BLK_CHANGED: {
- maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1 != 0);
+ maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1);
msg.arg1 = arg1;
break;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 88dca0c..282a12d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -80,9 +80,12 @@
private final @NonNull AudioService mAudioService;
private final @NonNull Context mContext;
- /** Forced device usage for communications sent to AudioSystem */
- private AudioDeviceAttributes mPreferredCommunicationDevice;
+ /** ID for Communication strategy retrieved form audio policy manager */
private int mCommunicationStrategyId = -1;
+ /** Active communication device reported by audio policy manager */
+ private AudioDeviceInfo mActiveCommunicationDevice;
+ /** Last preferred device set for communication strategy */
+ private AudioDeviceAttributes mPreferredCommunicationDevice;
// Manages all connected devices, only ever accessed on the message loop
private final AudioDeviceInventory mDeviceInventory;
@@ -153,8 +156,9 @@
private void init() {
setupMessaging(mContext);
- mPreferredCommunicationDevice = null;
initCommunicationStrategyId();
+ mPreferredCommunicationDevice = null;
+ updateActiveCommunicationDevice();
mSystemServer.registerUserStartedReceiver(mContext);
}
@@ -300,7 +304,6 @@
+ " from API: " + eventSource)).printLog(TAG));
final boolean wasBtScoRequested = isBluetoothScoRequested();
- final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested();
CommunicationRouteClient client;
@@ -341,16 +344,6 @@
mBtHelper.stopBluetoothSco(eventSource);
}
- if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
- try {
- mContext.sendBroadcastAsUser(
- new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
- } catch (Exception e) {
- Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
- }
- }
-
sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
}
@@ -386,66 +379,119 @@
* Returns the device currently requested for communication use case.
* @return AudioDeviceInfo the requested device for communication.
*/
- AudioDeviceInfo getCommunicationDevice() {
+ /* package */ AudioDeviceInfo getCommunicationDevice() {
synchronized (mDeviceStateLock) {
- AudioDeviceAttributes device = requestedCommunicationDevice();
- if (device == null) {
- AudioAttributes attr =
- AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
- AudioSystem.STREAM_VOICE_CALL);
- List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
- if (devices.isEmpty()) {
- if (mAudioService.isPlatformVoice()) {
- Log.w(TAG, "getCommunicationDevice(): no device for phone strategy");
- }
- return null;
- }
- device = devices.get(0);
- }
- return AudioManager.getDeviceInfoFromTypeAndAddress(
- device.getType(), device.getAddress());
+ updateActiveCommunicationDevice();
+ return mActiveCommunicationDevice;
}
}
/**
- * Helper method on top of requestedCommunicationDevice() indicating if
+ * Updates currently active communication device (mActiveCommunicationDevice).
+ */
+ @GuardedBy("mDeviceStateLock")
+ void updateActiveCommunicationDevice() {
+ AudioDeviceAttributes device = preferredCommunicationDevice();
+ if (device == null) {
+ AudioAttributes attr =
+ AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
+ AudioSystem.STREAM_VOICE_CALL);
+ List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
+ if (devices.isEmpty()) {
+ if (mAudioService.isPlatformVoice()) {
+ Log.w(TAG,
+ "updateActiveCommunicationDevice(): no device for phone strategy");
+ }
+ mActiveCommunicationDevice = null;
+ return;
+ }
+ device = devices.get(0);
+ }
+ mActiveCommunicationDevice = AudioManager.getDeviceInfoFromTypeAndAddress(
+ device.getType(), device.getAddress());
+ }
+
+ /**
+ * Indicates if the device which type is passed as argument is currently resquested to be used
+ * for communication.
+ * @param deviceType the device type the query applies to.
+ * @return true if this device type is requested for communication.
+ */
+ private boolean isDeviceRequestedForCommunication(int deviceType) {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = requestedCommunicationDevice();
+ return device != null && device.getType() == deviceType;
+ }
+ }
+
+ /**
+ * Indicates if the device which type is passed as argument is currently either resquested
+ * to be used for communication or selected for an other reason (e.g bluetooth SCO audio
+ * is active for SCO device).
+ * @param deviceType the device type the query applies to.
+ * @return true if this device type is requested for communication.
+ */
+ private boolean isDeviceOnForCommunication(int deviceType) {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = preferredCommunicationDevice();
+ return device != null && device.getType() == deviceType;
+ }
+ }
+
+ /**
+ * Indicates if the device which type is passed as argument is active for communication.
+ * Active means not only currently used by audio policy manager for communication strategy
+ * but also explicitly requested for use by communication strategy.
+ * @param deviceType the device type the query applies to.
+ * @return true if this device type is requested for communication.
+ */
+ private boolean isDeviceActiveForCommunication(int deviceType) {
+ return mActiveCommunicationDevice != null
+ && mActiveCommunicationDevice.getType() == deviceType
+ && mPreferredCommunicationDevice != null
+ && mPreferredCommunicationDevice.getType() == deviceType;
+ }
+
+ /**
+ * Helper method on top of isDeviceRequestedForCommunication() indicating if
* speakerphone ON is currently requested or not.
* @return true if speakerphone ON requested, false otherwise.
*/
-
private boolean isSpeakerphoneRequested() {
- synchronized (mDeviceStateLock) {
- AudioDeviceAttributes device = requestedCommunicationDevice();
- return device != null
- && device.getType()
- == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
- }
+ return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
}
/**
- * Indicates if active route selection for communication is speakerphone.
+ * Indicates if preferred route selection for communication is speakerphone.
* @return true if speakerphone is active, false otherwise.
*/
/*package*/ boolean isSpeakerphoneOn() {
- AudioDeviceAttributes device = getPreferredCommunicationDevice();
- if (device == null) {
- return false;
- }
- return device.getInternalType() == AudioSystem.DEVICE_OUT_SPEAKER;
+ return isDeviceOnForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+ }
+
+ private boolean isSpeakerphoneActive() {
+ return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
}
/**
- * Helper method on top of requestedCommunicationDevice() indicating if
+ * Helper method on top of isDeviceRequestedForCommunication() indicating if
* Bluetooth SCO ON is currently requested or not.
* @return true if Bluetooth SCO ON is requested, false otherwise.
*/
/*package*/ boolean isBluetoothScoRequested() {
- synchronized (mDeviceStateLock) {
- AudioDeviceAttributes device = requestedCommunicationDevice();
- return device != null
- && device.getType()
- == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
- }
+ return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
+ }
+
+ /**
+ * Indicates if preferred route selection for communication is Bluetooth SCO.
+ * @return true if Bluetooth SCO is preferred , false otherwise.
+ */
+ /*package*/ boolean isBluetoothScoOn() {
+ return isDeviceOnForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
+ }
+
+ /*package*/ boolean isBluetoothScoActive() {
+ return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
}
/*package*/ void setWiredDeviceConnectionState(int type,
@@ -593,18 +639,6 @@
}
}
- /**
- * Indicates if active route selection for communication is Bluetooth SCO.
- * @return true if Bluetooth SCO is active , false otherwise.
- */
- /*package*/ boolean isBluetoothScoOn() {
- AudioDeviceAttributes device = getPreferredCommunicationDevice();
- if (device == null) {
- return false;
- }
- return AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType());
- }
-
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.startWatchingRoutes(observer);
@@ -748,8 +782,8 @@
@GuardedBy("mDeviceStateLock")
private void dispatchCommunicationDevice() {
- AudioDeviceInfo device = getCommunicationDevice();
- int portId = (device == null) ? 0 : device.getId();
+ int portId = (mActiveCommunicationDevice == null) ? 0
+ : mActiveCommunicationDevice.getId();
if (portId == mCurCommunicationPortId) {
return;
}
@@ -1022,12 +1056,13 @@
pw.println(" " + prefix + "pid: " + cl.getPid() + " device: "
+ cl.getDevice() + " cb: " + cl.getBinder()); });
- pw.println("\n" + prefix + "mPreferredCommunicationDevice: "
+ pw.println("\n" + prefix + "Computed Preferred communication device: "
+ + preferredCommunicationDevice());
+ pw.println("\n" + prefix + "Applied Preferred communication device: "
+ mPreferredCommunicationDevice);
-
- AudioDeviceInfo device = getCommunicationDevice();
- pw.println(prefix + "Selected Communication Device: "
- + ((device == null) ? "None" : new AudioDeviceAttributes(device)));
+ pw.println(prefix + "Active communication device: "
+ + ((mActiveCommunicationDevice == null) ? "None"
+ : new AudioDeviceAttributes(mActiveCommunicationDevice)));
pw.println(prefix + "mCommunicationStrategyId: "
+ mCommunicationStrategyId);
@@ -1128,6 +1163,7 @@
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
initCommunicationStrategyId();
+ updateActiveCommunicationDevice();
mDeviceInventory.onRestoreDevices();
mBtHelper.onAudioServerDiedRestoreA2dp();
onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
@@ -1340,11 +1376,16 @@
final List<AudioDeviceAttributes> devices =
(List<AudioDeviceAttributes>) msg.obj;
setPreferredDevicesForStrategySync(strategy, devices);
-
+ if (strategy == mCommunicationStrategyId) {
+ onUpdatePhoneStrategyDevice(devices.isEmpty() ? null : devices.get(0));
+ }
} break;
case MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
final int strategy = msg.arg1;
removePreferredDevicesForStrategySync(strategy);
+ if (strategy == mCommunicationStrategyId) {
+ onUpdatePhoneStrategyDevice(null);
+ }
} break;
case MSG_CHECK_MUTE_MUSIC:
checkMessagesMuteMusic(0);
@@ -1672,14 +1713,14 @@
}
/**
- * Determines which forced usage for communication should be sent to audio policy manager
+ * Determines which preferred device for phone strategy should be sent to audio policy manager
* as a function of current SCO audio activation state and active communication route requests.
* SCO audio state has the highest priority as it can result from external activation by
* telephony service.
* @return selected forced usage for communication.
*/
@GuardedBy("mDeviceStateLock")
- @Nullable private AudioDeviceAttributes getPreferredCommunicationDevice() {
+ @Nullable private AudioDeviceAttributes preferredCommunicationDevice() {
boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
if (btSCoOn) {
// Use the SCO device known to BtHelper so that it matches exactly
@@ -1692,8 +1733,7 @@
}
}
AudioDeviceAttributes device = requestedCommunicationDevice();
- if (device == null
- || AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType())) {
+ if (device == null || device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
// Do not indicate BT SCO selection if SCO is requested but SCO is not ON
return null;
}
@@ -1707,30 +1747,49 @@
// @GuardedBy("mSetModeLock")
@GuardedBy("mDeviceStateLock")
private void onUpdateCommunicationRoute(String eventSource) {
- mPreferredCommunicationDevice = getPreferredCommunicationDevice();
+ AudioDeviceAttributes preferredCommunicationDevice = preferredCommunicationDevice();
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "onUpdateCommunicationRoute, mPreferredCommunicationDevice: "
- + mPreferredCommunicationDevice + " eventSource: " + eventSource);
+ Log.v(TAG, "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+ + preferredCommunicationDevice + " eventSource: " + eventSource);
}
AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
- "onUpdateCommunicationRoute, mPreferredCommunicationDevice: "
- + mPreferredCommunicationDevice + " eventSource: " + eventSource)));
+ "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+ + preferredCommunicationDevice + " eventSource: " + eventSource)));
- if (mPreferredCommunicationDevice == null
- || !AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(
- mPreferredCommunicationDevice.getInternalType())) {
+ if (preferredCommunicationDevice == null
+ || preferredCommunicationDevice.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
AudioSystem.setParameters("BT_SCO=off");
} else {
AudioSystem.setParameters("BT_SCO=on");
}
- if (mPreferredCommunicationDevice == null) {
+ if (preferredCommunicationDevice == null) {
postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
} else {
postSetPreferredDevicesForStrategy(
- mCommunicationStrategyId, Arrays.asList(mPreferredCommunicationDevice));
+ mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
}
- mAudioService.postUpdateRingerModeServiceInt();
- dispatchCommunicationDevice();
+ }
+
+ private void onUpdatePhoneStrategyDevice(AudioDeviceAttributes device) {
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ boolean wasSpeakerphoneActive = isSpeakerphoneActive();
+ mPreferredCommunicationDevice = device;
+ updateActiveCommunicationDevice();
+ if (wasSpeakerphoneActive != isSpeakerphoneActive()) {
+ try {
+ mContext.sendBroadcastAsUser(
+ new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+ UserHandle.ALL);
+ } catch (Exception e) {
+ Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
+ }
+ }
+ mAudioService.postUpdateRingerModeServiceInt();
+ dispatchCommunicationDevice();
+ }
+ }
}
private CommunicationRouteClient removeCommunicationRouteClient(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 148c723..16a9626 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4192,7 +4192,7 @@
final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
|| ringerMode == AudioManager.RINGER_MODE_SILENT;
final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
- && mDeviceBroker.isBluetoothScoOn();
+ && mDeviceBroker.isBluetoothScoActive();
// Ask audio policy engine to force use Bluetooth SCO channel if needed
final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
+ "/" + Binder.getCallingPid();
@@ -5600,7 +5600,7 @@
switch (mPlatformType) {
case AudioSystem.PLATFORM_VOICE:
if (isInCommunication()) {
- if (mDeviceBroker.isBluetoothScoOn()) {
+ if (mDeviceBroker.isBluetoothScoActive()) {
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else {
@@ -5636,7 +5636,7 @@
}
default:
if (isInCommunication()) {
- if (mDeviceBroker.isBluetoothScoOn()) {
+ if (mDeviceBroker.isBluetoothScoActive()) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 63e7b4b..70f26ac 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -766,15 +766,18 @@
public long[] getAuthenticatorIds(int callingUserId) {
checkInternalPermission();
- final List<Long> ids = new ArrayList<>();
+ final List<Long> authenticatorIds = new ArrayList<>();
for (BiometricSensor sensor : mSensors) {
try {
- final long id = sensor.impl.getAuthenticatorId(callingUserId);
- if (Utils.isAtLeastStrength(sensor.getCurrentStrength(),
- Authenticators.BIOMETRIC_STRONG) && id != 0) {
- ids.add(id);
+ final boolean hasEnrollments = sensor.impl.hasEnrolledTemplates(callingUserId,
+ getContext().getOpPackageName());
+ final long authenticatorId = sensor.impl.getAuthenticatorId(callingUserId);
+ if (hasEnrollments && Utils.isAtLeastStrength(sensor.getCurrentStrength(),
+ Authenticators.BIOMETRIC_STRONG)) {
+ authenticatorIds.add(authenticatorId);
} else {
- Slog.d(TAG, "Sensor " + sensor + ", sensorId " + id
+ Slog.d(TAG, "Sensor " + sensor + ", sensorId " + sensor.id
+ + ", hasEnrollments: " + hasEnrollments
+ " cannot participate in Keystore operations");
}
} catch (RemoteException e) {
@@ -782,9 +785,9 @@
}
}
- long[] result = new long[ids.size()];
- for (int i = 0; i < ids.size(); i++) {
- result[i] = ids.get(i);
+ long[] result = new long[authenticatorIds.size()];
+ for (int i = 0; i < authenticatorIds.size(); i++) {
+ result[i] = authenticatorIds.get(i);
}
return result;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b31fa70..d16a2de 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -201,6 +201,9 @@
// Set to true when partial connectivity was detected.
public boolean partialConnectivity;
+ // Delay between when the network is disconnected and when the native network is destroyed.
+ public int teardownDelayMs;
+
// Captive portal info of the network from RFC8908, if any.
// Obtained by ConnectivityService and merged into NetworkAgent-provided information.
public CaptivePortalData capportApiData;
@@ -589,13 +592,13 @@
}
/**
- * Notify the NetworkAgent that the network is disconnected and destroyed.
+ * Notify the NetworkAgent that the native network has been destroyed.
*/
- public void onNetworkDisconnected() {
+ public void onNetworkDestroyed() {
try {
- networkAgent.onNetworkDisconnected();
+ networkAgent.onNetworkDestroyed();
} catch (RemoteException e) {
- Log.e(TAG, "Error sending network disconnected event", e);
+ Log.e(TAG, "Error sending network destroyed event", e);
}
}
@@ -675,6 +678,12 @@
@QosCallbackException.ExceptionType final int exceptionType) {
mQosCallbackTracker.sendEventQosCallbackError(qosCallbackId, exceptionType);
}
+
+ @Override
+ public void sendTeardownDelayMs(int teardownDelayMs) {
+ mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED,
+ teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
+ }
}
/**
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a070f27..820198c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -464,7 +464,7 @@
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+ .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE, null))
.build();
loadAlwaysOnPackage();
@@ -528,7 +528,7 @@
private void resetNetworkCapabilities() {
mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
.setUids(null)
- .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+ .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE, null))
.build();
}
@@ -1259,7 +1259,7 @@
capsBuilder.setUids(createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
- capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+ capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType(), mConfig.session));
// Only apps targeting Q and above can explicitly declare themselves as metered.
// These VPNs are assumed metered unless they state otherwise.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9d02576..393a4eb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2932,9 +2932,6 @@
@Override // Binder call
public int getRefreshRateSwitchingType() {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
- "Permission required read refresh rate switching type.");
final long token = Binder.clearCallingIdentity();
try {
return getRefreshRateSwitchingTypeInternal();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 56ad01b..cb8541e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -874,6 +874,10 @@
private void cleanupHandlerThreadAfterStop() {
setProximitySensorEnabled(false);
mHandler.removeCallbacksAndMessages(null);
+ if (mUnfinishedBusiness) {
+ mCallbacks.releaseSuspendBlocker();
+ mUnfinishedBusiness = false;
+ }
if (mPowerState != null) {
mPowerState.stop();
mPowerState = null;
@@ -1701,12 +1705,7 @@
}
}
- private final Runnable mCleanListener = new Runnable() {
- @Override
- public void run() {
- sendUpdatePowerState();
- }
- };
+ private final Runnable mCleanListener = this::sendUpdatePowerState;
private void setProximitySensorEnabled(boolean enable) {
if (enable) {
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 6201b94..5abc438 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -52,7 +52,8 @@
// Entries added by LockSettingsService once a user's synthetic password is known. At this point
// things are still keyed by userId.
- @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockouts;
+ @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFingerprint;
+ @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFace;
/**
* Authentication info for a successful user unlock via Synthetic Password. This can be used to
@@ -151,7 +152,8 @@
mContext = context;
mSpManager = spManager;
mHandler = handler;
- mPendingResetLockouts = new ArrayList<>();
+ mPendingResetLockoutsForFingerprint = new ArrayList<>();
+ mPendingResetLockoutsForFace = new ArrayList<>();
}
public void systemReady(@Nullable FingerprintManager fingerprintManager,
@@ -173,17 +175,34 @@
*/
void addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword) {
mHandler.post(() -> {
- Slog.d(TAG, "addPendingLockoutResetForUser: " + userId);
- mPendingResetLockouts.add(new UserAuthInfo(userId, gatekeeperPassword));
+ if (mFaceManager != null && mFaceManager.hasEnrolledTemplates(userId)) {
+ Slog.d(TAG, "Face addPendingLockoutResetForUser: " + userId);
+ mPendingResetLockoutsForFace.add(new UserAuthInfo(userId, gatekeeperPassword));
+ }
+
+ if (mFingerprintManager != null
+ && mFingerprintManager.hasEnrolledFingerprints(userId)) {
+ Slog.d(TAG, "Fingerprint addPendingLockoutResetForUser: " + userId);
+ mPendingResetLockoutsForFingerprint.add(new UserAuthInfo(userId,
+ gatekeeperPassword));
+ }
});
}
void processPendingLockoutResets() {
mHandler.post(() -> {
- Slog.d(TAG, "processPendingLockoutResets: " + mPendingResetLockouts.size());
- processPendingLockoutsForFingerprint(new ArrayList<>(mPendingResetLockouts));
- processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockouts));
- mPendingResetLockouts.clear();
+ if (!mPendingResetLockoutsForFace.isEmpty()) {
+ Slog.d(TAG, "Processing pending resetLockout for face");
+ processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockoutsForFace));
+ mPendingResetLockoutsForFace.clear();
+ }
+
+ if (!mPendingResetLockoutsForFingerprint.isEmpty()) {
+ Slog.d(TAG, "Processing pending resetLockout for fingerprint");
+ processPendingLockoutsForFingerprint(
+ new ArrayList<>(mPendingResetLockoutsForFingerprint));
+ mPendingResetLockoutsForFingerprint.clear();
+ }
});
}
diff --git a/services/core/java/com/android/server/media/MediaSessionConfig.java b/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
similarity index 98%
rename from services/core/java/com/android/server/media/MediaSessionConfig.java
rename to services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
index a866b0d..9bb8e2e 100644
--- a/services/core/java/com/android/server/media/MediaSessionConfig.java
+++ b/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
@@ -23,7 +23,7 @@
import java.io.PrintWriter;
import java.util.Set;
-class MediaSessionConfig {
+class MediaSessionDeviceConfig {
/**
* Denotes the duration for which a media button receiver will be exempted from
* FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index aefdbca..50cfe1f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -238,7 +238,7 @@
mSession2TokenCallback);
break;
case PHASE_ACTIVITY_MANAGER_READY:
- MediaSessionConfig.initialize(mContext);
+ MediaSessionDeviceConfig.initialize(mContext);
break;
}
}
@@ -560,7 +560,7 @@
PowerExemptionManager.class);
powerExemptionManager.addToTemporaryAllowList(targetPackage,
PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason,
- MediaSessionConfig.getMediaSessionCallbackFgsAllowlistDurationMs());
+ MediaSessionDeviceConfig.getMediaSessionCallbackFgsAllowlistDurationMs());
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1975,7 +1975,7 @@
}
mAudioPlayerStateMonitor.dump(mContext, pw, "");
}
- MediaSessionConfig.dump(pw, "");
+ MediaSessionDeviceConfig.dump(pw, "");
}
/**
@@ -2260,7 +2260,7 @@
mContext, keyEvent, callingPackageName,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver,
mHandler,
- MediaSessionConfig.getMediaButtonReceiverFgsAllowlistDurationMs());
+ MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs());
if (sent) {
String pkgName = mediaButtonReceiverHolder.getPackageName();
for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index ce49d88..5f0aa03 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -333,10 +333,7 @@
if (!shortcutPackage.rescanPackageIfNeeded(isNewApp, forceRescan)) {
if (isNewApp) {
- final ShortcutPackage sp = mPackages.remove(packageName);
- if (sp != null) {
- sp.removeShortcuts();
- }
+ mPackages.remove(packageName);
}
}
}
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 e16c67f..9bc9d9f 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -278,7 +278,7 @@
try {
return mContext.getPackageManager().getPermissionInfo(permissionName, 0);
} catch (NameNotFoundException e) {
- Slog.e(TAG, "Permission not found: " + permissionName);
+ Slog.w(TAG, "Permission not found: " + permissionName);
return null;
}
}
@@ -737,10 +737,9 @@
grantPermissionsToSystemPackage(pm, packageName, userId,
CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
- SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
+ SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
- ALWAYS_LOCATION_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS,
- ACTIVITY_RECOGNITION_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
}
}
if (locationExtraPackageNames != null) {
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 7f55723..268de3e 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -18,11 +18,13 @@
import static android.view.KeyEvent.KEYCODE_POWER;
import android.os.SystemClock;
+import android.util.Log;
import android.util.SparseLongArray;
import android.view.KeyEvent;
import com.android.internal.util.ToBooleanFunction;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -89,8 +91,8 @@
@Override
public String toString() {
- return "KeyCode1 = " + KeyEvent.keyCodeToString(mKeyCode1)
- + ", KeyCode2 = " + KeyEvent.keyCodeToString(mKeyCode2);
+ return KeyEvent.keyCodeToString(mKeyCode1) + " + "
+ + KeyEvent.keyCodeToString(mKeyCode2);
}
}
@@ -151,6 +153,7 @@
if (!rule.shouldInterceptKeys(mDownTimes)) {
return false;
}
+ Log.v(TAG, "Performing combination rule : " + rule);
rule.execute();
mTriggeredRule = rule;
return true;
@@ -230,4 +233,11 @@
}
return false;
}
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "KeyCombination rules:");
+ forAllRules(mRules, (rule)-> {
+ pw.println(prefix + " " + rule);
+ });
+ }
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f185464..f931df8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -434,7 +434,6 @@
// to hold wakelocks during dispatch and eliminating the critical path.
volatile boolean mPowerKeyHandled;
volatile boolean mBackKeyHandled;
- volatile boolean mBeganFromNonInteractive;
volatile boolean mEndCallKeyHandled;
volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
@@ -876,16 +875,6 @@
if (!mPowerKeyHandled) {
if (!interactive) {
wakeUpFromPowerKey(event.getDownTime());
- if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
- mBeganFromNonInteractive = true;
- } else {
- final int maxCount = getMaxMultiPressPowerCount();
- if (maxCount <= 1) {
- mPowerKeyHandled = true;
- } else {
- mBeganFromNonInteractive = true;
- }
- }
}
} else {
// handled by another power key policy.
@@ -895,7 +884,7 @@
}
}
- private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
+ private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {
final boolean handled = canceled || mPowerKeyHandled;
if (!handled) {
@@ -906,33 +895,36 @@
} else {
// handled by single key or another power key policy.
mSingleKeyGestureDetector.reset();
- finishPowerKeyPress();
}
+
+ finishPowerKeyPress();
}
private void finishPowerKeyPress() {
- mBeganFromNonInteractive = false;
mPowerKeyHandled = false;
if (mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.release();
}
}
- private void powerPress(long eventTime, boolean interactive, int count) {
+ private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
Slog.i(TAG, "Suppressed redundant power key press while "
+ "already in the process of turning the screen on.");
return;
}
+
+ final boolean interactive = Display.isOnState(mDefaultDisplay.getState());
+
Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive
- + " count=" + count + " beganFromNonInteractive=" + mBeganFromNonInteractive +
- " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
+ + " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive
+ + " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
if (count == 2) {
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == 3) {
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
- } else if (interactive && !mBeganFromNonInteractive) {
+ } else if (interactive && !beganFromNonInteractive) {
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
@@ -1906,12 +1898,19 @@
@Override
void onPress(long downTime) {
- powerPress(downTime, true, 1 /*count*/);
+ powerPress(downTime, 1 /*count*/,
+ mSingleKeyGestureDetector.beganFromNonInteractive());
finishPowerKeyPress();
}
@Override
void onLongPress(long eventTime) {
+ if (mSingleKeyGestureDetector.beganFromNonInteractive()
+ && !mSupportLongPressPowerWhenNonInteractive) {
+ Slog.v(TAG, "Not support long press power when device is not interactive.");
+ return;
+ }
+
powerLongPress(eventTime);
}
@@ -1923,7 +1922,7 @@
@Override
void onMultiPress(long downTime, int count) {
- powerPress(downTime, true, count);
+ powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive());
finishPowerKeyPress();
}
}
@@ -3562,7 +3561,7 @@
if (down) {
interceptPowerKeyDown(event, interactiveAndOn);
} else {
- interceptPowerKeyUp(event, interactiveAndOn, canceled);
+ interceptPowerKeyUp(event, canceled);
}
break;
}
@@ -3752,7 +3751,7 @@
}
}
- mSingleKeyGestureDetector.interceptKey(event);
+ mSingleKeyGestureDetector.interceptKey(event, interactive);
}
// The camera gesture will be detected by GestureLauncherService.
@@ -5230,6 +5229,8 @@
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
mGlobalKeyManager.dump(prefix, pw);
+ mKeyCombinationManager.dump(prefix, pw);
+ mSingleKeyGestureDetector.dump(prefix, pw);
if (mWakeGestureListener != null) {
mWakeGestureListener.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index cae2093..3f4d920 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -25,6 +25,7 @@
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -37,7 +38,7 @@
public final class SingleKeyGestureDetector {
private static final String TAG = "SingleKeyGesture";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = PhoneWindowManager.DEBUG_INPUT;
private static final int MSG_KEY_LONG_PRESS = 0;
private static final int MSG_KEY_VERY_LONG_PRESS = 1;
@@ -47,6 +48,7 @@
private final long mVeryLongPressTimeout;
private volatile int mKeyPressCounter;
+ private boolean mBeganFromNonInteractive = false;
private final ArrayList<SingleKeyRule> mRules = new ArrayList();
private SingleKeyRule mActiveRule = null;
@@ -57,7 +59,6 @@
private final Handler mHandler;
private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
-
/** Supported gesture flags */
public static final int KEY_LONGPRESS = 1 << 1;
public static final int KEY_VERYLONGPRESS = 1 << 2;
@@ -143,10 +144,10 @@
@Override
public String toString() {
- return "KeyCode = " + KeyEvent.keyCodeToString(mKeyCode)
- + ", long press : " + supportLongPress()
- + ", very Long press : " + supportVeryLongPress()
- + ", max multi press count : " + getMaxMultiPressCount();
+ return "KeyCode=" + KeyEvent.keyCodeToString(mKeyCode)
+ + ", LongPress=" + supportLongPress()
+ + ", VeryLongPress=" + supportVeryLongPress()
+ + ", MaxMultiPressCount=" + getMaxMultiPressCount();
}
}
@@ -161,8 +162,12 @@
mRules.add(rule);
}
- void interceptKey(KeyEvent event) {
+ void interceptKey(KeyEvent event, boolean interactive) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ // Store the non interactive state when first down.
+ if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) {
+ mBeganFromNonInteractive = !interactive;
+ }
interceptKeyDown(event);
} else {
interceptKeyUp(event);
@@ -268,6 +273,7 @@
Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
}
mActiveRule.onPress(downTime);
+ reset();
return true;
}
@@ -316,6 +322,17 @@
return false;
}
+ boolean beganFromNonInteractive() {
+ return mBeganFromNonInteractive;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "SingleKey rules:");
+ for (SingleKeyRule rule : mRules) {
+ pw.println(prefix + " " + rule);
+ }
+ }
+
private class KeyHandler extends Handler {
KeyHandler() {
super(Looper.getMainLooper());
@@ -354,7 +371,7 @@
} else {
mActiveRule.onMultiPress(eventTime, mKeyPressCounter);
}
- mKeyPressCounter = 0;
+ reset();
break;
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index a4459d0..4adcfb6 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -615,7 +615,7 @@
if (session == null || session.isStagedSessionFailed()) {
iter.remove();
deleteRollback(rollback,
- "Session " + session.getSessionId() + " not existed or failed");
+ "Session " + rollback.getStagedSessionId() + " not existed or failed");
continue;
}
diff --git a/services/core/java/com/android/server/stats/StatsHelper.java b/services/core/java/com/android/server/stats/StatsHelper.java
new file mode 100644
index 0000000..9b9f6b50
--- /dev/null
+++ b/services/core/java/com/android/server/stats/StatsHelper.java
@@ -0,0 +1,45 @@
+/*
+ * 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.stats;
+
+import static android.app.StatsManager.ACTION_STATSD_STARTED;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+/**
+ * Provides helper methods for the Statsd APEX
+ *
+ * @hide
+ **/
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public final class StatsHelper {
+ private StatsHelper() {}
+
+ /**
+ * Send statsd ready broadcast
+ *
+ **/
+ public static void sendStatsdReadyBroadcast(@NonNull final Context context) {
+ context.sendBroadcastAsUser(
+ new Intent(ACTION_STATSD_STARTED).addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
+ UserHandle.SYSTEM, android.Manifest.permission.DUMP);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index b947c88..16692e1 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -514,6 +514,18 @@
}
}
+ void onImeSurfaceShownChanged(WindowState windowState, boolean shown) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".onImeSurfaceShownChanged",
+ "windowState=" + windowState + "; shown=" + shown);
+ }
+ final int displayId = windowState.getDisplayId();
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.onImeSurfaceShownChanged(shown);
+ }
+ }
+
private static void populateTransformationMatrix(WindowState windowState,
Matrix outMatrix) {
windowState.getTransformationMatrix(sTempFloats, outMatrix);
@@ -766,6 +778,15 @@
}
}
+ void onImeSurfaceShownChanged(boolean shown) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".onImeSurfaceShownChanged", "shown=" + shown);
+ }
+ mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED,
+ shown ? 1 : 0, 0).sendToTarget();
+ }
+
MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
if (mAccessibilityTracing.isEnabled()) {
mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
@@ -1337,6 +1358,7 @@
public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
+ public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6;
MyHandler(Looper looper) {
super(looper);
@@ -1380,6 +1402,11 @@
}
}
} break;
+
+ case MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED: {
+ final boolean shown = message.arg1 == 1;
+ mCallbacks.onImeWindowVisibilityChanged(shown);
+ } break;
}
}
}
@@ -2123,5 +2150,4 @@
}
}
}
-
}
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 1262dee..5a8af45 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
+import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
@@ -34,6 +36,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -49,15 +54,22 @@
import android.service.displayhash.DisplayHashParams;
import android.service.displayhash.DisplayHasherService;
import android.service.displayhash.IDisplayHasherService;
+import android.util.AttributeSet;
import android.util.Size;
import android.util.Slog;
+import android.util.Xml;
import android.view.MagnificationSpec;
import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -102,6 +114,41 @@
private final Matrix mTmpMatrix = new Matrix();
private final RectF mTmpRectF = new RectF();
+ /**
+ * Lock used when retrieving xml metadata. Lock when retrieving the xml data the first time
+ * since it will be cached after that. Check if {@link #mParsedXml} is set to determine if the
+ * metadata needs to retrieved.
+ */
+ private final Object mParseXmlLock = new Object();
+
+ /**
+ * Flag whether the xml metadata has been retrieved and parsed. Once this is set to true,
+ * there's no need to request metadata again.
+ */
+ @GuardedBy("mParseXmlLock")
+ private boolean mParsedXml;
+
+ /**
+ * Specified throttle time in milliseconds. Don't allow an app to generate a display hash more
+ * than once per throttleTime
+ */
+ private int mThrottleDurationMillis = 0;
+
+ /**
+ * The last time an app requested to generate a display hash in System time.
+ */
+ private long mLastRequestTimeMs;
+
+ /**
+ * The last uid that requested to generate a hash.
+ */
+ private int mLastRequestUid;
+
+ /**
+ * Only used for testing. Throttling should always be enabled unless running tests
+ */
+ private boolean mDisplayHashThrottlingEnabled = true;
+
private interface Command {
void run(IDisplayHasherService service) throws RemoteException;
}
@@ -131,6 +178,10 @@
return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
}
+ void setDisplayHashThrottlingEnabled(boolean enable) {
+ mDisplayHashThrottlingEnabled = enable;
+ }
+
private void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
connectAndRun(
@@ -138,8 +189,36 @@
callback));
}
+ private boolean allowedToGenerateHash(int uid) {
+ if (!mDisplayHashThrottlingEnabled) {
+ // Always allow to generate the hash. This is used to allow tests to run without
+ // waiting on the designated threshold.
+ return true;
+ }
+
+ long currentTime = System.currentTimeMillis();
+ if (mLastRequestUid != uid) {
+ mLastRequestUid = uid;
+ mLastRequestTimeMs = currentTime;
+ return true;
+ }
+
+ int throttleDurationMs = getThrottleDurationMillis();
+ if (currentTime - mLastRequestTimeMs < throttleDurationMs) {
+ return false;
+ }
+
+ mLastRequestTimeMs = currentTime;
+ return true;
+ }
+
void generateDisplayHash(SurfaceControl.LayerCaptureArgs.Builder args,
- Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) {
+ Rect boundsInWindow, String hashAlgorithm, int uid, RemoteCallback callback) {
+ if (!allowedToGenerateHash(uid)) {
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS);
+ return;
+ }
+
final Map<String, DisplayHashParams> displayHashAlgorithmsMap = getDisplayHashAlgorithms();
DisplayHashParams displayHashParams = displayHashAlgorithmsMap.get(hashAlgorithm);
if (displayHashParams == null) {
@@ -277,6 +356,64 @@
}
}
+ private int getThrottleDurationMillis() {
+ if (!parseXmlProperties()) {
+ return 0;
+ }
+ return mThrottleDurationMillis;
+ }
+
+ private boolean parseXmlProperties() {
+ // We have a separate lock for the xml parsing since it doesn't need to make the
+ // request through the service connection. Instead, we have a lock to ensure we can
+ // properly cache the xml metadata so we don't need to call into the ExtServices
+ // process for each request.
+ synchronized (mParseXmlLock) {
+ if (mParsedXml) {
+ return true;
+ }
+
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return false;
+
+ final PackageManager pm = mContext.getPackageManager();
+
+ XmlResourceParser parser;
+ parser = serviceInfo.loadXmlMetaData(pm, SERVICE_META_DATA);
+ if (parser == null) {
+ return false;
+ }
+
+ Resources res;
+ try {
+ res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while (true) {
+ try {
+ if (!((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG)) {
+ break;
+ }
+ } catch (XmlPullParserException | IOException e) {
+ return false;
+ }
+ }
+
+ TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHasherService);
+ mThrottleDurationMillis = sa.getInt(
+ R.styleable.DisplayHasherService_throttleDurationMillis, 0);
+ sa.recycle();
+ mParsedXml = true;
+ return true;
+ }
+ }
+
/**
* Run a command, starting the service connection if necessary.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6046cc6..9ff701c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1539,20 +1539,6 @@
}
}
- @Override
- public boolean canUseRotationResolver() {
- if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
-
- switch (mCurrentAppOrientation) {
- case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
- case ActivityInfo.SCREEN_ORIENTATION_USER:
- case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
- case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
- case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
- return true;
- }
- return false;
- }
@Override
public void onProposedRotationChanged(int rotation) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 8148f15..ab515d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -113,7 +113,7 @@
*
* @param magnificationRegion the current magnification region
*/
- public void onMagnificationRegionChanged(Region magnificationRegion);
+ void onMagnificationRegionChanged(Region magnificationRegion);
/**
* Called when an application requests a rectangle on the screen to allow
@@ -124,20 +124,27 @@
* @param right The rectangle right.
* @param bottom The rectangle bottom.
*/
- public void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
+ void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
/**
* Notifies that the rotation changed.
*
* @param rotation The current rotation.
*/
- public void onRotationChanged(int rotation);
+ void onRotationChanged(int rotation);
/**
* Notifies that the context of the user changed. For example, an application
* was started.
*/
- public void onUserContextChanged();
+ void onUserContextChanged();
+
+ /**
+ * Notifies that 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);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d494b75..a670b39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8626,6 +8626,14 @@
return mDisplayHashController.verifyDisplayHash(displayHash);
}
+ @Override
+ public void setDisplayHashThrottlingEnabled(boolean enable) {
+ if (!checkCallingPermission(READ_FRAME_BUFFER, "setDisplayHashThrottle()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+ mDisplayHashController.setDisplayHashThrottlingEnabled(enable);
+ }
+
void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow,
String hashAlgorithm, RemoteCallback callback) {
final SurfaceControl displaySurfaceControl;
@@ -8639,6 +8647,13 @@
return;
}
+ if (win.mActivityRecord == null || !win.mActivityRecord.isState(
+ Task.ActivityState.RESUMED)) {
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_MISSING_WINDOW);
+ return;
+ }
+
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
@@ -8669,8 +8684,8 @@
.setUid(uid)
.setSourceCrop(boundsInDisplay);
- mDisplayHashController.generateDisplayHash(args, boundsInWindow,
- hashAlgorithm, callback);
+ mDisplayHashController.generateDisplayHash(args, boundsInWindow, hashAlgorithm, uid,
+ callback);
}
boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 49e704f..b971cab 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -275,19 +275,6 @@
}
}
- /**
- * Returns true if the current status of the phone is suitable for using rotation resolver
- * service.
- *
- * To reduce the power consumption of rotation resolver service, rotation query should run less
- * frequently than other low power orientation sensors. This method is used to check whether
- * the current status of the phone is necessary to request a suggested screen rotation from the
- * rotation resolver service. Note that it always returns {@code false} in the base class. It
- * should be overridden in the derived classes.
- */
- public boolean canUseRotationResolver() {
- return false;
- }
/**
* Returns true if the rotation resolver feature is enabled by setting. It means {@link
@@ -1155,7 +1142,7 @@
}
}
- if (isRotationResolverEnabled() && canUseRotationResolver()) {
+ if (isRotationResolverEnabled()) {
if (mRotationResolverService == null) {
mRotationResolverService = LocalServices.getService(
RotationResolverInternal.class);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index eb83152..92c9522 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3462,6 +3462,9 @@
if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST) {
mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
}
+ if (mIsImWindow && mWmService.mAccessibilityController != null) {
+ mWmService.mAccessibilityController.onImeSurfaceShownChanged(this, shown);
+ }
}
private void logExclusionRestrictions(int side) {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 29aedce..1208ecc 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -30,6 +30,8 @@
import android.os.ServiceManager;
import android.os.UpdateEngine;
import android.os.UpdateEngineCallback;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.Log;
@@ -301,10 +303,18 @@
new Thread(() -> {
try {
- String reportPath = mIProfcollect.report();
+ String reportUuid = mIProfcollect.report();
+
if (!uploadReport) {
return;
}
+
+ final int profileId = getBBProfileId();
+ mIProfcollect.copy_report_to_bb(profileId, reportUuid);
+ String reportPath =
+ "/data/user/" + profileId
+ + "/com.google.android.apps.internal.betterbug/cache/"
+ + reportUuid + ".zip";
Intent uploadIntent =
new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE")
.setPackage("com.google.android.apps.internal.betterbug")
@@ -316,9 +326,27 @@
if (context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0) != null) {
context.sendBroadcast(uploadIntent);
}
+ mIProfcollect.delete_report(reportUuid);
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
}
}).start();
}
+
+ /**
+ * Get BetterBug's profile ID. It is the work profile ID, if it exists. Otherwise the system
+ * user ID.
+ *
+ * @return BetterBug's profile ID.
+ */
+ private int getBBProfileId() {
+ UserManager userManager = UserManager.get(getContext());
+ int[] profiles = userManager.getProfileIds(UserHandle.USER_SYSTEM, false);
+ for (int p : profiles) {
+ if (userManager.getUserInfo(p).isManagedProfile()) {
+ return p;
+ }
+ }
+ return UserHandle.USER_SYSTEM;
+ }
}
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 29691fb..502f64a 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,7 +16,7 @@
package com.android.server.accessibility.magnification;
-import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
+import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -96,8 +96,8 @@
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
private final MagnificationAnimationCallback mAnimationCallback = mock(
MagnificationAnimationCallback.class);
- private final MagnificationRequestObserver mRequestObserver = mock(
- MagnificationRequestObserver.class);
+ private final MagnificationInfoChangedCallback mRequestObserver = mock(
+ MagnificationInfoChangedCallback.class);
final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
ValueAnimator mMockValueAnimator;
@@ -1145,6 +1145,15 @@
verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(false));
}
+ @Test
+ public void testImeWindowIsShown_serviceNotified() {
+ register(DISPLAY_0);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(DISPLAY_0);
+ callbacks.onImeWindowVisibilityChanged(true);
+ mMessageCapturingHandler.sendAllMessages();
+ verify(mRequestObserver).onImeWindowVisibilityChanged(eq(true));
+ }
+
private void setScaleToMagnifying() {
register(DISPLAY_0);
float scale = 2.0f;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 28a6ff7..f881f04 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -53,7 +53,7 @@
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.EventStreamTransformation;
-import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
import com.android.server.wm.WindowManagerInternal;
@@ -126,7 +126,7 @@
@Mock
MagnificationGestureHandler.Callback mMockCallback;
@Mock
- MagnificationRequestObserver mMagnificationRequestObserver;
+ MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
@Mock
WindowMagnificationPromptController mWindowMagnificationPromptController;
@@ -151,7 +151,7 @@
when(mockController.getAnimationDuration()).thenReturn(1000L);
when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true);
mFullScreenMagnificationController = new FullScreenMagnificationController(mockController,
- new Object(), mMagnificationRequestObserver) {
+ new Object(), mMagnificationInfoChangedCallback) {
@Override
public boolean magnificationRegionContains(int displayId, float x, float y) {
return true;
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 cf23197..3dff36e 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
@@ -486,6 +486,47 @@
eq(MODE_WINDOW));
}
+ @Test
+ public void imeWindowStateShown_windowMagnifying_logWindowMode() {
+ mMagnificationController.onWindowMagnificationActivationState(true);
+
+ mMagnificationController.onImeWindowVisibilityChanged(true);
+
+ verify(mMagnificationController).logMagnificationModeWithIme(
+ eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+ }
+
+ @Test
+ public void imeWindowStateShown_fullScreenMagnifying_logFullScreenMode() {
+ mMagnificationController.onFullScreenMagnificationActivationState(true);
+
+ mMagnificationController.onImeWindowVisibilityChanged(true);
+
+ verify(mMagnificationController).logMagnificationModeWithIme(
+ eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
+ }
+
+ @Test
+ public void imeWindowStateShown_noMagnifying_noLogAnyMode() {
+ mMagnificationController.onImeWindowVisibilityChanged(true);
+
+ verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ }
+
+ @Test
+ public void imeWindowStateHidden_windowMagnifying_noLogAnyMode() {
+ mMagnificationController.onFullScreenMagnificationActivationState(true);
+
+ verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ }
+
+ @Test
+ public void imeWindowStateHidden_fullScreenMagnifying_noLogAnyMode() {
+ mMagnificationController.onWindowMagnificationActivationState(true);
+
+ verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ }
+
private void setMagnificationEnabled(int mode) throws RemoteException {
setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index ef3e320..88b0651 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -850,6 +850,12 @@
}
@Override
+ public void getStorageInfo(String packageName, String databaseName, int userId,
+ IAppSearchResultCallback callback) throws RemoteException {
+ ignore(callback);
+ }
+
+ @Override
public void persistToDisk(int userId) throws RemoteException {
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index f5d0ca7..584bcf4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -58,7 +58,6 @@
private com.android.server.wm.WindowOrientationListener mWindowOrientationListener;
private int mFinalizedRotation;
private boolean mRotationResolverEnabled;
- private boolean mCanUseRotationResolver;
private SensorEvent mFakeSensorEvent;
private Sensor mFakeSensor;
@@ -66,7 +65,6 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mRotationResolverEnabled = true;
- mCanUseRotationResolver = true;
mFakeRotationResolverInternal = new TestableRotationResolver();
doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE);
@@ -89,16 +87,6 @@
}
@Test
- public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() {
- mCanUseRotationResolver = false;
-
- mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
-
- assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
-
- }
-
- @Test
public void testOnSensorChanged_normalCase() {
mFakeRotationResolverInternal.mResult = Surface.ROTATION_180;
@@ -139,11 +127,6 @@
}
@Override
- public boolean canUseRotationResolver() {
- return mCanUseRotationResolver;
- }
-
- @Override
public boolean isRotationResolverEnabled() {
return mRotationResolverEnabled;
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 3025a95..222c692 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -26,6 +26,7 @@
import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
@@ -62,6 +63,10 @@
private long mLongPressTime;
private long mVeryLongPressTime;
+ // Allow press from non interactive mode.
+ private boolean mAllowNonInteractiveForPress = true;
+ private boolean mAllowNonInteractiveForLongPress = true;
+
@Before
public void setUp() {
mDetector = new SingleKeyGestureDetector(mContext);
@@ -81,11 +86,17 @@
}
@Override
public void onPress(long downTime) {
+ if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
+ return;
+ }
mShortPressed.countDown();
}
@Override
void onLongPress(long downTime) {
+ if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForLongPress) {
+ return;
+ }
mLongPressed.countDown();
}
@@ -96,6 +107,9 @@
@Override
void onMultiPress(long downTime, int count) {
+ if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
+ return;
+ }
mMultiPressed.countDown();
assertEquals(mMaxMultiPressPowerCount, count);
}
@@ -103,9 +117,13 @@
}
private void pressKey(long eventTime, int keyCode, long pressTime) {
+ pressKey(eventTime, keyCode, pressTime, true /* interactive */);
+ }
+
+ private void pressKey(long eventTime, int keyCode, long pressTime, boolean interactive) {
final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
keyCode, 0 /* repeat */, 0 /* metaState */);
- mDetector.interceptKey(keyDown);
+ mDetector.interceptKey(keyDown, interactive);
// keep press down.
try {
@@ -118,7 +136,7 @@
final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP,
keyCode, 0 /* repeat */, 0 /* metaState */);
- mDetector.interceptKey(keyUp);
+ mDetector.interceptKey(keyUp, interactive);
}
@Test
@@ -149,4 +167,18 @@
pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
+
+ @Test
+ public void testNonInteractive() throws InterruptedException {
+ long eventTime = SystemClock.uptimeMillis();
+ // Disallow short press behavior from non interactive.
+ mAllowNonInteractiveForPress = false;
+ pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */, false /* interactive */);
+ assertFalse(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+
+ // Allow long press behavior from non interactive.
+ eventTime = SystemClock.uptimeMillis();
+ pressKey(eventTime, KEYCODE_POWER, mLongPressTime, false /* interactive */);
+ assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 6315b24..b384e50 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -391,6 +391,7 @@
* event to the framework.
* @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
* exchange if it is supported by the device.
+ * @hide
*/
public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
@NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) {
@@ -399,14 +400,45 @@
}
/**
+ * Retrieve the implementation of UCE for this {@link RcsFeature}, which can use either
+ * presence or OPTIONS for capability exchange.
+ *
+ * Will only be requested by the framework if capability exchange is configured
+ * as capable during a
+ * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+ * operation and the RcsFeature sets the status of the capability to true using
+ * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+ *
+ * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+ * event to the framework.
+ * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
+ * exchange if it is supported by the device.
+ */
+ public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
+ @NonNull CapabilityExchangeEventListener listener) {
+ // Base Implementation, override to implement functionality
+ return new RcsCapabilityExchangeImplBase();
+ }
+
+ /**
* Remove the given CapabilityExchangeImplBase instance.
* @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be removed.
+ * @hide
*/
public void removeCapabilityExchangeImpl(
@NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
// Override to implement the process of removing RcsCapabilityExchangeImplBase instance.
}
+ /**
+ * Remove the given CapabilityExchangeImplBase instance.
+ * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
+ */
+ public void destroyCapabilityExchangeImpl(
+ @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
+ // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
+ }
+
/**{@inheritDoc}*/
@Override
public void onFeatureRemoved() {
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 25b9446..a117adc 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -356,12 +356,13 @@
void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
}
- private final Executor mBinderExecutor;
+ private Executor mBinderExecutor;
/**
* Create a new RcsCapabilityExchangeImplBase instance.
*
* @param executor The executor that remote calls from the framework will be called on.
+ * @hide
*/
public RcsCapabilityExchangeImplBase(@NonNull Executor executor) {
if (executor == null) {
@@ -371,6 +372,12 @@
}
/**
+ * Create a new RcsCapabilityExchangeImplBase instance.
+ */
+ public RcsCapabilityExchangeImplBase() {
+ }
+
+ /**
* The user capabilities of one or multiple contacts have been requested by the framework.
* <p>
* The implementer must follow up this call with an
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index e6a4501..ed0a98d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -101,10 +101,10 @@
}
} else {
assertLayersStart {
- this.coversAtLeast(startingBounds)
+ this.visibleRegion().coversAtLeast(startingBounds)
}
assertLayersEnd {
- this.coversAtLeast(endingBounds)
+ this.visibleRegion().coversAtLeast(endingBounds)
}
}
}
@@ -152,10 +152,10 @@
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
assertLayersStart {
- this.coversExactly(startingPos, NAV_BAR_LAYER_NAME)
+ this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(startingPos)
}
assertLayersEnd {
- this.coversExactly(endingPos, NAV_BAR_LAYER_NAME)
+ this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(endingPos)
}
}
@@ -168,10 +168,10 @@
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
assertLayersStart {
- this.coversExactly(startingPos, STATUS_BAR_WINDOW_NAME)
+ this.visibleRegion(STATUS_BAR_WINDOW_NAME).coversExactly(startingPos)
}
assertLayersEnd {
- this.coversExactly(endingPos, STATUS_BAR_WINDOW_NAME)
+ this.visibleRegion(STATUS_BAR_WINDOW_NAME).coversExactly(endingPos)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index e118363..a524466 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
@@ -87,13 +88,13 @@
testSpec.statusBarLayerIsAlwaysVisible()
}
- @Presubmit
+ @FlakyTest
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
}
- @Presubmit
+ @FlakyTest
@Test
open fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 619a05e..bd7c185 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -34,7 +34,7 @@
wmHelper: WindowManagerStateHelper?
) {
// do nothing (the app is focused automatically)
- waitAndAssertIMEShown(device, wmHelper)
+ waitIMEShown(device, wmHelper)
}
override fun open() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index d8091a9..83fddae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -51,17 +51,17 @@
"was left in an unknown state (e.g. in split screen)"
}
editText.click()
- waitAndAssertIMEShown(device, wmHelper)
+ waitIMEShown(device, wmHelper)
}
- protected fun waitAndAssertIMEShown(
+ protected fun waitIMEShown(
device: UiDevice,
wmHelper: WindowManagerStateHelper? = null
) {
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeWindowShown()) { "IME did not appear" }
+ wmHelper.waitImeWindowShown()
}
}
@@ -78,7 +78,7 @@
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
+ wmHelper.waitImeWindowGone()
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index d39044ab..7ba9db1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -20,6 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -134,7 +135,7 @@
}
}
- @Presubmit
+ @FlakyTest
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index f037f1d..35ad597 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -24,7 +24,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,20 +60,6 @@
@Presubmit
@Test
- override fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- super.navBarLayerRotatesAndScales()
- }
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun navBarLayerRotatesAndScales_flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- super.navBarLayerRotatesAndScales()
- }
-
- @Presubmit
- @Test
fun screenshotLayerBecomesInvisible() {
testSpec.assertLayers {
this.isVisible(testApp.getPackage())
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index f037f08..2989035 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -139,7 +139,7 @@
@Test
open fun appLayerRotates_StartingPos() {
testSpec.assertLayersStart {
- this.coversExactly(startingPos, testApp.getPackage())
+ this.visibleRegion(testApp.getPackage()).coversExactly(startingPos)
}
}
@@ -147,7 +147,7 @@
@Test
open fun appLayerRotates_EndingPos() {
testSpec.assertLayersEnd {
- this.coversExactly(endingPos, testApp.getPackage())
+ this.visibleRegion(testApp.getPackage()).coversExactly(endingPos)
}
}
}
\ No newline at end of file
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
index b7a42ec..fee65f0 100644
--- a/tests/net/java/android/net/VpnTransportInfoTest.java
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
+
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
@@ -33,23 +36,33 @@
@Test
public void testParceling() {
- VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- assertParcelSane(v, 1 /* fieldCount */);
+ VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
+ assertParcelSane(v, 2 /* fieldCount */);
}
@Test
public void testEqualsAndHashCode() {
- VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE);
- VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- VpnTransportInfo v4 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY);
- VpnTransportInfo v5 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM);
+ String session1 = "12345";
+ String session2 = "6789";
+ VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+ VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1);
+ VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+ VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1);
+ VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1);
+ VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2);
- assertNotEquals(v1, v2);
- assertNotEquals(v3, v4);
- assertNotEquals(v4, v5);
+ VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
+ VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
- assertEquals(v1, v3);
- assertEquals(v1.hashCode(), v3.hashCode());
+ assertNotEquals(v11, v12);
+ assertNotEquals(v13, v14);
+ assertNotEquals(v14, v15);
+ assertNotEquals(v14, v21);
+
+ assertEquals(v11, v13);
+ assertEquals(v31, v32);
+ assertEquals(v11.hashCode(), v13.hashCode());
+ assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions());
+ assertEquals(session1, v15.makeCopy(REDACT_NONE).sessionId);
}
-}
\ No newline at end of file
+}
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
index b62bdbc..5006d53 100644
--- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
@@ -101,7 +101,7 @@
// Check valid customization generates expected array.
val validRes = arrayOf("0,3", "1,0", "4,4")
- val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0)
+ val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0, 0)
val mockContext = getMockedContextWithStringArrayRes(
R.array.config_networkSupportedKeepaliveCount,
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 54d4938..8254f6b3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -717,6 +717,9 @@
private int mProbesSucceeded;
private String mNmValidationRedirectUrl = null;
private boolean mNmProvNotificationRequested = false;
+ private Runnable mCreatedCallback;
+ private Runnable mUnwantedCallback;
+ private Runnable mDisconnectedCallback;
private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
// Contains the redirectUrl from networkStatus(). Before reading, wait for
@@ -771,6 +774,24 @@
mRedirectUrl = redirectUrl;
mNetworkStatusReceived.open();
}
+
+ @Override
+ public void onNetworkCreated() {
+ super.onNetworkCreated();
+ if (mCreatedCallback != null) mCreatedCallback.run();
+ }
+
+ @Override
+ public void onNetworkUnwanted() {
+ super.onNetworkUnwanted();
+ if (mUnwantedCallback != null) mUnwantedCallback.run();
+ }
+
+ @Override
+ public void onNetworkDestroyed() {
+ super.onNetworkDestroyed();
+ if (mDisconnectedCallback != null) mDisconnectedCallback.run();
+ }
};
assertEquals(na.getNetwork().netId, nmNetworkCaptor.getValue().netId);
@@ -972,6 +993,18 @@
p.timestampMillis = DATA_STALL_TIMESTAMP;
mNmCallbacks.notifyDataStallSuspected(p);
}
+
+ public void setCreatedCallback(Runnable r) {
+ mCreatedCallback = r;
+ }
+
+ public void setUnwantedCallback(Runnable r) {
+ mUnwantedCallback = r;
+ }
+
+ public void setDisconnectedCallback(Runnable r) {
+ mDisconnectedCallback = r;
+ }
}
/**
@@ -1182,10 +1215,12 @@
if (mAgentRegistered) throw new IllegalStateException("already registered");
updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent");
mConfig = new VpnConfig();
+ mConfig.session = "MySession12345";
setUids(uids);
if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
mInterface = VPN_IFNAME;
- mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+ mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType(),
+ mConfig.session));
mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
mNetworkCapabilities);
mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
@@ -2800,6 +2835,94 @@
}
@Test
+ public void testNetworkAgentCallbacks() throws Exception {
+ // Keeps track of the order of events that happen in this test.
+ final LinkedBlockingQueue<String> eventOrder = new LinkedBlockingQueue<>();
+
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final AtomicReference<Network> wifiNetwork = new AtomicReference<>();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+
+ // Expectations for state when various callbacks fire. These expectations run on the handler
+ // thread and not on the test thread because they need to prevent the handler thread from
+ // advancing while they examine state.
+
+ // 1. When onCreated fires, netd has been told to create the network.
+ mWiFiNetworkAgent.setCreatedCallback(() -> {
+ eventOrder.offer("onNetworkCreated");
+ wifiNetwork.set(mWiFiNetworkAgent.getNetwork());
+ assertNotNull(wifiNetwork.get());
+ try {
+ verify(mMockNetd).networkCreatePhysical(wifiNetwork.get().getNetId(),
+ INetd.PERMISSION_NONE);
+ } catch (RemoteException impossible) {
+ fail();
+ }
+ });
+
+ // 2. onNetworkUnwanted isn't precisely ordered with respect to any particular events. Just
+ // check that it is fired at some point after disconnect.
+ mWiFiNetworkAgent.setUnwantedCallback(() -> eventOrder.offer("onNetworkUnwanted"));
+
+ // 3. While the teardown timer is running, connectivity APIs report the network is gone, but
+ // netd has not yet been told to destroy it.
+ final Runnable duringTeardown = () -> {
+ eventOrder.offer("timePasses");
+ assertNull(mCm.getLinkProperties(wifiNetwork.get()));
+ try {
+ verify(mMockNetd, never()).networkDestroy(wifiNetwork.get().getNetId());
+ } catch (RemoteException impossible) {
+ fail();
+ }
+ };
+
+ // 4. After onNetworkDisconnected is called, connectivity APIs report the network is gone,
+ // and netd has been told to destroy it.
+ mWiFiNetworkAgent.setDisconnectedCallback(() -> {
+ eventOrder.offer("onNetworkDisconnected");
+ assertNull(mCm.getLinkProperties(wifiNetwork.get()));
+ try {
+ verify(mMockNetd).networkDestroy(wifiNetwork.get().getNetId());
+ } catch (RemoteException impossible) {
+ fail();
+ }
+ });
+
+ // Connect a network, and file a request for it after it has come up, to ensure the nascent
+ // timer is cleared and the test does not have to wait for it. Filing the request after the
+ // network has come up is necessary because ConnectivityService does not appear to clear the
+ // nascent timer if the first request satisfied by the network was filed before the network
+ // connected.
+ // TODO: fix this bug, file the request before connecting, and remove the waitForIdle.
+ mWiFiNetworkAgent.connectWithoutInternet();
+ waitForIdle();
+ mCm.requestNetwork(request, callback);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Set teardown delay and make sure CS has processed it.
+ mWiFiNetworkAgent.getNetworkAgent().setTeardownDelayMs(300);
+ waitForIdle();
+
+ // Post the duringTeardown lambda to the handler so it fires while teardown is in progress.
+ // The delay must be long enough it will run after the unregisterNetworkCallback has torn
+ // down the network and started the teardown timer, and short enough that the lambda is
+ // scheduled to run before the teardown timer.
+ final Handler h = new Handler(mCsHandlerThread.getLooper());
+ h.postDelayed(duringTeardown, 150);
+
+ // Disconnect the network and check that events happened in the right order.
+ mCm.unregisterNetworkCallback(callback);
+ assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ mCm.unregisterNetworkCallback(callback);
+ }
+
+ @Test
public void testExplicitlySelected() throws Exception {
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index a447cef..24c60b7 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -42,6 +42,10 @@
using android::idmap2::policy::kPolicyStringToFlag;
namespace aapt {
+namespace {
+constexpr const char* kPublicGroupTag = "public-group";
+constexpr const char* kStagingPublicGroupTag = "staging-public-group";
+} // namespace
constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
@@ -102,6 +106,7 @@
ResourceId id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
+ bool staged_api = false;
bool allow_new = false;
Maybe<OverlayableItem> overlayable_item;
@@ -122,6 +127,7 @@
if (res->visibility_level != Visibility::Level::kUndefined) {
Visibility visibility;
visibility.level = res->visibility_level;
+ visibility.staged_api = res->staged_api;
visibility.source = res->source;
visibility.comment = res->comment;
res_builder.SetVisibility(visibility);
@@ -525,6 +531,7 @@
{"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
{"public", std::mem_fn(&ResourceParser::ParsePublic)},
{"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
+ {"staging-public-group", std::mem_fn(&ResourceParser::ParseStagingPublicGroup)},
{"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
{"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
std::placeholders::_2, std::placeholders::_3)},
@@ -653,7 +660,8 @@
const auto bag_iter = elToBagMap.find(resource_type);
if (bag_iter != elToBagMap.end()) {
// Ensure we have a name (unless this is a <public-group> or <overlayable>).
- if (resource_type != "public-group" && resource_type != "overlayable") {
+ if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag &&
+ resource_type != "overlayable") {
if (!maybe_name) {
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name() << "> missing 'name' attribute");
@@ -890,54 +898,45 @@
return true;
}
-bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
- if (options_.visibility) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> tag not allowed with --visibility flag");
- return false;
- }
-
+template <typename Func>
+bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
+ const char* tag_name, IDiagnostics* diag, Func&& func) {
if (out_resource->config != ConfigDescription::DefaultConfig()) {
- diag_->Warn(DiagMessage(out_resource->source)
- << "ignoring configuration '" << out_resource->config
- << "' for <public-group> tag");
+ diag->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config << "' for <" << tag_name
+ << "> tag");
}
Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> must have a 'type' attribute");
+ diag->Error(DiagMessage(out_resource->source)
+ << "<" << tag_name << "> must have a 'type' attribute");
return false;
}
const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
if (!parsed_type) {
- diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
- << maybe_type.value()
- << "' in <public-group>");
+ diag->Error(DiagMessage(out_resource->source)
+ << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
return false;
}
- Maybe<StringPiece> maybe_id_str =
- xml::FindNonEmptyAttribute(parser, "first-id");
+ Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
if (!maybe_id_str) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> must have a 'first-id' attribute");
+ diag->Error(DiagMessage(out_resource->source)
+ << "<" << tag_name << "> must have a 'first-id' attribute");
return false;
}
- Maybe<ResourceId> maybe_id =
- ResourceUtils::ParseResourceId(maybe_id_str.value());
+ Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
if (!maybe_id) {
- diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
- << maybe_id_str.value()
- << "' in <public-group>");
+ diag->Error(DiagMessage(out_resource->source)
+ << "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">");
return false;
}
- ResourceId next_id = maybe_id.value();
-
std::string comment;
+ ResourceId next_id = maybe_id.value();
bool error = false;
const size_t depth = parser->depth();
while (xml::XmlPullParser::NextChildNode(parser, depth)) {
@@ -949,53 +948,72 @@
continue;
}
- const Source item_source = source_.WithLine(parser->line_number());
+ const Source item_source = out_resource->source.WithLine(parser->line_number());
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
if (element_namespace.empty() && element_name == "public") {
- Maybe<StringPiece> maybe_name =
- xml::FindNonEmptyAttribute(parser, "name");
+ auto maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
- diag_->Error(DiagMessage(item_source)
- << "<public> must have a 'name' attribute");
+ diag->Error(DiagMessage(item_source) << "<public> must have a 'name' attribute");
error = true;
continue;
}
if (xml::FindNonEmptyAttribute(parser, "id")) {
- diag_->Error(DiagMessage(item_source)
- << "'id' is ignored within <public-group>");
+ diag->Error(DiagMessage(item_source) << "'id' is ignored within <" << tag_name << ">");
error = true;
continue;
}
if (xml::FindNonEmptyAttribute(parser, "type")) {
- diag_->Error(DiagMessage(item_source)
- << "'type' is ignored within <public-group>");
+ diag->Error(DiagMessage(item_source) << "'type' is ignored within <" << tag_name << ">");
error = true;
continue;
}
- ParsedResource child_resource;
- child_resource.name.type = *parsed_type;
- child_resource.name.entry = maybe_name.value().to_string();
- child_resource.id = next_id;
- // NOLINTNEXTLINE(bugprone-use-after-move) move+reset comment
- child_resource.comment = std::move(comment);
- child_resource.source = item_source;
- child_resource.visibility_level = Visibility::Level::kPublic;
- out_resource->child_resources.push_back(std::move(child_resource));
+ ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
+ .name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
+ .source = item_source,
+ .id = next_id,
+ .comment = std::move(comment),
+ });
- next_id.id += 1;
+ // Execute group specific code.
+ func(entry_res, next_id);
+ next_id.id++;
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
- diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+ diag->Error(DiagMessage(item_source) << ":" << element_name << ">");
error = true;
}
}
return !error;
}
+bool ResourceParser::ParseStagingPublicGroup(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseGroupImpl(parser, out_resource, kStagingPublicGroupTag, diag_,
+ [](ParsedResource& parsed_entry, ResourceId id) {
+ parsed_entry.id = id;
+ parsed_entry.staged_api = true;
+ parsed_entry.visibility_level = Visibility::Level::kPublic;
+ });
+}
+
+bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ if (options_.visibility) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << kPublicGroupTag << "> tag not allowed with --visibility flag");
+ return false;
+ }
+
+ return ParseGroupImpl(parser, out_resource, kPublicGroupTag, diag_,
+ [](ParsedResource& parsed_entry, ResourceId id) {
+ parsed_entry.id = id;
+ parsed_entry.visibility_level = Visibility::Level::kPublic;
+ });
+}
+
bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 9d3ecc8..af0db8c 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -99,6 +99,7 @@
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 53bfa0b..4a509be 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -840,6 +840,31 @@
EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01010041)));
}
+TEST_F(ResourceParserTest, StagingPublicGroup) {
+ std::string input = R"(
+ <staging-public-group type="attr" first-id="0x01ff0049">
+ <public name="foo" />
+ <public name="bar" />
+ </staging-public-group>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ ASSERT_TRUE(result);
+
+ ASSERT_TRUE(result.value().entry->id);
+ EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01ff0049)));
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(result.value().entry->visibility.staged_api);
+
+ result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
+ ASSERT_TRUE(result);
+
+ ASSERT_TRUE(result.value().entry->id);
+ EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01ff004a)));
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(result.value().entry->visibility.staged_api);
+}
+
TEST_F(ResourceParserTest, StrongestSymbolVisibilityWins) {
std::string input = R"(
<!-- private -->
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index cff9872..27f7bdd 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -47,11 +47,6 @@
}
template <typename T>
-bool less_than_type_and_id(const T& lhs, const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
- return lhs.id != rhs.second ? lhs.id < rhs.second : lhs.type < rhs.first;
-}
-
-template <typename T>
bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
@@ -80,12 +75,6 @@
return lhs.name.compare(0, lhs.name.size(), rhs.first.data(), rhs.first.size()) < 0;
}
-template <typename T, typename U>
-bool less_than_struct_with_name_and_id_pointer(const T* lhs,
- const std::pair<std::string_view, Maybe<U>>& rhs) {
- return less_than_struct_with_name_and_id(*lhs, rhs);
-}
-
template <typename T, typename Func, typename Elements>
T* FindElementsRunAction(const android::StringPiece& name, Elements& entries, Func action) {
const auto iter =
@@ -307,51 +296,115 @@
return CollisionResult::kConflict;
}
+template <typename T, typename Comparer>
+struct SortedVectorInserter : public Comparer {
+ std::pair<bool, typename std::vector<T>::iterator> LowerBound(std::vector<T>& el,
+ const T& value) {
+ auto it = std::lower_bound(el.begin(), el.end(), value, [&](auto& lhs, auto& rhs) {
+ return Comparer::operator()(lhs, rhs);
+ });
+ bool found =
+ it != el.end() && !Comparer::operator()(*it, value) && !Comparer::operator()(value, *it);
+ return std::make_pair(found, it);
+ }
+
+ T* Insert(std::vector<T>& el, T&& value) {
+ auto [found, it] = LowerBound(el, value);
+ if (found) {
+ return &*it;
+ }
+ return &*el.insert(it, std::move(value));
+ }
+};
+
+struct PackageViewComparer {
+ bool operator()(const ResourceTablePackageView& lhs, const ResourceTablePackageView& rhs) {
+ return less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>(
+ lhs, std::make_pair(rhs.name, rhs.id));
+ }
+};
+
+struct TypeViewComparer {
+ bool operator()(const ResourceTableTypeView& lhs, const ResourceTableTypeView& rhs) {
+ return lhs.id != rhs.id ? lhs.id < rhs.id : lhs.type < rhs.type;
+ }
+};
+
+struct EntryViewComparer {
+ bool operator()(const ResourceEntry* lhs, const ResourceEntry* rhs) {
+ return less_than_struct_with_name_and_id<ResourceEntry, ResourceId>(
+ *lhs, std::make_pair(rhs->name, rhs->id));
+ }
+};
+
ResourceTableView ResourceTable::GetPartitionedView() const {
ResourceTableView view;
+ SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
+ SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
+ SortedVectorInserter<const ResourceEntry*, EntryViewComparer> entry_inserter;
+
for (const auto& package : packages) {
for (const auto& type : package->types) {
for (const auto& entry : type->entries) {
- std::pair<std::string_view, Maybe<uint8_t>> package_key(package->name, {});
- std::pair<std::string_view, Maybe<ResourceId>> entry_key(entry->name, {});
- std::pair<ResourceType, Maybe<uint8_t>> type_key(type->type, {});
- if (entry->id) {
- // If the entry has a defined id, use the id to determine insertion position.
- package_key.second = entry->id.value().package_id();
- type_key.second = entry->id.value().type_id();
- entry_key.second = entry->id.value();
- }
+ ResourceTablePackageView new_package{
+ package->name, entry->id ? entry->id.value().package_id() : Maybe<uint8_t>{}};
+ auto view_package = package_inserter.Insert(view.packages, std::move(new_package));
- auto package_it =
- std::lower_bound(view.packages.begin(), view.packages.end(), package_key,
- less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>);
- if (package_it == view.packages.end() || package_it->name != package_key.first ||
- package_it->id != package_key.second) {
- ResourceTablePackageView new_package{std::string(package_key.first), package_key.second};
- package_it = view.packages.insert(package_it, new_package);
- }
-
- auto type_it = std::lower_bound(package_it->types.begin(), package_it->types.end(),
- type_key, less_than_type_and_id<ResourceTableTypeView>);
- if (type_it == package_it->types.end() || type_key.first != type_it->type ||
- type_it->id != type_key.second) {
- ResourceTableTypeView new_type{type_key.first, type_key.second};
- type_it = package_it->types.insert(type_it, new_type);
- }
+ ResourceTableTypeView new_type{type->type,
+ entry->id ? entry->id.value().type_id() : Maybe<uint8_t>{}};
+ auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
if (entry->visibility.level == Visibility::Level::kPublic) {
// Only mark the type visibility level as public, it doesn't care about being private.
- type_it->visibility_level = Visibility::Level::kPublic;
+ view_type->visibility_level = Visibility::Level::kPublic;
}
- auto entry_it =
- std::lower_bound(type_it->entries.begin(), type_it->entries.end(), entry_key,
- less_than_struct_with_name_and_id_pointer<ResourceEntry, ResourceId>);
- type_it->entries.insert(entry_it, entry.get());
+ entry_inserter.Insert(view_type->entries, entry.get());
}
}
}
+ // The android runtime does not support querying resources when the there are multiple type ids
+ // for the same resource type within the same package. For this reason, if there are types with
+ // multiple type ids, each type needs to exist in its own package in order to be queried by name.
+ std::vector<ResourceTablePackageView> new_packages;
+ for (auto& package : view.packages) {
+ // If a new package was already created for a different type within this package, then
+ // we can reuse those packages for other types that need to be extracted from this package.
+ // `start_index` is the index of the first newly created package that can be reused.
+ const size_t start_index = new_packages.size();
+ std::map<ResourceType, size_t> type_new_package_index;
+ for (auto type_it = package.types.begin(); type_it != package.types.end();) {
+ auto& type = *type_it;
+ auto type_index_iter = type_new_package_index.find(type.type);
+ if (type_index_iter == type_new_package_index.end()) {
+ // First occurrence of the resource type in this package. Keep it in this package.
+ type_new_package_index.insert(type_index_iter, std::make_pair(type.type, start_index));
+ ++type_it;
+ continue;
+ }
+
+ // The resource type has already been seen for this package, so this type must be extracted to
+ // a new separate package.
+ const size_t index = type_index_iter->second;
+ if (new_packages.size() == index) {
+ new_packages.emplace_back(ResourceTablePackageView{package.name, package.id});
+ type_new_package_index[type.type] = index + 1;
+ }
+
+ // Move the type into a new package
+ auto& other_package = new_packages[index];
+ type_inserter.Insert(other_package.types, std::move(type));
+ type_it = package.types.erase(type_it);
+ }
+ }
+
+ for (auto& new_package : new_packages) {
+ // Insert newly created packages after their original packages
+ auto [_, it] = package_inserter.LowerBound(view.packages, new_package);
+ view.packages.insert(++it, std::move(new_package));
+ }
+
return view;
}
@@ -424,6 +477,10 @@
// This symbol definition takes precedence, replace.
entry->visibility = res.visibility.value();
}
+
+ if (res.visibility->staged_api) {
+ entry->visibility.staged_api = entry->visibility.staged_api;
+ }
}
if (res.overlayable.has_value()) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 49392a5..080ecc2 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -51,6 +51,11 @@
Level level = Level::kUndefined;
Source source;
std::string comment;
+
+ // Indicates that the resource id may change across builds and that the public R.java identifier
+ // for this resource should not be final. This is set to `true` for resources in `staging-group`
+ // tags.
+ bool staged_api = false;
};
// Represents <add-resource> in an overlay.
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index b1e1a77..4247ec5 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -132,6 +132,11 @@
// The comment associated with the <public> tag.
string comment = 3;
+
+ // Indicates that the resource id may change across builds and that the public R.java identifier
+ // for this resource should not be final. This is set to `true` for resources in `staging-group`
+ // tags.
+ bool staged_api = 4;
}
// Whether a resource comes from a compile-time overlay and is explicitly allowed to not overlay an
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 27cbe88..dfdac6b 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -25,6 +25,7 @@
using testing::Eq;
using testing::HasSubstr;
using testing::Ne;
+using testing::NotNull;
namespace aapt {
@@ -400,4 +401,127 @@
EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
}
+TEST_F(LinkTest, StagedAndroidApi) {
+ StdErrDiagnostics diag;
+ const std::string android_values =
+ R"(<resources>
+ <public type="attr" name="finalized_res" id="0x01010001"/>
+
+ <!-- S staged attributes (support staged resources in the same type id) -->
+ <staging-public-group type="attr" first-id="0x01010050">
+ <public name="staged_s_res" />
+ </staging-public-group>
+
+ <!-- SV2 staged attributes (support staged resources in a separate type id) -->
+ <staging-public-group type="attr" first-id="0x01ff0049">
+ <public name="staged_s2_res" />
+ </staging-public-group>
+
+ <!-- T staged attributes (support staged resources in multiple separate type ids) -->
+ <staging-public-group type="attr" first-id="0x01fe0063">
+ <public name="staged_t_res" />
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x01fd0072">
+ <public name="staged_t_string" />
+ </staging-public-group>
+
+ <attr name="finalized_res" />
+ <attr name="staged_s_res" />
+ <attr name="staged_s2_res" />
+ <attr name="staged_t_res" />
+ <string name="staged_t_string">Hello</string>
+ </resources>)";
+
+ const std::string app_values =
+ R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <attr name="bar" />
+ <declare-styleable name="ClientStyleable">
+ <attr name="android:finalized_res" />
+ <attr name="android:staged_s_res" />
+ <attr name="bar" />
+ </declare-styleable>
+ </resources>)";
+
+ const std::string android_res = GetTestPath("android-res");
+ ASSERT_TRUE(
+ CompileFile(GetTestPath("res/values/values.xml"), android_values, android_res, &diag));
+
+ const std::string android_apk = GetTestPath("android.apk");
+ const std::string android_java = GetTestPath("android_java");
+ // clang-format off
+ auto android_manifest = ManifestBuilder(this)
+ .SetPackageName("android")
+ .Build();
+
+ auto android_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(android_manifest)
+ .AddParameter("--private-symbols", "com.android.internal")
+ .AddParameter("--java", android_java)
+ .AddCompiledResDir(android_res, &diag)
+ .Build(android_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(android_link_args, &diag));
+
+ const std::string android_r_java = android_java + "/android/R.java";
+ std::string android_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static final int finalized_res=0x01010001;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_s_res=0x01010050;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_s2_res=0x01ff0049;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_t_res=0x01fe0063;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_t_string=0x01fd0072;"));
+
+ // Build an app that uses the framework attribute in a declare-styleable
+ const std::string client_res = GetTestPath("app-res");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), app_values, client_res, &diag));
+
+ const std::string app_apk = GetTestPath("app.apk");
+ const std::string app_java = GetTestPath("app_java");
+ // clang-format off
+ auto app_manifest = ManifestBuilder(this)
+ .SetPackageName("com.example.app")
+ .Build();
+
+ auto app_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(app_manifest)
+ .AddParameter("--java", app_java)
+ .AddParameter("-I", android_apk)
+ .AddCompiledResDir(client_res, &diag)
+ .Build(app_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(app_link_args, &diag));
+
+ const std::string client_r_java = app_java + "/com/example/app/R.java";
+ std::string client_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
+ EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
+
+ // Test that the resource ids of staged and non-staged resource can be retrieved
+ android::AssetManager2 am;
+ auto android_asset = android::ApkAssets::Load(android_apk);
+ ASSERT_THAT(android_asset, NotNull());
+ ASSERT_TRUE(am.SetApkAssets({android_asset.get()}));
+
+ auto result = am.GetResourceId("android:attr/finalized_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010001));
+
+ result = am.GetResourceId("android:attr/staged_s_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010050));
+
+ result = am.GetResourceId("android:attr/staged_s2_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01ff0049));
+
+ result = am.GetResourceId("android:attr/staged_t_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01fe0063));
+
+ result = am.GetResourceId("android:string/staged_t_string");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01fd0072));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 07db73d..9a50b26 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -77,6 +77,27 @@
NextIdFinder<uint16_t, ResourceName> next_entry_id_;
};
+struct ResourceTypeKey {
+ ResourceType type;
+ uint8_t id;
+
+ bool operator<(const ResourceTypeKey& other) const {
+ return (type != other.type) ? type < other.type : id < other.id;
+ }
+
+ bool operator==(const ResourceTypeKey& other) const {
+ return type == other.type && id == other.id;
+ }
+
+ bool operator!=(const ResourceTypeKey& other) const {
+ return !(*this == other);
+ }
+};
+
+::std::ostream& operator<<(::std::ostream& out, const ResourceTypeKey& type) {
+ return out << type.type;
+}
+
struct IdAssignerContext {
IdAssignerContext(std::string package_name, uint8_t package_id)
: package_name_(std::move(package_name)), package_id_(package_id) {
@@ -85,7 +106,8 @@
// Attempts to reserve the resource id for the specified resource name.
// Returns whether the id was reserved successfully.
// Reserving identifiers must be completed before `NextId` is called for the first time.
- bool ReserveId(const ResourceName& name, ResourceId id, IDiagnostics* diag);
+ bool ReserveId(const ResourceName& name, ResourceId id, const Visibility& visibility,
+ IDiagnostics* diag);
// Retrieves the next available resource id that has not been reserved.
std::optional<ResourceId> NextId(const ResourceName& name, IDiagnostics* diag);
@@ -93,8 +115,10 @@
private:
std::string package_name_;
uint8_t package_id_;
- std::map<ResourceType, TypeGroup> types_;
- NextIdFinder<uint8_t, ResourceType> type_id_finder_ = NextIdFinder<uint8_t, ResourceType>(1);
+ std::map<ResourceTypeKey, TypeGroup> types_;
+ std::map<ResourceType, uint8_t> non_staged_type_ids_;
+ NextIdFinder<uint8_t, ResourceTypeKey> type_id_finder_ =
+ NextIdFinder<uint8_t, ResourceTypeKey>(1);
};
} // namespace
@@ -106,7 +130,8 @@
for (auto& entry : type->entries) {
const ResourceName name(package->name, type->type, entry->name);
if (entry->id) {
- if (!assigned_ids.ReserveId(name, entry->id.value(), context->GetDiagnostics())) {
+ if (!assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
+ context->GetDiagnostics())) {
return false;
}
}
@@ -116,7 +141,8 @@
const auto iter = assigned_id_map_->find(name);
if (iter != assigned_id_map_->end()) {
const ResourceId assigned_id = iter->second;
- if (!assigned_ids.ReserveId(name, assigned_id, context->GetDiagnostics())) {
+ if (!assigned_ids.ReserveId(name, assigned_id, entry->visibility,
+ context->GetDiagnostics())) {
return false;
}
entry->id = assigned_id;
@@ -132,7 +158,8 @@
for (const auto& stable_id_entry : *assigned_id_map_) {
const ResourceName& pre_assigned_name = stable_id_entry.first;
const ResourceId& pre_assigned_id = stable_id_entry.second;
- if (!assigned_ids.ReserveId(pre_assigned_name, pre_assigned_id, context->GetDiagnostics())) {
+ if (!assigned_ids.ReserveId(pre_assigned_name, pre_assigned_id, {},
+ context->GetDiagnostics())) {
return false;
}
}
@@ -165,7 +192,7 @@
auto assign_result = pre_assigned_ids_.emplace(id, key);
if (!assign_result.second && assign_result.first->second != key) {
std::stringstream error;
- error << "ID " << id << " is already assigned to " << assign_result.first->second;
+ error << "ID is already assigned to " << assign_result.first->second;
return unexpected(error.str());
}
return id;
@@ -210,7 +237,7 @@
if (type_id_ != id.type_id()) {
// Currently there cannot be multiple type ids for a single type.
std::stringstream error;
- error << "type '" << name.type << "' already has ID " << id.type_id();
+ error << "type '" << name.type << "' already has ID " << std::hex << (int)id.type_id();
return unexpected(error.str());
}
@@ -234,24 +261,38 @@
return ResourceId(package_id_, type_id_, entry_id.value());
}
-bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id, IDiagnostics* diag) {
+bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id,
+ const Visibility& visibility, IDiagnostics* diag) {
if (package_id_ != id.package_id()) {
diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
- << " because package already has ID " << id.package_id());
+ << " because package already has ID " << std::hex
+ << (int)id.package_id());
return false;
}
- auto type = types_.find(name.type);
+ auto key = ResourceTypeKey{name.type, id.type_id()};
+ auto type = types_.find(key);
if (type == types_.end()) {
// The type has not been assigned an id yet. Ensure that the specified id is not being used by
// another type.
- auto assign_result = type_id_finder_.ReserveId(name.type, id.type_id());
+ auto assign_result = type_id_finder_.ReserveId(key, id.type_id());
if (!assign_result.has_value()) {
diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
<< " because type " << assign_result.error());
return false;
}
- type = types_.emplace(name.type, TypeGroup(package_id_, id.type_id())).first;
+ type = types_.emplace(key, TypeGroup(package_id_, id.type_id())).first;
+ }
+
+ if (!visibility.staged_api) {
+ // Ensure that non-staged resources can only exist in one type ID.
+ auto non_staged_type = non_staged_type_ids_.emplace(name.type, id.type_id());
+ if (!non_staged_type.second && non_staged_type.first->second != id.type_id()) {
+ diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
+ << " because type already has ID " << std::hex
+ << (int)id.type_id());
+ return false;
+ }
}
auto assign_result = type->second.ReserveId(name, id);
@@ -268,11 +309,19 @@
// The package name is not known during the compile stage.
// Resources without a package name are considered a part of the app being linked.
CHECK(name.package.empty() || name.package == package_name_);
- auto type = types_.find(name.type);
- if (type == types_.end()) {
+
+ // Find the type id for non-staged resources of this type.
+ auto non_staged_type = non_staged_type_ids_.find(name.type);
+ if (non_staged_type == non_staged_type_ids_.end()) {
auto next_type_id = type_id_finder_.NextId();
CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
- type = types_.emplace(name.type, TypeGroup(package_id_, next_type_id.value())).first;
+ non_staged_type = non_staged_type_ids_.emplace(name.type, *next_type_id).first;
+ }
+
+ ResourceTypeKey key{name.type, non_staged_type->second};
+ auto type = types_.find(key);
+ if (type == types_.end()) {
+ type = types_.emplace(key, TypeGroup(package_id_, key.id)).first;
}
auto assign_result = type->second.NextId();
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index 0065a22..6637766 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -98,6 +98,37 @@
ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
}
+TEST_F(IdAssignerTests, FailWhenNonUniqueTypeIdsAssigned) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:string/foo", ResourceId(0x01040000))
+ .AddSimple("android:attr/bar", ResourceId(0x01040006))
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIds) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01050000))
+ .AddSimple("android:attr/bar", ResourceId(0x01040006))
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIdsRegardlessOfStagedId) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01050000))
+ .AddSimple("android:attr/bar", ResourceId(0x01ff0006))
+ .Add(NewResourceBuilder("android:attr/staged_baz")
+ .SetId(0x01ff0000)
+ .SetVisibility({.staged_api = true})
+ .Build())
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
TEST_F(IdAssignerTests, AssignIdsWithIdMap) {
auto table = test::ResourceTableBuilder()
.AddSimple("android:attr/foo")
@@ -154,52 +185,24 @@
}
::testing::AssertionResult VerifyIds(ResourceTable* table) {
- std::set<uint8_t> package_ids;
- auto table_view = table->GetPartitionedView();
- for (auto& package : table_view.packages) {
- if (!package.id) {
- return ::testing::AssertionFailure() << "package " << package.name << " has no ID";
- }
-
- if (!package_ids.insert(package.id.value()).second) {
- return ::testing::AssertionFailure() << "package " << package.name << " has non-unique ID "
- << std::hex << (int)package.id.value() << std::dec;
- }
- }
-
- for (auto& package : table_view.packages) {
- std::set<uint8_t> type_ids;
- for (auto& type : package.types) {
- if (!type.id) {
- return ::testing::AssertionFailure()
- << "type " << type.type << " of package " << package.name << " has no ID";
- }
-
- if (!type_ids.insert(type.id.value()).second) {
- return ::testing::AssertionFailure()
- << "type " << type.type << " of package " << package.name << " has non-unique ID "
- << std::hex << (int)type.id.value() << std::dec;
- }
- }
-
- for (auto& type : package.types) {
- std::set<ResourceId> entry_ids;
- for (auto& entry : type.entries) {
+ std::set<ResourceId> seen_ids;
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
if (!entry->id) {
return ::testing::AssertionFailure()
- << "entry " << entry->name << " of type " << type.type << " of package "
- << package.name << " has no ID";
+ << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+ << " has no ID";
}
-
- if (!entry_ids.insert(entry->id.value()).second) {
+ if (!seen_ids.insert(entry->id.value()).second) {
return ::testing::AssertionFailure()
- << "entry " << entry->name << " of type " << type.type << " of package "
- << package.name << " has non-unique ID " << std::hex << entry->id.value()
- << std::dec;
+ << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+ << " has a non-unique ID" << std::hex << entry->id.value() << std::dec;
}
}
}
}
+
return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 5fea897..17d11a6 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -546,8 +546,12 @@
const uint16_t entry_id = entry->id.value().entry_id();
// Populate the config masks for this entry.
+ uint32_t& entry_config_masks = config_masks[entry_id];
if (entry->visibility.level == Visibility::Level::kPublic) {
- config_masks[entry_id] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ }
+ if (entry->visibility.staged_api) {
+ entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_STAGED_API);
}
const size_t config_count = entry->values.size();
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index bfb92da..498d5a2 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -456,6 +456,7 @@
DeserializeSourceFromPb(pb_visibility.source(), src_pool, &entry->visibility.source);
}
entry->visibility.comment = pb_visibility.comment();
+ entry->visibility.staged_api = pb_visibility.staged_api();
const Visibility::Level level = DeserializeVisibilityFromPb(pb_visibility.level());
entry->visibility.level = level;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 9842e25..f13f82d 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -378,6 +378,7 @@
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
+ pb_visibility->set_staged_api(entry->visibility.staged_api);
pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
if (source_pool != nullptr) {
SerializeSourceToPb(entry->visibility.source, source_pool.get(),
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index ad5ed4d..591ba149 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -50,6 +50,53 @@
return (result) ? result.value().entry : nullptr;
}
+TEST(ProtoSerializeTest, SerializeVisibility) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .Add(NewResourceBuilder("com.app.a:bool/foo")
+ .SetVisibility({Visibility::Level::kUndefined})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/bar")
+ .SetVisibility({Visibility::Level::kPrivate})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/baz")
+ .SetVisibility({Visibility::Level::kPublic})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/fiz")
+ .SetVisibility({.level = Visibility::Level::kPublic, .staged_api = true})
+ .Build())
+ .Build();
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ auto search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPrivate));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(search_result.value().entry->visibility.staged_api);
+}
+
TEST(ProtoSerializeTest, SerializeSinglePackage) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 995495a..d3648c8 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -59,8 +59,9 @@
template <typename T>
class PrimitiveMember : public ClassMember {
public:
- PrimitiveMember(const android::StringPiece& name, const T& val)
- : name_(name.to_string()), val_(val) {}
+ PrimitiveMember(const android::StringPiece& name, const T& val, bool staged_api = false)
+ : name_(name.to_string()), val_(val), staged_api_(staged_api) {
+ }
bool empty() const override {
return false;
@@ -77,7 +78,7 @@
ClassMember::Print(final, printer, strip_api_annotations);
printer->Print("public static ");
- if (final) {
+ if (final && !staged_api_) {
printer->Print("final ");
}
printer->Print("int ").Print(name_).Print("=").Print(to_string(val_)).Print(";");
@@ -88,14 +89,16 @@
std::string name_;
T val_;
+ bool staged_api_;
};
// Specialization for strings so they get the right type and are quoted with "".
template <>
class PrimitiveMember<std::string> : public ClassMember {
public:
- PrimitiveMember(const android::StringPiece& name, const std::string& val)
- : name_(name.to_string()), val_(val) {}
+ PrimitiveMember(const android::StringPiece& name, const std::string& val, bool staged_api = false)
+ : name_(name.to_string()), val_(val) {
+ }
bool empty() const override {
return false;
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 039448e..e1e2e01 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -460,7 +460,8 @@
const std::string field_name = TransformToFieldName(name.entry);
if (out_class_def != nullptr) {
- auto resource_member = util::make_unique<ResourceMember>(field_name, real_id);
+ auto resource_member =
+ util::make_unique<ResourceMember>(field_name, real_id, entry.visibility.staged_api);
// Build the comments and annotations for this entry.
AnnotationProcessor* processor = resource_member->GetCommentBuilder();
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index de72334..d385267 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -199,7 +199,8 @@
if (sr.entry->id) {
symbol->id = sr.entry->id.value();
- symbol->is_dynamic = (sr.entry->id.value().package_id() == 0);
+ symbol->is_dynamic =
+ (sr.entry->id.value().package_id() == 0) || sr.entry->visibility.staged_api;
}
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
@@ -374,7 +375,8 @@
if (s) {
s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
+ s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package) ||
+ (type_spec_flags & android::ResTable_typeSpec::SPEC_STAGED_API) != 0;
return s;
}
return {};
@@ -421,7 +423,8 @@
if (s) {
s->is_public = (*flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
+ s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package) ||
+ (*flags & android::ResTable_typeSpec::SPEC_STAGED_API) != 0;
return s;
}
return {};