Merge "Add IColorDisplayManager"
diff --git a/Android.bp b/Android.bp
index 4bfb30c..c568d36 100644
--- a/Android.bp
+++ b/Android.bp
@@ -158,9 +158,9 @@
"core/java/android/hardware/IConsumerIrService.aidl",
"core/java/android/hardware/ISerialManager.aidl",
"core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl",
- "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricService.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
+ "core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
"core/java/android/hardware/display/IColorDisplayManager.aidl",
"core/java/android/hardware/display/IDisplayManager.aidl",
@@ -291,7 +291,6 @@
"core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
"core/java/android/service/gatekeeper/IGateKeeperService.aidl",
"core/java/android/service/intelligence/IIntelligenceService.aidl",
-
"core/java/android/service/notification/INotificationListener.aidl",
"core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
"core/java/android/service/notification/IConditionListener.aidl",
@@ -574,6 +573,7 @@
"telephony/java/com/android/internal/telephony/IApnSourceService.aidl",
"telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl",
"telephony/java/com/android/internal/telephony/IMms.aidl",
+ "telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl",
"telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
@@ -654,6 +654,8 @@
"core/java/com/android/server/DropboxLogTags.logtags",
"core/java/org/chromium/arc/EventLogTags.logtags",
+ ":platform-properties",
+
":framework-statslog-gen",
],
@@ -732,8 +734,10 @@
"netd_aidl_interface-java",
],
- // Loaded with System.loadLibrary by android.view.textclassifier
required: [
+ // TODO: remove gps_debug when the build system propagates "required" properly.
+ "gps_debug.conf",
+ // Loaded with System.loadLibrary by android.view.textclassifier
"libmedia2_jni",
],
@@ -781,9 +785,11 @@
java_library_host {
name: "inspector-annotation",
srcs: [
- "core/java/android/view/inspector/InspectableChildren.java",
"core/java/android/view/inspector/InspectableNodeName.java",
"core/java/android/view/inspector/InspectableProperty.java",
+ // Needed for the ResourceId.ID_NULL constant
+ "core/java/android/content/res/ResourceId.java",
+ "core/java/android/annotation/AnyRes.java",
],
}
@@ -1721,7 +1727,9 @@
"core/java/android/annotation/RequiresPermission.java",
"core/java/android/annotation/SdkConstant.java",
"core/java/android/annotation/StringDef.java",
+ "core/java/android/annotation/TestApi.java",
"core/java/android/annotation/UnsupportedAppUsage.java",
+ "core/java/com/android/internal/annotations/GuardedBy.java",
],
}
diff --git a/Android.mk b/Android.mk
index 770ec20..92e33e9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -73,55 +73,35 @@
( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
# ==== hiddenapi lists =======================================
-.KATI_RESTAT: \
- $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
- .KATI_IMPLICIT_OUTPUTS := \
- $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
- $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
+.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
+$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- frameworks/base/config/hiddenapi-light-greylist.txt \
- frameworks/base/config/hiddenapi-vendor-list.txt \
- frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
+ frameworks/base/config/hiddenapi-greylist.txt \
+ frameworks/base/config/hiddenapi-greylist-max-p.txt \
+ frameworks/base/config/hiddenapi-greylist-max-o.txt \
frameworks/base/config/hiddenapi-force-blacklist.txt \
$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
$(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- --input-public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
- --input-private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
- --input-whitelists $(PRIVATE_WHITELIST_INPUTS) \
- --input-greylists \
- frameworks/base/config/hiddenapi-light-greylist.txt \
- frameworks/base/config/hiddenapi-vendor-list.txt \
- frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
- <(comm -12 <(sort $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)) \
- $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)) \
- $(PRIVATE_GREYLIST_INPUTS) \
- --input-blacklists frameworks/base/config/hiddenapi-force-blacklist.txt \
- --output-whitelist $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST).tmp \
- --output-light-greylist $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST).tmp \
- --output-dark-greylist $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST).tmp \
- --output-blacklist $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST).tmp
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
- $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+ --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
+ --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
+ --csv $(PRIVATE_FLAGS_INPUTS) \
+ --greylist frameworks/base/config/hiddenapi-greylist.txt \
+ --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
+ --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
+ --greylist-max-o-ignore-conflicts \
+ frameworks/base/config/hiddenapi-greylist-max-o.txt \
+ --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \
+ --output $@.tmp
+ $(call commit-change-for-toc,$@)
$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
frameworks/base/tools/hiddenapi/merge_csv.py \
$(PRIVATE_METADATA_INPUTS)
frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
# Include subdirectory makefiles
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6deda0c..478e4fe 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -248,6 +248,9 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.future.usb.accessory.jar)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.media.remotedisplay.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5936ee4..e731138 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,7 +5,9 @@
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
-hidden_api_txt_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+
+hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
diff --git a/api/current.txt b/api/current.txt
index 14214cb..2eb90d5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -150,6 +150,7 @@
field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final java.lang.String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC";
field public static final deprecated java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
+ field public static final java.lang.String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT";
field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
@@ -480,6 +481,10 @@
field public static final int dashGap = 16843175; // 0x10101a7
field public static final int dashWidth = 16843174; // 0x10101a6
field public static final int data = 16842798; // 0x101002e
+ field public static final int dataRetentionTime = 16844189; // 0x101059d
+ field public static final int dataSentOffDevice = 16844186; // 0x101059a
+ field public static final int dataSharedWithThirdParty = 16844187; // 0x101059b
+ field public static final int dataUsedForMonetization = 16844188; // 0x101059c
field public static final int datePickerDialogTheme = 16843948; // 0x10104ac
field public static final int datePickerMode = 16843955; // 0x10104b3
field public static final int datePickerStyle = 16843612; // 0x101035c
@@ -1197,6 +1202,7 @@
field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
field public static final deprecated int selectedDateVerticalBar = 16843591; // 0x1010347
field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
+ field public static final int selectionDividerHeight = 16844190; // 0x101059e
field public static final int sessionService = 16843837; // 0x101043d
field public static final int settingsActivity = 16843301; // 0x1010225
field public static final int settingsSliceUri = 16844179; // 0x1010593
@@ -1312,7 +1318,6 @@
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
- field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
@@ -1496,7 +1501,9 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
+ field public static final int usageInfoRequired = 16844185; // 0x1010599
field public static final int use32bitAbi = 16844053; // 0x1010515
+ field public static final int useAppZygote = 16844184; // 0x1010598
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
@@ -3802,6 +3809,7 @@
method public void overridePendingTransition(int, int);
method public void postponeEnterTransition();
method public void recreate();
+ method public void registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
method public void registerForContextMenu(android.view.View);
method public boolean releaseInstance();
method public final deprecated void removeDialog(int);
@@ -3879,6 +3887,7 @@
method public deprecated void stopManagingCursor(android.database.Cursor);
method public void takeKeyEvents(boolean);
method public void triggerSearch(java.lang.String, android.os.Bundle);
+ method public void unregisterActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
method public void unregisterForContextMenu(android.view.View);
field public static final int DEFAULT_KEYS_DIALER = 1; // 0x1
field public static final int DEFAULT_KEYS_DISABLE = 0; // 0x0
@@ -6351,7 +6360,6 @@
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
- method public boolean supportsAmbientMode();
method public boolean supportsMultipleDisplays();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
@@ -6510,6 +6518,7 @@
}
public class DevicePolicyManager {
+ method public void addCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String);
method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public int addOverrideApn(android.content.ComponentName, android.telephony.data.ApnSetting);
@@ -6539,6 +6548,7 @@
method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
method public boolean getCameraDisabled(android.content.ComponentName);
method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+ method public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(android.content.ComponentName);
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
@@ -6627,6 +6637,7 @@
method public int logoutUser(android.content.ComponentName);
method public void reboot(android.content.ComponentName);
method public void removeActiveAdmin(android.content.ComponentName);
+ method public boolean removeCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
method public boolean removeOverrideApn(android.content.ComponentName, int);
@@ -7681,6 +7692,7 @@
method protected void prepareView(android.view.View);
method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
method public void setExecutor(java.util.concurrent.Executor);
+ method public void setOnLightBackground(boolean);
method public void updateAppWidget(android.widget.RemoteViews);
method public void updateAppWidgetOptions(android.os.Bundle);
method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
@@ -9547,6 +9559,7 @@
method public abstract java.io.File getNoBackupFilesDir();
method public abstract java.io.File getObbDir();
method public abstract java.io.File[] getObbDirs();
+ method public abstract java.lang.String getOpPackageName();
method public abstract java.lang.String getPackageCodePath();
method public abstract android.content.pm.PackageManager getPackageManager();
method public abstract java.lang.String getPackageName();
@@ -9757,6 +9770,7 @@
method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
+ method public java.lang.String getOpPackageName();
method public java.lang.String getPackageCodePath();
method public android.content.pm.PackageManager getPackageManager();
method public java.lang.String getPackageName();
@@ -11167,8 +11181,8 @@
field public android.content.pm.ProviderInfo[] providers;
field public android.content.pm.ActivityInfo[] receivers;
field public android.content.pm.FeatureInfo[] reqFeatures;
- field public java.lang.String[] requestedPermissions;
- field public int[] requestedPermissionsFlags;
+ field public deprecated java.lang.String[] requestedPermissions;
+ field public deprecated int[] requestedPermissionsFlags;
field public android.content.pm.ServiceInfo[] services;
field public java.lang.String sharedUserId;
field public int sharedUserLabel;
@@ -11176,6 +11190,7 @@
field public android.content.pm.SigningInfo signingInfo;
field public java.lang.String[] splitNames;
field public int[] splitRevisionCodes;
+ field public android.content.pm.UsesPermissionInfo[] usesPermissions;
field public deprecated int versionCode;
field public java.lang.String versionName;
}
@@ -11326,7 +11341,7 @@
method public abstract int checkSignatures(java.lang.String, java.lang.String);
method public abstract int checkSignatures(int, int);
method public abstract void clearInstantAppCookie();
- method public abstract void clearPackagePreferredActivities(java.lang.String);
+ method public abstract deprecated void clearPackagePreferredActivities(java.lang.String);
method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
method public abstract void extendVerificationTimeout(int, int, long);
method public abstract android.graphics.drawable.Drawable getActivityBanner(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11370,8 +11385,8 @@
method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
- method public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
+ method public abstract deprecated int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
+ method public abstract deprecated java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
method public abstract android.content.pm.ProviderInfo getProviderInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.ActivityInfo getReceiverInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.res.Resources getResourcesForActivity(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11651,6 +11666,7 @@
field public java.lang.String group;
field public java.lang.CharSequence nonLocalizedDescription;
field public deprecated int protectionLevel;
+ field public boolean usageInfoRequired;
}
public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
@@ -11713,6 +11729,7 @@
field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
+ field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8
field public int flags;
field public java.lang.String permission;
}
@@ -11829,6 +11846,28 @@
field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
}
+ public final class UsesPermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getDataRetention();
+ method public int getDataRetentionWeeks();
+ method public int getDataSentOffDevice();
+ method public int getDataSharedWithThirdParty();
+ method public int getDataUsedForMonetization();
+ method public int getFlags();
+ method public java.lang.String getPermission();
+ field public static final android.os.Parcelable.Creator<android.content.pm.UsesPermissionInfo> CREATOR;
+ field public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+ field public static final int RETENTION_NOT_RETAINED = 1; // 0x1
+ field public static final int RETENTION_SPECIFIED = 4; // 0x4
+ field public static final int RETENTION_UNDEFINED = 0; // 0x0
+ field public static final int RETENTION_UNLIMITED = 3; // 0x3
+ field public static final int RETENTION_USER_SELECTED = 2; // 0x2
+ field public static final int USAGE_NO = 3; // 0x3
+ field public static final int USAGE_UNDEFINED = 0; // 0x0
+ field public static final int USAGE_USER_TRIGGERED = 2; // 0x2
+ field public static final int USAGE_YES = 1; // 0x1
+ }
+
public final class VersionedPackage implements android.os.Parcelable {
ctor public VersionedPackage(java.lang.String, int);
ctor public VersionedPackage(java.lang.String, long);
@@ -13690,6 +13729,8 @@
public abstract class ColorSpace {
method public static android.graphics.ColorSpace adapt(android.graphics.ColorSpace, float[]);
method public static android.graphics.ColorSpace adapt(android.graphics.ColorSpace, float[], android.graphics.ColorSpace.Adaptation);
+ method public static float[] cctToIlluminantdXyz(int);
+ method public static float[] chromaticAdaptation(android.graphics.ColorSpace.Adaptation, float[], float[]);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace);
@@ -13938,6 +13979,7 @@
}
public final class Insets {
+ method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets);
method public static android.graphics.Insets of(int, int, int, int);
method public static android.graphics.Insets of(android.graphics.Rect);
field public static final android.graphics.Insets NONE;
@@ -14690,6 +14732,7 @@
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public long getUniqueId();
method public int getWidth();
method public boolean hasDisplayList();
method public boolean hasIdentityMatrix();
@@ -14823,6 +14866,7 @@
ctor public Typeface.CustomFallbackBuilder(android.graphics.fonts.FontFamily);
method public android.graphics.Typeface.CustomFallbackBuilder addCustomFallback(android.graphics.fonts.FontFamily);
method public android.graphics.Typeface build();
+ method public static int getMaxCustomFallbackCount();
method public android.graphics.Typeface.CustomFallbackBuilder setStyle(android.graphics.fonts.FontStyle);
method public android.graphics.Typeface.CustomFallbackBuilder setSystemFallback(java.lang.String);
}
@@ -15042,6 +15086,7 @@
method public void invalidateSelf();
method public boolean isAutoMirrored();
method public boolean isFilterBitmap();
+ method public boolean isProjected();
method public boolean isStateful();
method public final boolean isVisible();
method public void jumpToCurrentState();
@@ -23458,6 +23503,7 @@
}
public class ExifInterface {
+ ctor public ExifInterface(java.io.File) throws java.io.IOException;
ctor public ExifInterface(java.lang.String) throws java.io.IOException;
ctor public ExifInterface(java.io.FileDescriptor) throws java.io.IOException;
ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
@@ -23465,11 +23511,13 @@
method public java.lang.String getAttribute(java.lang.String);
method public double getAttributeDouble(java.lang.String, double);
method public int getAttributeInt(java.lang.String, int);
+ method public long[] getAttributeRange(java.lang.String);
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
method public android.graphics.Bitmap getThumbnailBitmap();
method public byte[] getThumbnailBytes();
method public long[] getThumbnailRange();
+ method public boolean hasAttribute(java.lang.String);
method public boolean hasThumbnail();
method public boolean isThumbnailCompressed();
method public void saveAttributes() throws java.io.IOException;
@@ -24504,16 +24552,30 @@
public final class MediaFormat {
ctor public MediaFormat();
+ ctor public MediaFormat(android.media.MediaFormat);
+ method public boolean containsFeature(java.lang.String);
method public boolean containsKey(java.lang.String);
method public static android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
method public static android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
method public static android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
method public java.nio.ByteBuffer getByteBuffer(java.lang.String);
+ method public java.nio.ByteBuffer getByteBuffer(java.lang.String, java.nio.ByteBuffer);
method public boolean getFeatureEnabled(java.lang.String);
+ method public java.util.Set<java.lang.String> getFeatures();
method public float getFloat(java.lang.String);
+ method public float getFloat(java.lang.String, float);
method public int getInteger(java.lang.String);
+ method public int getInteger(java.lang.String, int);
+ method public java.util.Set<java.lang.String> getKeys();
method public long getLong(java.lang.String);
+ method public long getLong(java.lang.String, long);
+ method public java.lang.Number getNumber(java.lang.String);
+ method public java.lang.Number getNumber(java.lang.String, java.lang.Number);
method public java.lang.String getString(java.lang.String);
+ method public java.lang.String getString(java.lang.String, java.lang.String);
+ method public int getValueTypeForKey(java.lang.String);
+ method public void removeFeature(java.lang.String);
+ method public void removeKey(java.lang.String);
method public void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
method public void setFeatureEnabled(java.lang.String, boolean);
method public void setFloat(java.lang.String, float);
@@ -24618,6 +24680,12 @@
field public static final java.lang.String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
field public static final java.lang.String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+ field public static final int TYPE_BYTE_BUFFER = 5; // 0x5
+ field public static final int TYPE_FLOAT = 3; // 0x3
+ field public static final int TYPE_INTEGER = 1; // 0x1
+ field public static final int TYPE_LONG = 2; // 0x2
+ field public static final int TYPE_NULL = 0; // 0x0
+ field public static final int TYPE_STRING = 4; // 0x4
}
public final class MediaMetadata implements android.os.Parcelable {
@@ -29009,20 +29077,21 @@
public class WifiManager {
method public deprecated int addNetwork(android.net.wifi.WifiConfiguration);
- method public boolean addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+ method public int addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
method public static int calculateSignalLevel(int, int);
method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
method public static int compareSignalLevel(int, int);
method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
- method public android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
+ method public deprecated android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
method public deprecated boolean disableNetwork(int);
method public deprecated boolean disconnect();
method public deprecated boolean enableNetwork(int, boolean);
method public deprecated java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
method public android.net.wifi.WifiInfo getConnectionInfo();
method public android.net.DhcpInfo getDhcpInfo();
+ method public int getMaxNumberOfNetworkSuggestionsPerApp();
method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
method public int getWifiState();
@@ -29041,7 +29110,7 @@
method public deprecated boolean reassociate();
method public deprecated boolean reconnect();
method public deprecated boolean removeNetwork(int);
- method public boolean removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+ method public int removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
method public void removePasspointConfiguration(java.lang.String);
method public deprecated boolean saveConfiguration();
method public void setTdlsEnabled(java.net.InetAddress, boolean);
@@ -29070,11 +29139,17 @@
field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; // 0x2
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; // 0x3
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; // 0x4
+ field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0
field public static final deprecated java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE";
field public static final deprecated java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE";
- field public static final int WIFI_MODE_FULL = 1; // 0x1
+ field public static final deprecated int WIFI_MODE_FULL = 1; // 0x1
field public static final int WIFI_MODE_FULL_HIGH_PERF = 3; // 0x3
- field public static final int WIFI_MODE_SCAN_ONLY = 2; // 0x2
+ field public static final int WIFI_MODE_FULL_LOW_LATENCY = 4; // 0x4
+ field public static final deprecated int WIFI_MODE_SCAN_ONLY = 2; // 0x2
field public static final java.lang.String WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED";
field public static final int WIFI_STATE_DISABLED = 1; // 0x1
field public static final int WIFI_STATE_DISABLING = 0; // 0x0
@@ -29111,6 +29186,9 @@
method public void setReferenceCounted(boolean);
}
+ public static abstract class WifiManager.NetworkSuggestionsStatusCode implements java.lang.annotation.Annotation {
+ }
+
public class WifiManager.WifiLock {
method public void acquire();
method public boolean isHeld();
@@ -29402,11 +29480,24 @@
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pConfig> CREATOR;
+ field public static final int GROUP_OWNER_BAND_2GHZ = 1; // 0x1
+ field public static final int GROUP_OWNER_BAND_5GHZ = 2; // 0x2
+ field public static final int GROUP_OWNER_BAND_AUTO = 0; // 0x0
field public java.lang.String deviceAddress;
field public int groupOwnerIntent;
field public android.net.wifi.WpsInfo wps;
}
+ public static final class WifiP2pConfig.Builder {
+ ctor public WifiP2pConfig.Builder();
+ method public android.net.wifi.p2p.WifiP2pConfig build();
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(android.net.MacAddress);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOwnerBand(int);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(java.lang.String);
+ method public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(java.lang.String);
+ }
+
public class WifiP2pDevice implements android.os.Parcelable {
ctor public WifiP2pDevice();
ctor public WifiP2pDevice(android.net.wifi.p2p.WifiP2pDevice);
@@ -29473,6 +29564,7 @@
method public void clearServiceRequests(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public android.net.wifi.p2p.WifiP2pManager.Channel initialize(android.content.Context, android.os.Looper, android.net.wifi.p2p.WifiP2pManager.ChannelListener);
@@ -29480,7 +29572,10 @@
method public void removeLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener);
method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener);
+ method public void requestDiscoveryState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener);
method public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener);
+ method public void requestNetworkInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener);
+ method public void requestP2pState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.P2pStateListener);
method public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener);
method public void setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener);
method public void setServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ServiceResponseListener);
@@ -29525,6 +29620,10 @@
method public abstract void onConnectionInfoAvailable(android.net.wifi.p2p.WifiP2pInfo);
}
+ public static abstract interface WifiP2pManager.DiscoveryStateListener {
+ method public abstract void onDiscoveryStateAvailable(int);
+ }
+
public static abstract interface WifiP2pManager.DnsSdServiceResponseListener {
method public abstract void onDnsSdServiceAvailable(java.lang.String, java.lang.String, android.net.wifi.p2p.WifiP2pDevice);
}
@@ -29537,6 +29636,14 @@
method public abstract void onGroupInfoAvailable(android.net.wifi.p2p.WifiP2pGroup);
}
+ public static abstract interface WifiP2pManager.NetworkInfoListener {
+ method public abstract void onNetworkInfoAvailable(android.net.NetworkInfo);
+ }
+
+ public static abstract interface WifiP2pManager.P2pStateListener {
+ method public abstract void onP2pStateAvailable(int);
+ }
+
public static abstract interface WifiP2pManager.PeerListListener {
method public abstract void onPeersAvailable(android.net.wifi.p2p.WifiP2pDeviceList);
}
@@ -33453,6 +33560,7 @@
method public static boolean isExternalStorageRemovable();
method public static boolean isExternalStorageRemovable(java.io.File);
field public static java.lang.String DIRECTORY_ALARMS;
+ field public static java.lang.String DIRECTORY_AUDIOBOOKS;
field public static java.lang.String DIRECTORY_DCIM;
field public static java.lang.String DIRECTORY_DOCUMENTS;
field public static java.lang.String DIRECTORY_DOWNLOADS;
@@ -37212,6 +37320,7 @@
method public static java.lang.String getVolumeName(android.net.Uri);
method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri);
method public static android.net.Uri setIncludePending(android.net.Uri);
+ method public static android.net.Uri setRequireOriginal(android.net.Uri);
field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW";
@@ -37309,6 +37418,7 @@
field public static final java.lang.String COMPOSER = "composer";
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String IS_ALARM = "is_alarm";
+ field public static final java.lang.String IS_AUDIOBOOK = "is_audiobook";
field public static final java.lang.String IS_MUSIC = "is_music";
field public static final java.lang.String IS_NOTIFICATION = "is_notification";
field public static final java.lang.String IS_PODCAST = "is_podcast";
@@ -37388,6 +37498,17 @@
field public static final java.lang.String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
}
+ public static abstract interface MediaStore.DownloadColumns implements android.provider.MediaStore.MediaColumns {
+ field public static final java.lang.String DOWNLOAD_URI = "download_uri";
+ field public static final java.lang.String REFERER_URI = "referer_uri";
+ }
+
+ public static final class MediaStore.Downloads implements android.provider.MediaStore.DownloadColumns {
+ method public static android.net.Uri getContentUri(java.lang.String);
+ field public static final android.net.Uri EXTERNAL_CONTENT_URI;
+ field public static final android.net.Uri INTERNAL_CONTENT_URI;
+ }
+
public static final class MediaStore.Files {
ctor public MediaStore.Files();
method public static android.net.Uri getContentUri(java.lang.String);
@@ -37416,8 +37537,8 @@
field public static final java.lang.String DATE_TAKEN = "datetaken";
field public static final java.lang.String DESCRIPTION = "description";
field public static final java.lang.String IS_PRIVATE = "isprivate";
- field public static final java.lang.String LATITUDE = "latitude";
- field public static final java.lang.String LONGITUDE = "longitude";
+ field public static final deprecated java.lang.String LATITUDE = "latitude";
+ field public static final deprecated java.lang.String LONGITUDE = "longitude";
field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
field public static final java.lang.String ORIENTATION = "orientation";
field public static final deprecated java.lang.String PICASA_ID = "picasa_id";
@@ -37479,7 +37600,9 @@
public static class MediaStore.PendingParams {
ctor public MediaStore.PendingParams(android.net.Uri, java.lang.String, java.lang.String);
+ method public void setDownloadUri(android.net.Uri);
method public void setPrimaryDirectory(java.lang.String);
+ method public void setRefererUri(android.net.Uri);
method public void setSecondaryDirectory(java.lang.String);
}
@@ -37539,8 +37662,8 @@
field public static final java.lang.String DURATION = "duration";
field public static final java.lang.String IS_PRIVATE = "isprivate";
field public static final java.lang.String LANGUAGE = "language";
- field public static final java.lang.String LATITUDE = "latitude";
- field public static final java.lang.String LONGITUDE = "longitude";
+ field public static final deprecated java.lang.String LATITUDE = "latitude";
+ field public static final deprecated java.lang.String LONGITUDE = "longitude";
field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
field public static final java.lang.String RESOLUTION = "resolution";
field public static final java.lang.String TAGS = "tags";
@@ -40859,11 +40982,9 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
- method public boolean isInAmbientMode();
method public boolean isPreview();
method public boolean isVisible();
method public void notifyColorsChanged();
- method public void onAmbientModeChanged(boolean, boolean);
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
method public android.app.WallpaperColors onComputeColors();
@@ -42803,6 +42924,19 @@
field public static final int BAND_9 = 9; // 0x9
}
+ public final class AvailableNetworkInfo implements android.os.Parcelable {
+ ctor public AvailableNetworkInfo(int, int, java.util.ArrayList<java.lang.String>);
+ method public int describeContents();
+ method public java.util.List<java.lang.String> getMccMncs();
+ method public int getPriority();
+ method public int getSubId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.AvailableNetworkInfo> CREATOR;
+ field public static final int PRIORITY_HIGH = 1; // 0x1
+ field public static final int PRIORITY_LOW = 3; // 0x3
+ field public static final int PRIORITY_MED = 2; // 0x2
+ }
+
public class CarrierConfigManager {
method public android.os.PersistableBundle getConfig();
method public android.os.PersistableBundle getConfigForSubId(int);
@@ -43624,7 +43758,7 @@
method public static int getDefaultVoiceSubscriptionId();
method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
method public static int getSlotIndex(int);
- method public static int[] getSubscriptionIds(int);
+ method public int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isActiveSubscriptionId(int);
method public boolean isNetworkRoaming(int);
@@ -43781,6 +43915,7 @@
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
method public deprecated void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
+ method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>);
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
@@ -43843,6 +43978,7 @@
field public static final int NETWORK_TYPE_IDEN = 11; // 0xb
field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
field public static final int NETWORK_TYPE_LTE = 13; // 0xd
+ field public static final int NETWORK_TYPE_NR = 20; // 0x14
field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
@@ -45752,6 +45888,7 @@
method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
+ method public int getUnderlineColor();
method public void setFlags(int);
method public void updateDrawState(android.text.TextPaint);
method public void writeToParcel(android.os.Parcel, int);
@@ -47752,6 +47889,7 @@
method public final boolean isFunctionPressed();
method public static final boolean isGamepadButton(int);
method public final boolean isLongPress();
+ method public static final boolean isMediaSessionKey(int);
method public final boolean isMetaPressed();
method public static boolean isModifierKey(int);
method public final boolean isNumLockOn();
@@ -48984,10 +49122,12 @@
method protected int getTopPaddingOffset();
method public android.view.TouchDelegate getTouchDelegate();
method public java.util.ArrayList<android.view.View> getTouchables();
+ method public float getTransitionAlpha();
method public java.lang.String getTransitionName();
method public float getTranslationX();
method public float getTranslationY();
method public float getTranslationZ();
+ method public long getUniqueDrawingId();
method public int getVerticalFadingEdgeLength();
method public int getVerticalScrollbarPosition();
method public int getVerticalScrollbarWidth();
@@ -49192,6 +49332,7 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setAnimationMatrix(android.graphics.Matrix);
method public void setAutofillHints(java.lang.String...);
method public void setAutofillId(android.view.autofill.AutofillId);
method public void setBackground(android.graphics.drawable.Drawable);
@@ -49243,6 +49384,7 @@
method public void setLayoutDirection(int);
method public void setLayoutParams(android.view.ViewGroup.LayoutParams);
method public final void setLeft(int);
+ method public final void setLeftTopRightBottom(int, int, int, int);
method public void setLongClickable(boolean);
method protected final void setMeasuredDimension(int, int);
method public void setMinimumHeight(int);
@@ -49309,6 +49451,7 @@
method public void setTooltipText(java.lang.CharSequence);
method public final void setTop(int);
method public void setTouchDelegate(android.view.TouchDelegate);
+ method public void setTransitionAlpha(float);
method public final void setTransitionName(java.lang.String);
method public void setTranslationX(float);
method public void setTranslationY(float);
@@ -49782,6 +49925,7 @@
method public deprecated boolean isAnimationCacheEnabled();
method protected boolean isChildrenDrawingOrderEnabled();
method protected deprecated boolean isChildrenDrawnWithCacheEnabled();
+ method public boolean isLayoutSuppressed();
method public boolean isMotionEventSplittingEnabled();
method public boolean isTransitionGroup();
method public final void layout(int, int, int, int);
@@ -49847,6 +49991,7 @@
method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
method public void startLayoutAnimation();
method public void startViewTransition(android.view.View);
+ method public void suppressLayout(boolean);
method public void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams);
field protected static final int CLIP_TO_PADDING_MASK = 34; // 0x22
field public static final int FOCUS_AFTER_DESCENDANTS = 262144; // 0x40000
@@ -51860,10 +52005,90 @@
}
+package android.view.inspector {
+
+ public abstract interface InspectionCompanion<T> {
+ method public default java.lang.String getNodeName();
+ method public abstract void mapProperties(android.view.inspector.PropertyMapper);
+ method public abstract void readProperties(T, android.view.inspector.PropertyReader);
+ }
+
+ public static class InspectionCompanion.UninitializedPropertyMapException extends java.lang.RuntimeException {
+ ctor public InspectionCompanion.UninitializedPropertyMapException();
+ }
+
+ public final class IntEnumMapping {
+ method public java.lang.String nameOf(int);
+ }
+
+ public static final class IntEnumMapping.Builder {
+ ctor public IntEnumMapping.Builder();
+ method public android.view.inspector.IntEnumMapping.Builder addValue(java.lang.String, int);
+ method public android.view.inspector.IntEnumMapping build();
+ method public void clear();
+ }
+
+ public final class IntFlagMapping {
+ method public java.lang.String[] namesOf(int);
+ }
+
+ public static final class IntFlagMapping.Builder {
+ ctor public IntFlagMapping.Builder();
+ method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int);
+ method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int, int);
+ method public android.view.inspector.IntFlagMapping build();
+ method public void clear();
+ }
+
+ public abstract interface PropertyMapper {
+ method public abstract int mapBoolean(java.lang.String, int);
+ method public abstract int mapByte(java.lang.String, int);
+ method public abstract int mapChar(java.lang.String, int);
+ method public abstract int mapColor(java.lang.String, int);
+ method public abstract int mapDouble(java.lang.String, int);
+ method public abstract int mapFloat(java.lang.String, int);
+ method public abstract int mapGravity(java.lang.String, int);
+ method public abstract int mapInt(java.lang.String, int);
+ method public abstract int mapIntEnum(java.lang.String, int, android.view.inspector.IntEnumMapping);
+ method public abstract int mapIntFlag(java.lang.String, int, android.view.inspector.IntFlagMapping);
+ method public abstract int mapLong(java.lang.String, int);
+ method public abstract int mapObject(java.lang.String, int);
+ method public abstract int mapShort(java.lang.String, int);
+ }
+
+ public static class PropertyMapper.PropertyConflictException extends java.lang.RuntimeException {
+ ctor public PropertyMapper.PropertyConflictException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public abstract interface PropertyReader {
+ method public abstract void readBoolean(int, boolean);
+ method public abstract void readByte(int, byte);
+ method public abstract void readChar(int, char);
+ method public abstract void readColor(int, int);
+ method public abstract void readColor(int, long);
+ method public abstract void readColor(int, android.graphics.Color);
+ method public abstract void readDouble(int, double);
+ method public abstract void readFloat(int, float);
+ method public abstract void readGravity(int, int);
+ method public abstract void readInt(int, int);
+ method public abstract void readIntEnum(int, int);
+ method public abstract void readIntFlag(int, int);
+ method public abstract void readLong(int, long);
+ method public abstract void readObject(int, java.lang.Object);
+ method public abstract void readShort(int, short);
+ }
+
+ public static class PropertyReader.PropertyTypeMismatchException extends java.lang.RuntimeException {
+ ctor public PropertyReader.PropertyTypeMismatchException(int, java.lang.String, java.lang.String, java.lang.String);
+ ctor public PropertyReader.PropertyTypeMismatchException(int, java.lang.String, java.lang.String);
+ }
+
+}
+
package android.view.intelligence {
- public final class IntelligenceManager {
- method public android.content.ComponentName getIntelligenceServiceComponentName();
+ public final class ContentCaptureManager {
+ method public android.content.ComponentName getServiceComponentName();
method public boolean isContentCaptureEnabled();
method public android.view.ViewStructure newVirtualViewStructure(android.view.autofill.AutofillId, int);
method public void notifyViewAppeared(android.view.ViewStructure);
@@ -54494,6 +54719,7 @@
method public java.lang.String[] getDisplayedValues();
method public int getMaxValue();
method public int getMinValue();
+ method public int getSelectionDividerHeight();
method public int getValue();
method public boolean getWrapSelectorWheel();
method public void setDisplayedValues(java.lang.String[]);
@@ -54503,6 +54729,7 @@
method public void setOnLongPressUpdateInterval(long);
method public void setOnScrollListener(android.widget.NumberPicker.OnScrollListener);
method public void setOnValueChangedListener(android.widget.NumberPicker.OnValueChangeListener);
+ method public void setSelectionDividerHeight(int);
method public void setValue(int);
method public void setWrapSelectorWheel(boolean);
}
@@ -54855,6 +55082,7 @@
method public void setInt(int, java.lang.String, int);
method public void setIntent(int, java.lang.String, android.content.Intent);
method public void setLabelFor(int, int);
+ method public void setLightBackgroundLayoutId(int);
method public void setLong(int, java.lang.String, long);
method public void setOnClickFillInIntent(int, android.content.Intent);
method public void setOnClickPendingIntent(int, android.app.PendingIntent);
@@ -55486,6 +55714,7 @@
method public java.lang.CharSequence getText();
method public android.view.textclassifier.TextClassifier getTextClassifier();
method public final android.content.res.ColorStateList getTextColors();
+ method public android.text.TextDirectionHeuristic getTextDirectionHeuristic();
method public java.util.Locale getTextLocale();
method public android.os.LocaleList getTextLocales();
method public android.text.PrecomputedText.Params getTextMetricsParams();
@@ -55509,6 +55738,7 @@
method public boolean isElegantTextHeight();
method public boolean isFallbackLineSpacing();
method public boolean isInputMethodTarget();
+ method public boolean isSingleLine();
method public boolean isSuggestionsEnabled();
method public boolean isTextSelectable();
method public int length();
diff --git a/api/system-current.txt b/api/system-current.txt
index 57330be..0215b2f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -17,6 +17,7 @@
field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
+ field public static final java.lang.String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
field public static final java.lang.String BACKUP = "android.permission.BACKUP";
field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
@@ -24,7 +25,6 @@
field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
field public static final java.lang.String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
- field public static final java.lang.String BIND_INTELLIGENCE_SERVICE = "android.permission.BIND_INTELLIGENCE_SERVICE";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
@@ -33,6 +33,7 @@
field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
+ field public static final java.lang.String BIND_SMART_SUGGESTIONS_SERVICE = "android.permission.BIND_SMART_SUGGESTIONS_SERVICE";
field public static final java.lang.String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE";
field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
@@ -108,6 +109,7 @@
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
+ field public static final java.lang.String MANAGE_SMART_SUGGESTIONS = "android.permission.MANAGE_SMART_SUGGESTIONS";
field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
@@ -159,6 +161,7 @@
field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
+ field public static final java.lang.String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY";
field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
field public static final java.lang.String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE";
field public static final java.lang.String RECOVERY = "android.permission.RECOVERY";
@@ -172,6 +175,7 @@
field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
+ field public static final java.lang.String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final java.lang.String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -215,6 +219,10 @@
field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
}
+ public static final class Manifest.permission_group {
+ field public static final java.lang.String UNDEFINED = "android.permission-group.UNDEFINED";
+ }
+
public static final class R.array {
field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
}
@@ -223,6 +231,7 @@
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+ field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -554,6 +563,10 @@
method public void onVrStateChanged(boolean);
}
+ public final class WallpaperInfo implements android.os.Parcelable {
+ method public boolean supportsAmbientMode();
+ }
+
public class WallpaperManager {
method public void clearWallpaper(int, int);
method public void setDisplayOffset(android.os.IBinder, int, int);
@@ -1056,12 +1069,12 @@
field public static final java.lang.String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED";
field public static final java.lang.String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
field public static final java.lang.String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
+ field public static final java.lang.String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY";
field public static final java.lang.String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
field public static final java.lang.String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
field public static final java.lang.String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
- field public static final java.lang.String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
@@ -1233,8 +1246,9 @@
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
- method public void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
+ method public deprecated void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public void sendDeviceCustomizationReadyBroadcast();
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
@@ -1821,9 +1835,19 @@
field public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubInfo> CREATOR;
}
+ public class ContextHubIntentEvent {
+ method public static android.hardware.location.ContextHubIntentEvent fromIntent(android.content.Intent);
+ method public android.hardware.location.ContextHubInfo getContextHubInfo();
+ method public int getEventType();
+ method public int getNanoAppAbortCode();
+ method public long getNanoAppId();
+ method public android.hardware.location.NanoAppMessage getNanoAppMessage();
+ }
+
public final class ContextHubManager {
method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback, java.util.concurrent.Executor);
method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback);
+ method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.app.PendingIntent, long);
method public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(android.hardware.location.ContextHubInfo, long);
method public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(android.hardware.location.ContextHubInfo, long);
method public deprecated int[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter);
@@ -1840,6 +1864,18 @@
method public deprecated int unloadNanoApp(int);
method public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(android.hardware.location.ContextHubInfo, long);
method public deprecated int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
+ field public static final int EVENT_HUB_RESET = 6; // 0x6
+ field public static final int EVENT_NANOAPP_ABORTED = 4; // 0x4
+ field public static final int EVENT_NANOAPP_DISABLED = 3; // 0x3
+ field public static final int EVENT_NANOAPP_ENABLED = 2; // 0x2
+ field public static final int EVENT_NANOAPP_LOADED = 0; // 0x0
+ field public static final int EVENT_NANOAPP_MESSAGE = 5; // 0x5
+ field public static final int EVENT_NANOAPP_UNLOADED = 1; // 0x1
+ field public static final java.lang.String EXTRA_CONTEXT_HUB_INFO = "android.hardware.location.extra.CONTEXT_HUB_INFO";
+ field public static final java.lang.String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
+ field public static final java.lang.String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
+ field public static final java.lang.String EXTRA_NANOAPP_ABORT_CODE = "android.hardware.location.extra.NANOAPP_ABORT_CODE";
+ field public static final java.lang.String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
}
public static abstract deprecated class ContextHubManager.Callback {
@@ -2731,7 +2767,6 @@
public class LocationManager {
method public deprecated boolean addGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
- method public android.app.PendingIntent createManageLocationPermissionIntent(java.lang.String, java.lang.String);
method public void flushGnssBatch();
method public int getGnssBatchSize();
method public java.lang.String getNetworkProviderPackage();
@@ -2840,6 +2875,7 @@
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
+ field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
public static abstract class AudioManager.AudioServerStateCallback {
@@ -2979,6 +3015,7 @@
package android.media.session {
public final class MediaSessionManager {
+ method public android.media.session.ISession createSession(android.media.session.MediaSession.CallbackStub, java.lang.String, int);
method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
}
@@ -3654,8 +3691,11 @@
public class WifiManager {
method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+ method public void connect(int, android.net.wifi.WifiManager.ActionListener);
+ method public void disable(int, android.net.wifi.WifiManager.ActionListener);
+ method public void disableEphemeralNetwork(java.lang.String);
+ method public void forget(int, android.net.wifi.WifiManager.ActionListener);
method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
- method public java.util.List<android.net.wifi.hotspot2.OsuProvider> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method public int getWifiApState();
@@ -3665,6 +3705,7 @@
method public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
+ method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method public boolean startScan(android.os.WorkSource);
method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -3813,6 +3854,7 @@
ctor public WifiScanner.ScanSettings();
field public int band;
field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
+ field public boolean ignoreLocationSettings;
field public int maxPeriodInMs;
field public int maxScansToCache;
field public int numBssidsPerScan;
@@ -4270,6 +4312,7 @@
public class UserManager {
method public void clearSeedAccountData();
+ method public android.os.UserHandle getProfileParent(android.os.UserHandle);
method public java.lang.String getSeedAccountName();
method public android.os.PersistableBundle getSeedAccountOptions();
method public java.lang.String getSeedAccountType();
@@ -4589,6 +4632,7 @@
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+ field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled";
field public static final java.lang.String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -4601,6 +4645,8 @@
field public static final java.lang.String HUSH_GESTURE_USED = "hush_gesture_used";
field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
field public static final java.lang.String LAST_SETUP_SHOWN = "last_setup_shown";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
field public static final java.lang.String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
field public static final java.lang.String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
field public static final java.lang.String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
@@ -4938,6 +4984,13 @@
package android.service.intelligence {
+ public final class ContentCaptureEventsRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.List<android.view.intelligence.ContentCaptureEvent> getEvents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.intelligence.ContentCaptureEventsRequest> CREATOR;
+ }
+
public final class FillCallback {
method public void onSuccess(android.service.intelligence.FillResponse);
}
@@ -4972,16 +5025,6 @@
field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
}
- public abstract class IntelligenceService extends android.app.Service {
- ctor public IntelligenceService();
- method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
- method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>);
- method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
- method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
- method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback);
- field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService";
- }
-
public final class InteractionContext implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
@@ -5017,6 +5060,21 @@
method public android.service.intelligence.PresentationParams.Area getSubArea(android.graphics.Rect);
}
+ public abstract class SmartSuggestionsService extends android.app.Service {
+ ctor public SmartSuggestionsService();
+ method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
+ method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
+ method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
+ method public abstract void onContentCaptureEventsRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.ContentCaptureEventsRequest);
+ method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
+ method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
+ method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback);
+ method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
+ method public final void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
+ method public final void setPackageContentCaptureEnabled(java.lang.String, boolean);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.SmartSuggestionsService";
+ }
+
public final class SnapshotData implements android.os.Parcelable {
method public int describeContents();
method public android.app.assist.AssistContent getAssistContent();
@@ -5075,6 +5133,7 @@
ctor public NotificationAssistantService();
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+ method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
method public final android.os.IBinder onBind(android.content.Intent);
method public void onNotificationDirectReply(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
@@ -5138,6 +5197,7 @@
package android.service.oemlock {
public class OemLockManager {
+ method public java.lang.String getLockName();
method public boolean isOemUnlockAllowedByCarrier();
method public boolean isOemUnlockAllowedByUser();
method public void setOemUnlockAllowedByCarrier(boolean, byte[]);
@@ -5308,6 +5368,15 @@
}
+package android.service.wallpaper {
+
+ public class WallpaperService.Engine {
+ method public boolean isInAmbientMode();
+ method public void onAmbientModeChanged(boolean, long);
+ }
+
+}
+
package android.telecom {
public deprecated class AudioState implements android.os.Parcelable {
@@ -5646,6 +5715,26 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public abstract interface NumberVerificationCallback {
+ method public default void onCallReceived(java.lang.String);
+ method public default void onVerificationFailed(int);
+ field public static final int REASON_CONCURRENT_REQUESTS = 4; // 0x4
+ field public static final int REASON_IN_ECBM = 5; // 0x5
+ field public static final int REASON_IN_EMERGENCY_CALL = 6; // 0x6
+ field public static final int REASON_NETWORK_NOT_AVAILABLE = 2; // 0x2
+ field public static final int REASON_TIMED_OUT = 1; // 0x1
+ field public static final int REASON_TOO_MANY_CALLS = 3; // 0x3
+ field public static final int REASON_UNSPECIFIED = 0; // 0x0
+ }
+
+ public final class PhoneNumberRange implements android.os.Parcelable {
+ ctor public PhoneNumberRange(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ method public int describeContents();
+ method public boolean matches(java.lang.String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.PhoneNumberRange> CREATOR;
+ }
+
public class PhoneStateListener {
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
@@ -5810,6 +5899,7 @@
method public boolean needsOtaServiceProvisioning();
method public boolean rebootRadio();
method public void requestCellInfoUpdate(android.os.WorkSource, java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback);
+ method public void requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback);
method public boolean resetRadioConfig();
method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method public void setCarrierDataEnabled(boolean);
@@ -5839,6 +5929,7 @@
field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+ field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L
field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
@@ -5877,6 +5968,7 @@
field public static final int NETWORK_TYPE_BITMASK_HSUPA = 512; // 0x200
field public static final int NETWORK_TYPE_BITMASK_LTE = 8192; // 0x2000
field public static final int NETWORK_TYPE_BITMASK_LTE_CA = 524288; // 0x80000
+ field public static final int NETWORK_TYPE_BITMASK_NR = 1048576; // 0x100000
field public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = 131072; // 0x20000
field public static final int NETWORK_TYPE_BITMASK_UMTS = 8; // 0x8
field public static final int NETWORK_TYPE_BITMASK_UNKNOWN = 1; // 0x1
@@ -6189,6 +6281,7 @@
method public android.os.Bundle getCallExtras();
method public int getCallType();
method public static int getCallTypeFromVideoState(int);
+ method public int getEmergencyServiceCategories();
method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
method public int getRestrictCause();
method public int getServiceType();
@@ -6201,6 +6294,7 @@
method public void setCallExtraBoolean(java.lang.String, boolean);
method public void setCallExtraInt(java.lang.String, int);
method public void setCallRestrictCause(int);
+ method public void setEmergencyServiceCategories(int);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -7187,14 +7281,6 @@
field public static final int TYPE_VIEW_TEXT_CHANGED = 7; // 0x7
}
- public final class IntelligenceManager {
- method public java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
- method public java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
- method public void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
- method public void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
- method public void setPackageContentCaptureEnabled(java.lang.String, boolean);
- }
-
public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
method public android.view.autofill.AutofillId getParentAutofillId();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 738caec..46cbb52 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -61,6 +61,7 @@
}
public class ActivityTaskManager {
+ method public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
method public java.lang.String listAllStacks();
method public void moveTaskToStack(int, int, boolean);
method public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
@@ -81,6 +82,10 @@
field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
}
+ public class AppDetailsActivity extends android.app.Activity {
+ ctor public AppDetailsActivity();
+ }
+
public class AppOpsManager {
method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long);
method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
@@ -300,16 +305,11 @@
public abstract class Context {
method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
- method public abstract java.lang.String getOpPackageName();
method public android.os.UserHandle getUser();
method public int getUserId();
method public void setAutofillCompatibilityEnabled(boolean);
}
- public class ContextWrapper extends android.content.Context {
- method public java.lang.String getOpPackageName();
- }
-
}
package android.content.pm {
@@ -943,6 +943,10 @@
package android.os.storage {
+ public class StorageManager {
+ method public static boolean hasIsolatedStorage();
+ }
+
public final class StorageVolume implements android.os.Parcelable {
method public java.lang.String getPath();
}
@@ -998,6 +1002,7 @@
}
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
+ method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
field public static final java.lang.String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
@@ -1009,6 +1014,8 @@
field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
field public static final java.lang.String DISABLED_PRINT_SERVICES = "disabled_print_services";
field public static final deprecated java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
+ field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
field public static final java.lang.String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final java.lang.String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final java.lang.String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
@@ -1155,6 +1162,7 @@
ctor public NotificationAssistantService();
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+ method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
method public final android.os.IBinder onBind(android.content.Intent);
method public void onNotificationDirectReply(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 1597c8c..52a2ab4 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -77,7 +77,7 @@
+ " <BINDING> binds a typed value to a column and is formatted:\n"
+ " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
+ " <TYPE> specifies data type such as:\n"
- + " b - boolean, s - string, i - integer, l - long, f - float, d - double\n"
+ + " b - boolean, s - string, i - integer, l - long, f - float, d - double, n - null\n"
+ " Note: Omit the value for passing an empty string, e.g column:s:\n"
+ " Example:\n"
+ " # Add \"new_setting\" secure setting with value \"new_value\".\n"
@@ -153,6 +153,7 @@
private static final String TYPE_LONG = "l";
private static final String TYPE_FLOAT = "f";
private static final String TYPE_DOUBLE = "d";
+ private static final String TYPE_NULL = "n";
private static final String COLON = ":";
private static final String ARGUMENT_PREFIX = "--";
@@ -410,6 +411,8 @@
values.put(column, Long.parseLong(value));
} else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) {
values.put(column, Double.parseDouble(value));
+ } else if (TYPE_NULL.equalsIgnoreCase(type)) {
+ values.putNull(column);
} else {
throw new IllegalArgumentException("Unsupported type: " + type);
}
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 7d675ce..d7922bc 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -181,6 +181,7 @@
"libutils",
"libziparchive",
],
+ init_rc: ["idmap2d/idmap2d.rc"],
}
filegroup {
diff --git a/cmds/idmap2/idmap2d/idmap2d.rc b/cmds/idmap2/idmap2d/idmap2d.rc
new file mode 100644
index 0000000..203e7be
--- /dev/null
+++ b/cmds/idmap2/idmap2d/idmap2d.rc
@@ -0,0 +1,4 @@
+service idmap2d /system/bin/idmap2d
+ class main
+ user system
+ group system
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 1c3ebd8..b23c1ed 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -24,6 +24,7 @@
#include "incidentd_util.h"
#include "section_list.h"
+#include <android/os/IncidentReportArgs.h>
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
@@ -41,6 +42,15 @@
#define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB
#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
+// Skip logs (1100 - 1108) because they are already in the bug report
+// Skip 1200, 1201, 1202, 3018 because they take too long
+// TODO(120079956): Skip 3008, 3015 because of error
+#define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
+ 1200, 1201, 1202, /* Native, hal, java traces */ \
+ 3008, /* "package --proto" */ \
+ 3015, /* "activity --proto processes" */ \
+ 3018 /* "meminfo -a --proto" */ }
+
namespace android {
namespace os {
namespace incidentd {
@@ -391,6 +401,38 @@
return NO_ERROR;
}
+status_t IncidentService::dump(int fd, const Vector<String16>& args) {
+ if (std::find(args.begin(), args.end(), String16("--proto")) == args.end()) {
+ ALOGD("Skip dumping incident. Only proto format is supported.");
+ dprintf(fd, "Incident dump only supports proto version.\n");
+ return NO_ERROR;
+ }
+
+ ALOGD("Dump incident proto");
+ IncidentReportArgs incidentArgs;
+ incidentArgs.setDest(DEST_EXPLICIT);
+ int skipped[] = SKIPPED_SECTIONS;
+ for (const Section** section = SECTION_LIST; *section; section++) {
+ const int id = (*section)->id;
+ if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)) {
+ incidentArgs.addSection(id);
+ }
+ }
+
+ if (!checkIncidentPermissions(incidentArgs).isOk()) {
+ return PERMISSION_DENIED;
+ }
+
+ int fd1 = dup(fd);
+ if (fd1 < 0) {
+ return -errno;
+ }
+
+ mHandler->scheduleRunReport(new ReportRequest(incidentArgs, NULL, fd1));
+
+ return NO_ERROR;
+}
+
} // namespace incidentd
} // namespace os
} // namespace android
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index e176bfd..6252ad2 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -112,6 +112,7 @@
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) override;
virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+ virtual status_t dump(int fd, const Vector<String16>& args);
private:
sp<ReportRequestQueue> mQueue;
diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp
index 4948823..098d74e 100644
--- a/cmds/incidentd/src/main.cpp
+++ b/cmds/incidentd/src/main.cpp
@@ -45,7 +45,8 @@
// Create the service
sp<IncidentService> service = new IncidentService(looper);
- if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
+ if (defaultServiceManager()->addService(String16("incident"), service, false,
+ IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) {
ALOGE("Failed to add service");
return -1;
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index e97d6c3..41a2021 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -34,6 +34,8 @@
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
+import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
+import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
/**
* The master atom class. This message defines all of the available
@@ -156,6 +158,7 @@
ServiceLaunchReported service_launch_reported = 100;
PhenotypeFlagStateChanged phenotype_flag_state_changed = 101;
BinaryPushStateChanged binary_push_state_changed = 102;
+ DevicePolicyEvent device_policy_event = 103;
}
// Pulled events will start at field 10000.
@@ -1335,8 +1338,9 @@
* Log bucketed battery charge cycles.
*
* Each bucket represents cycles of the battery past
- * a given charge point. For example, bucket 1 is the
- * lowest 1/8th of the battery, and bucket 8 is 100%.
+ * a given charge point. For example, if 10 cycle buckets are
+ * initialized, bucket 1 is the lowest 1/10th of the battery,
+ * and bucket 10 is 100%.
*
* Logged from:
* /sys/class/power_supply/bms/cycle_count, via Vendor.
@@ -1350,6 +1354,8 @@
optional int32 cycle_bucket_6 = 6;
optional int32 cycle_bucket_7 = 7;
optional int32 cycle_bucket_8 = 8;
+ optional int32 cycle_bucket_9 = 9;
+ optional int32 cycle_bucket_10 = 10;
}
/**
@@ -2012,6 +2018,9 @@
// SWAP
optional int64 swap_in_bytes = 8;
+
+ // The elapsed real time of start of the process.
+ optional int64 process_start_time_nanos = 9;
}
/*
@@ -2537,10 +2546,7 @@
// SWAP
optional int64 swap_in_bytes = 8;
- // RSS high watermark.
- // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
- // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
- // Deprecated: use ProcessMemoryHighWaterMark atom instead.
+ // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true];
// Elapsed real time when the process started.
@@ -2567,9 +2573,7 @@
// RSS
optional int64 rss_in_bytes = 5;
- // RSS high watermark.
- // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status.
- // Deprecated: use ProcessMemoryHighWaterMark atom instead.
+ // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true];
// Elapsed real time when the process started.
@@ -3325,10 +3329,27 @@
optional string process_name = 4;
// Name of the thread taken from `/proc/$PID/task/$TID/comm`
optional string thread_name = 5;
- // What frequency the CPU was running at, in KHz
- optional int32 frequency_khz = 6;
- // Time spent in frequency in milliseconds, since thread start.
- optional int32 time_millis = 7;
+
+ // Report eight different frequencies, and how much time is spent in each frequency. Frequencies
+ // are given in KHz, and time is given in milliseconds since the thread started. All eight
+ // frequencies are given here as the alternative is sending eight separate atoms. This method
+ // significantly reduces the amount of data created
+ optional int32 frequency1_khz = 6;
+ optional int32 time1_millis = 7;
+ optional int32 frequency2_khz = 8;
+ optional int32 time2_millis = 9;
+ optional int32 frequency3_khz = 10;
+ optional int32 time3_millis = 11;
+ optional int32 frequency4_khz = 12;
+ optional int32 time4_millis = 13;
+ optional int32 frequency5_khz = 14;
+ optional int32 time5_millis = 15;
+ optional int32 frequency6_khz = 16;
+ optional int32 time6_millis = 17;
+ optional int32 frequency7_khz = 18;
+ optional int32 time7_millis = 19;
+ optional int32 frequency8_khz = 20;
+ optional int32 time8_millis = 21;
}
/**
@@ -3424,3 +3445,25 @@
// (i.e. roughly since device was last significantly charged).
optional float power_milli_amp_hours = 2;
}
+
+/**
+ * Logs device policy features.
+ *
+ * Logged from:
+ * frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+ * packages/apps/ManagedProvisioning/src/com/android/managedprovisioning/
+ */
+message DevicePolicyEvent {
+ // The event id - unique for each event.
+ optional android.stats.devicepolicy.EventId event_id = 1;
+ // The admin package name.
+ optional string admin_package_name = 2;
+ // A generic integer parameter.
+ optional int32 integer_value = 3;
+ // A generic boolean parameter.
+ optional bool boolean_value = 4;
+ // A parameter specifying a time period in milliseconds.
+ optional uint64 time_period_millis = 5;
+ // A parameter specifying a list of package names, bundle extras or string parameters.
+ optional android.stats.devicepolicy.StringList string_list_value = 6 [(log_mode) = MODE_BYTES];
+}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 3eb05a9..4e4b8f3 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -59,7 +59,7 @@
}
data->clear();
for (const StatsLogEventWrapper& it : returned_value) {
- data->push_back(make_shared<LogEvent>(it));
+ LogEvent::createLogEvents(it, *data);
}
VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
return true;
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index e3f251a..f501574 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -32,9 +32,10 @@
sp<UidMap> StatsPuller::mUidMap = nullptr;
void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
-// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
+ // Pullers can cause significant impact to system health and battery.
+ // So that we don't pull too frequently.
mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
}
@@ -64,8 +65,8 @@
data->setLogdWallClockTimestampNs(wallClockTimeNs);
}
if (ret && mCachedData.size() > 0) {
- mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
- (*data) = mCachedData;
+ mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+ (*data) = mCachedData;
}
StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
return ret;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 87a065b..c070ca3 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -54,52 +54,42 @@
// wifi_bytes_transfer
{android::util::WIFI_BYTES_TRANSFER,
{{2, 3, 4, 5},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
// wifi_bytes_transfer_by_fg_bg
{android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
{{3, 4, 5, 6},
- {2},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
// mobile_bytes_transfer
{android::util::MOBILE_BYTES_TRANSFER,
{{2, 3, 4, 5},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
// mobile_bytes_transfer_by_fg_bg
{android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
{{3, 4, 5, 6},
- {2},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
// bluetooth_bytes_transfer
{android::util::BLUETOOTH_BYTES_TRANSFER,
{{2, 3},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
// kernel_wakelock
{android::util::KERNEL_WAKELOCK,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
// subsystem_sleep_state
{android::util::SUBSYSTEM_SLEEP_STATE,
- {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+ {{}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
// on_device_power_measurement
- {android::util::ON_DEVICE_POWER_MEASUREMENT,
- {{}, {}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
+ {android::util::ON_DEVICE_POWER_MEASUREMENT, {{}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
// cpu_time_per_freq
{android::util::CPU_TIME_PER_FREQ,
- {{3},
- {2},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+ {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
// cpu_time_per_uid
{android::util::CPU_TIME_PER_UID,
{{2, 3},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
// cpu_time_per_uid_freq
@@ -107,169 +97,140 @@
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_TIME_PER_UID_FREQ,
{{4},
- {2, 3},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
// cpu_active_time
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_ACTIVE_TIME,
- {{2},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+ {{2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
// cpu_cluster_time
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{android::util::CPU_CLUSTER_TIME,
- {{3},
- {2},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+ {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
// wifi_activity_energy_info
{android::util::WIFI_ACTIVITY_INFO,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
// modem_activity_info
{android::util::MODEM_ACTIVITY_INFO,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
// bluetooth_activity_info
{android::util::BLUETOOTH_ACTIVITY_INFO,
{{},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
// system_elapsed_realtime
{android::util::SYSTEM_ELAPSED_REALTIME,
{{},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
// system_uptime
{android::util::SYSTEM_UPTIME,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
// remaining_battery_capacity
{android::util::REMAINING_BATTERY_CAPACITY,
{{},
- {},
1 * NS_PER_SEC,
new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
// full_battery_capacity
{android::util::FULL_BATTERY_CAPACITY,
{{},
- {},
1 * NS_PER_SEC,
new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
// battery_voltage
{android::util::BATTERY_VOLTAGE,
- {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
- // battery_voltage
+ {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+ // battery_level
{android::util::BATTERY_LEVEL,
- {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+ {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
{{4, 5, 6, 7, 8, 9},
- {2, 3, 10},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
// native_process_memory_state
{android::util::NATIVE_PROCESS_MEMORY_STATE,
{{3, 4, 5, 6},
- {2, 7},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
{android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
{{3},
- {2},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
// temperature
- {android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
+ {android::util::TEMPERATURE, {{}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
// binder_calls
{android::util::BINDER_CALLS,
{{4, 5, 6, 8, 12},
- {2, 3, 7, 9, 10, 11, 13},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
// binder_calls_exceptions
{android::util::BINDER_CALLS_EXCEPTIONS,
{{},
- {},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
// looper_stats
{android::util::LOOPER_STATS,
{{5, 6, 7, 8, 9},
- {2, 3, 4, 10},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
// Disk Stats
{android::util::DISK_STATS,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
// Directory usage
{android::util::DIRECTORY_USAGE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
// Size of app's code, data, and cache
{android::util::APP_SIZE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
// Size of specific categories of files. Eg. Music.
{android::util::CATEGORY_SIZE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
// Number of fingerprints registered to each user.
{android::util::NUM_FINGERPRINTS,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
// ProcStats.
{android::util::PROC_STATS,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
// ProcStatsPkgProc.
{android::util::PROC_STATS_PKG_PROC,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
// Disk I/O stats per uid.
{android::util::DISK_IO,
- {{2,3,4,5,6,7,8,9,10,11},
- {},
+ {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
3 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DISK_IO)}},
// PowerProfile constants for power model calculations.
{android::util::POWER_PROFILE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
// Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
{android::util::PROCESS_CPU_TIME,
- {{} /* additive fields */, {} /* non additive fields */,
- 5 * NS_PER_SEC /* min cool-down in seconds*/,
- new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
+ {{} /* additive fields */,
+ 5 * NS_PER_SEC /* min cool-down in seconds*/,
+ new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
{android::util::CPU_TIME_PER_THREAD_FREQ,
- {{7},
- {2, 3, 4, 5, 6},
+ {{7, 9, 11, 13, 15, 17, 19, 21},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
// DeviceCalculatedPowerUse.
{android::util::DEVICE_CALCULATED_POWER_USE,
- {{}, {}, 1 * NS_PER_SEC,
+ {{},
+ 1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
// DeviceCalculatedPowerBlameUid.
{android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
- {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
+ {{}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
// DeviceCalculatedPowerBlameOther.
{android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
- {{}, {},
+ {{},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
// BuildInformation.
{android::util::BUILD_INFORMATION,
- {{}, {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
+ {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index bbf5d9d..3350736 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -36,9 +36,6 @@
// The field numbers of the fields that need to be summed when merging
// isolated uid with host uid.
std::vector<int> additiveFields;
- // The field numbers of the fields that can't be merged when merging
- // data belong to isolated uid and host uid.
- std::vector<int> nonAdditiveFields;
// How long should the puller wait before doing an actual pull again. Default
// 1 sec. Set this to 0 if this is handled elsewhere.
int64_t coolDownNs = 1 * NS_PER_SEC;
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index ea7fa97..0b9b6ab 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -25,67 +25,13 @@
namespace os {
namespace statsd {
+using std::list;
using std::map;
+using std::set;
using std::shared_ptr;
+using std::sort;
using std::vector;
-namespace {
-bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
- const vector<int>& nonAdditiveFields) {
- const auto& l_values = lhs->getValues();
- const auto& r_values = rhs->getValues();
-
- for (size_t i : nonAdditiveFields) {
- // We store everything starting from index 0, so we need to use i-1
- if (!(l_values.size() > i - 1 && r_values.size() > i - 1 &&
- l_values[i - 1].mValue == r_values[i - 1].mValue)) {
- return false;
- }
- }
- return true;
-}
-
-// merge rhs to lhs
-// when calling this function, all sanity check should be done already.
-// e.g., index boundary, nonAdditiveFields matching etc.
-bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
- const vector<int>& additiveFields) {
- vector<FieldValue>* host_values = lhs->getMutableValues();
- const auto& child_values = rhs->getValues();
- for (int i : additiveFields) {
- Value& host = (*host_values)[i - 1].mValue;
- const Value& child = (child_values[i - 1]).mValue;
- if (child.getType() != host.getType()) {
- return false;
- }
- switch (child.getType()) {
- case INT:
- host.setInt(host.int_value + child.int_value);
- break;
- case LONG:
- host.setLong(host.long_value + child.long_value);
- break;
- default:
- ALOGE("Tried to merge 2 fields with unsupported type");
- return false;
- }
- }
- return true;
-}
-
-bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos,
- const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) {
- for (const auto& pos : host_pos) {
- if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) &&
- mergeEvent(data[pos], data[child_pos], additiveFields)) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
/**
* Process all data and merge isolated with host if necessary.
* For example:
@@ -95,7 +41,7 @@
* int byte_send = 3;
* int byte_recv = 4;
* }
- * additive fields are {3, 4}, non-additive field is {2}
+ * additive fields are {3, 4}
* If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
* [uid1, fg, 100, 200]
* [uid1_child, fg, 100, 200]
@@ -104,65 +50,119 @@
* We want to merge them and results should be:
* [uid1, fg, 200, 400]
* [uid1, bg, 100, 200]
+ *
+ * All atoms should be of the same tagId. All fields should be present.
*/
-void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
- int tagId) {
+void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
+ int tagId) {
if (StatsPullerManager::kAllPullAtomInfo.find(tagId) ==
StatsPullerManager::kAllPullAtomInfo.end()) {
VLOG("Unknown pull atom id %d", tagId);
return;
}
- int uidField;
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
- if (it == android::util::AtomsInfo::kAtomsWithUidField.end()) {
- VLOG("No uid to merge for atom %d", tagId);
+ if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) ==
+ android::util::AtomsInfo::kAtomsWithAttributionChain.end()) &&
+ (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
+ android::util::AtomsInfo::kAtomsWithUidField.end())) {
+ VLOG("No uid or attribution chain to merge, atom %d", tagId);
return;
- } else {
- uidField = it->second; // uidField is the field number in proto,
}
- const vector<int>& additiveFields =
- StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
- const vector<int>& nonAdditiveFields =
- StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
- // map of host uid to their position in the original vector
- map<int, vector<int>> hostPosition;
- vector<bool> toRemove = vector<bool>(data.size(), false);
-
- for (size_t i = 0; i < data.size(); i++) {
- vector<FieldValue>* valueList = data[i]->getMutableValues();
-
- int uid;
- if (uidField > 0 && (int)data[i]->getValues().size() >= uidField &&
- (data[i]->getValues())[uidField - 1].mValue.getType() == INT) {
- uid = (*data[i]->getMutableValues())[uidField - 1].mValue.int_value;
- } else {
- ALOGE("Malformed log, uid not found. %s", data[i]->ToString().c_str());
- continue;
+ // 1. Map all isolated uid in-place to host uid
+ for (shared_ptr<LogEvent>& event : data) {
+ if (event->GetTagId() != tagId) {
+ ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
+ return;
}
-
- const int hostUid = uidMap->getHostUidOrSelf(uid);
-
- if (hostUid != uid) {
- (*valueList)[0].mValue.setInt(hostUid);
- }
- if (hostPosition.find(hostUid) == hostPosition.end()) {
- hostPosition[hostUid].push_back(i);
+ if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) !=
+ android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
+ for (auto& value : *(event->getMutableValues())) {
+ if (value.mField.getPosAtDepth(0) > kAttributionField) {
+ break;
+ }
+ if (isAttributionUidField(value)) {
+ const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value);
+ value.mValue.setInt(hostUid);
+ }
+ }
} else {
- if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) {
- toRemove[i] = true;
- } else {
- hostPosition[hostUid].push_back(i);
+ auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
+ if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+ int uidField = it->second; // uidField is the field number in proto,
+ // starting from 1
+ if (uidField > 0 && (int)event->getValues().size() >= uidField &&
+ (event->getValues())[uidField - 1].mValue.getType() == INT) {
+ Value& value = (*event->getMutableValues())[uidField - 1].mValue;
+ const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
+ } else {
+ ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
+ return;
+ }
}
}
}
+ // 2. sort the data, bit-wise
+ sort(data.begin(), data.end(),
+ [](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) {
+ if (lhs->size() != rhs->size()) {
+ return lhs->size() < rhs->size();
+ }
+ const std::vector<FieldValue>& lhsValues = lhs->getValues();
+ const std::vector<FieldValue>& rhsValues = rhs->getValues();
+ for (int i = 0; i < (int)lhs->size(); i++) {
+ if (lhsValues[i] != rhsValues[i]) {
+ return lhsValues[i] < rhsValues[i];
+ }
+ }
+ return false;
+ });
+
vector<shared_ptr<LogEvent>> mergedData;
- for (size_t i = 0; i < toRemove.size(); i++) {
- if (!toRemove[i]) {
+ const vector<int>& additiveFieldsVec =
+ StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
+ const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end());
+ bool needMerge = true;
+
+ // 3. do the merge.
+ // The loop invariant is this: for every event, check if it differs on
+ // non-additive fields, or have different attribution chain length.
+ // If so, no need to merge, add itself to the result.
+ // Otherwise, merge the value onto the one immediately next to it.
+ for (int i = 0; i < (int)data.size() - 1; i++) {
+ // Size different, must be different chains.
+ if (data[i]->size() != data[i + 1]->size()) {
mergedData.push_back(data[i]);
+ continue;
+ }
+ vector<FieldValue>* lhsValues = data[i]->getMutableValues();
+ vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues();
+ needMerge = true;
+ for (int p = 0; p < (int)lhsValues->size(); p++) {
+ if ((*lhsValues)[p] != (*rhsValues)[p]) {
+ int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
+ // Differ on non-additive field, abort.
+ if (additiveFields.find(pos) == additiveFields.end()) {
+ needMerge = false;
+ break;
+ }
+ }
+ }
+ if (!needMerge) {
+ mergedData.push_back(data[i]);
+ continue;
+ }
+ // This should be infrequent operation.
+ for (int p = 0; p < (int)lhsValues->size(); p++) {
+ int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
+ if (additiveFields.find(pos) != additiveFields.end()) {
+ (*rhsValues)[p].mValue += (*lhsValues)[p].mValue;
+ }
}
}
+ mergedData.push_back(data.back());
+
data.clear();
data = mergedData;
}
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
index fd4a4a2..f703e6c 100644
--- a/cmds/statsd/src/external/puller_util.h
+++ b/cmds/statsd/src/external/puller_util.h
@@ -25,8 +25,8 @@
namespace os {
namespace statsd {
-void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
- const sp<UidMap>& uidMap, int tagId);
+void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
+ const sp<UidMap>& uidMap, int tagId);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 6617689..3e5e82f 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -101,6 +101,7 @@
const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
{android::util::BINDER_CALLS, {6000, 10000}},
+ {android::util::LOOPER_STATS, {1500, 2500}},
{android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
};
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 625294c..8d61aba 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,13 +41,28 @@
}
}
-LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) {
+LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
mTagId = statsLogEventWrapper.getTagId();
mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
mLogUid = 0;
+ int workChainPosOffset = 0;
+ if (workChainIndex != -1) {
+ const WorkChain& wc = statsLogEventWrapper.getWorkChains()[workChainIndex];
+ // chains are at field 1, level 2
+ int depth = 2;
+ for (int i = 0; i < (int)wc.uids.size(); i++) {
+ int pos[] = {1, i + 1, 1};
+ mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.uids[i])));
+ pos[2]++;
+ mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.tags[i])));
+ mValues.back().mField.decorateLastPos(2);
+ }
+ mValues.back().mField.decorateLastPos(1);
+ workChainPosOffset = 1;
+ }
for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
- Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1));
+ Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1 + workChainPosOffset));
switch (statsLogEventWrapper.getElements()[i].type) {
case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
mValues.push_back(
@@ -79,6 +94,17 @@
}
}
+void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
+ std::vector<std::shared_ptr<LogEvent>>& logEvents) {
+ if (statsLogEventWrapper.getWorkChains().size() == 0) {
+ logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, -1));
+ } else {
+ for (size_t i = 0; i < statsLogEventWrapper.getWorkChains().size(); i++) {
+ logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, i));
+ }
+ }
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
mLogdTimestampNs = wallClockTimestampNs;
mTagId = tagId;
@@ -653,7 +679,7 @@
string LogEvent::ToString() const {
string result;
- result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs,
+ result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
(long long)mElapsedTimestampNs, mTagId);
for (const auto& value : mValues) {
result +=
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 3d5b2ab..5408d17 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -65,7 +65,16 @@
*/
explicit LogEvent(log_msg& msg);
- explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper);
+ /**
+ * Creates LogEvent from StatsLogEventWrapper.
+ */
+ static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
+ std::vector<std::shared_ptr<LogEvent>>& logEvents);
+
+ /**
+ * Construct one LogEvent from a StatsLogEventWrapper with the i-th work chain. -1 if no chain.
+ */
+ explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex);
/**
* Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c3912ee..9fe84dc 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -165,7 +165,7 @@
const bool mSkipZeroDiffOutput;
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4ac55b5..b317361 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -454,6 +454,16 @@
ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
return false;
}
+ if (!metric.has_value_field()) {
+ ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return false;
+ }
+ std::vector<Matcher> fieldMatchers;
+ translateFieldMatcher(metric.value_field(), &fieldMatchers);
+ if (fieldMatchers.size() < 1) {
+ ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return false;
+ }
int metricIndex = allMetricProducers.size();
metricMap.insert({metric.id(), metricIndex});
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 504c586..f1310db 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -343,9 +343,11 @@
}
}
if (isBytesField) {
- protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
- (const char*)dim.mValue.str_value.c_str(),
- dim.mValue.str_value.length());
+ if (dim.mValue.str_value.length() > 0) {
+ protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+ (const char*)dim.mValue.str_value.c_str(),
+ dim.mValue.str_value.length());
+ }
} else {
protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 6384757..3a5be43 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -394,6 +394,167 @@
EXPECT_EQ(1.1f, item16.mValue.float_value);
}
+TEST(LogEventTest, TestStatsLogEventWrapperNoChain) {
+ Parcel parcel;
+ // tag id
+ parcel.writeInt32(1);
+ // elapsed realtime
+ parcel.writeInt64(1111L);
+ // wallclock time
+ parcel.writeInt64(2222L);
+ // no chain
+ parcel.writeInt32(0);
+ // 2 data
+ parcel.writeInt32(2);
+ // int 6
+ parcel.writeInt32(1);
+ parcel.writeInt32(6);
+ // long 10
+ parcel.writeInt32(2);
+ parcel.writeInt64(10);
+ parcel.setDataPosition(0);
+
+ StatsLogEventWrapper statsLogEventWrapper;
+ EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
+ EXPECT_EQ(1, statsLogEventWrapper.getTagId());
+ EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
+ EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
+ EXPECT_EQ(0, statsLogEventWrapper.getWorkChains().size());
+ EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
+ EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
+ EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
+ LogEvent event(statsLogEventWrapper, -1);
+ EXPECT_EQ(1, event.GetTagId());
+ EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event.GetLogdTimestampNs());
+ EXPECT_EQ(2, event.size());
+ EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
+ EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+}
+
+TEST(LogEventTest, TestStatsLogEventWrapperWithChain) {
+ Parcel parcel;
+ // tag id
+ parcel.writeInt32(1);
+ // elapsed realtime
+ parcel.writeInt64(1111L);
+ // wallclock time
+ parcel.writeInt64(2222L);
+ // 3 chains
+ parcel.writeInt32(3);
+ // chain1, 2 nodes (1, "tag1") (2, "tag2")
+ parcel.writeInt32(2);
+ parcel.writeInt32(1);
+ parcel.writeString16(String16("tag1"));
+ parcel.writeInt32(2);
+ parcel.writeString16(String16("tag2"));
+ // chain2, 1 node (3, "tag3")
+ parcel.writeInt32(1);
+ parcel.writeInt32(3);
+ parcel.writeString16(String16("tag3"));
+ // chain3, 2 nodes (4, "") (5, "")
+ parcel.writeInt32(2);
+ parcel.writeInt32(4);
+ parcel.writeString16(String16(""));
+ parcel.writeInt32(5);
+ parcel.writeString16(String16(""));
+ // 2 data
+ parcel.writeInt32(2);
+ // int 6
+ parcel.writeInt32(1);
+ parcel.writeInt32(6);
+ // long 10
+ parcel.writeInt32(2);
+ parcel.writeInt64(10);
+ parcel.setDataPosition(0);
+
+ StatsLogEventWrapper statsLogEventWrapper;
+ EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
+ EXPECT_EQ(1, statsLogEventWrapper.getTagId());
+ EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
+ EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
+ EXPECT_EQ(3, statsLogEventWrapper.getWorkChains().size());
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids.size());
+ EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[0].uids[0]);
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids[1]);
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].tags.size());
+ EXPECT_EQ("tag1", statsLogEventWrapper.getWorkChains()[0].tags[0]);
+ EXPECT_EQ("tag2", statsLogEventWrapper.getWorkChains()[0].tags[1]);
+ EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].uids.size());
+ EXPECT_EQ(3, statsLogEventWrapper.getWorkChains()[1].uids[0]);
+ EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].tags.size());
+ EXPECT_EQ("tag3", statsLogEventWrapper.getWorkChains()[1].tags[0]);
+ EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
+ EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
+ EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].uids.size());
+ EXPECT_EQ(4, statsLogEventWrapper.getWorkChains()[2].uids[0]);
+ EXPECT_EQ(5, statsLogEventWrapper.getWorkChains()[2].uids[1]);
+ EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].tags.size());
+ EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[0]);
+ EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[1]);
+
+ LogEvent event(statsLogEventWrapper, -1);
+ EXPECT_EQ(1, event.GetTagId());
+ EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event.GetLogdTimestampNs());
+ EXPECT_EQ(2, event.size());
+ EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
+ EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+
+ LogEvent event1(statsLogEventWrapper, 0);
+
+ EXPECT_EQ(1, event1.GetTagId());
+ EXPECT_EQ(1111L, event1.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event1.GetLogdTimestampNs());
+ EXPECT_EQ(6, event1.size());
+ EXPECT_EQ(1, event1.getValues()[0].mValue.int_value);
+ EXPECT_EQ(0x2010101, event1.getValues()[0].mField.getField());
+ EXPECT_EQ("tag1", event1.getValues()[1].mValue.str_value);
+ EXPECT_EQ(0x2010182, event1.getValues()[1].mField.getField());
+ EXPECT_EQ(2, event1.getValues()[2].mValue.int_value);
+ EXPECT_EQ(0x2010201, event1.getValues()[2].mField.getField());
+ EXPECT_EQ("tag2", event1.getValues()[3].mValue.str_value);
+ EXPECT_EQ(0x2018282, event1.getValues()[3].mField.getField());
+ EXPECT_EQ(6, event1.getValues()[4].mValue.int_value);
+ EXPECT_EQ(0x20000, event1.getValues()[4].mField.getField());
+ EXPECT_EQ(10, event1.getValues()[5].mValue.long_value);
+ EXPECT_EQ(0x30000, event1.getValues()[5].mField.getField());
+
+ LogEvent event2(statsLogEventWrapper, 1);
+
+ EXPECT_EQ(1, event2.GetTagId());
+ EXPECT_EQ(1111L, event2.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event2.GetLogdTimestampNs());
+ EXPECT_EQ(4, event2.size());
+ EXPECT_EQ(3, event2.getValues()[0].mValue.int_value);
+ EXPECT_EQ(0x2010101, event2.getValues()[0].mField.getField());
+ EXPECT_EQ("tag3", event2.getValues()[1].mValue.str_value);
+ EXPECT_EQ(0x2018182, event2.getValues()[1].mField.getField());
+ EXPECT_EQ(6, event2.getValues()[2].mValue.int_value);
+ EXPECT_EQ(0x20000, event2.getValues()[2].mField.getField());
+ EXPECT_EQ(10, event2.getValues()[3].mValue.long_value);
+ EXPECT_EQ(0x30000, event2.getValues()[3].mField.getField());
+
+ LogEvent event3(statsLogEventWrapper, 2);
+
+ EXPECT_EQ(1, event3.GetTagId());
+ EXPECT_EQ(1111L, event3.GetElapsedTimestampNs());
+ EXPECT_EQ(2222L, event3.GetLogdTimestampNs());
+ EXPECT_EQ(6, event3.size());
+ EXPECT_EQ(4, event3.getValues()[0].mValue.int_value);
+ EXPECT_EQ(0x2010101, event3.getValues()[0].mField.getField());
+ EXPECT_EQ("", event3.getValues()[1].mValue.str_value);
+ EXPECT_EQ(0x2010182, event3.getValues()[1].mField.getField());
+ EXPECT_EQ(5, event3.getValues()[2].mValue.int_value);
+ EXPECT_EQ(0x2010201, event3.getValues()[2].mField.getField());
+ EXPECT_EQ("", event3.getValues()[3].mValue.str_value);
+ EXPECT_EQ(0x2018282, event3.getValues()[3].mField.getField());
+ EXPECT_EQ(6, event3.getValues()[4].mValue.int_value);
+ EXPECT_EQ(0x20000, event3.getValues()[4].mField.getField());
+ EXPECT_EQ(10, event3.getValues()[5].mValue.long_value);
+ EXPECT_EQ(0x30000, event3.getValues()[5].mField.getField());
+}
TEST(LogEventTest, TestBinaryFieldAtom) {
Atom launcherAtom;
@@ -444,7 +605,44 @@
EXPECT_EQ(orig_str, result_str);
}
+TEST(LogEventTest, TestBinaryFieldAtom_empty) {
+ Atom launcherAtom;
+ auto launcher_event = launcherAtom.mutable_launcher_event();
+ launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
+ launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
+ launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
+ // empty string.
+ string extension_str;
+
+ LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
+
+ event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
+ event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
+ event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
+ event1.write(extension_str);
+ event1.init();
+
+ ProtoOutputStream proto;
+ event1.ToProto(proto);
+
+ std::vector<uint8_t> outData;
+ outData.resize(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
+
+ std::string result_str(outData.begin(), outData.end());
+ std::string orig_str;
+ launcherAtom.SerializeToString(&orig_str);
+
+ EXPECT_EQ(orig_str, result_str);
+}
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index fc6e420..266ea35 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -80,7 +80,7 @@
.WillRepeatedly(Return(hostUid));
EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
.WillRepeatedly(ReturnArg<0>());
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
vector<vector<int>> actual;
extractIntoVector(inputData, actual);
@@ -120,7 +120,7 @@
.WillRepeatedly(Return(hostUid));
EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
.WillRepeatedly(ReturnArg<0>());
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
vector<vector<int>> actual;
extractIntoVector(inputData, actual);
@@ -154,7 +154,7 @@
.WillRepeatedly(Return(hostUid));
EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
.WillRepeatedly(ReturnArg<0>());
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
// 20->32->31
// 20->22->21
@@ -190,7 +190,7 @@
.WillRepeatedly(Return(hostUid));
EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
.WillRepeatedly(ReturnArg<0>());
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
// 20->32->31
// 20->22->21
@@ -231,7 +231,7 @@
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
- mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
vector<vector<int>> actual;
extractIntoVector(inputData, actual);
@@ -256,7 +256,7 @@
inputData.push_back(event);
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+ mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
EXPECT_EQ(2, (int)inputData.size());
}
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 8e9b91d..f3bf6e7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -46,6 +46,10 @@
private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
+ private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
+ private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
+ "add-or-remove-call-companion-app";
+ private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -64,6 +68,9 @@
"usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
+ "usage: telecom set-test-call-screening-app <PACKAGE>\n" +
+ "usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
+ "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
"usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" +
"usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" +
"usage: telecom set-default-dialer <PACKAGE>\n" +
@@ -113,6 +120,15 @@
case COMMAND_REGISTER_PHONE_ACCOUNT:
runRegisterPhoneAccount();
break;
+ case COMMAND_SET_TEST_CALL_SCREENING_APP:
+ runSetTestCallScreeningApp();
+ break;
+ case COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP:
+ runAddOrRemoveCallCompanionApp();
+ break;
+ case COMMAND_SET_TEST_AUTO_MODE_APP:
+ runSetTestAutoModeApp();
+ break;
case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
runRegisterSimPhoneAccount();
break;
@@ -173,6 +189,23 @@
System.out.println("Success - " + handle + " registered.");
}
+ private void runSetTestCallScreeningApp() throws RemoteException {
+ final String packageName = nextArg();
+ mTelecomService.setTestDefaultCallScreeningApp(packageName);
+ }
+
+ private void runAddOrRemoveCallCompanionApp() throws RemoteException {
+ final String packageName = nextArgRequired();
+ String isAdded = nextArgRequired();
+ boolean isAddedBool = "1".equals(isAdded);
+ mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool);
+ }
+
+ private void runSetTestAutoModeApp() throws RemoteException {
+ final String packageName = nextArg();
+ mTelecomService.setTestAutoModeApp(packageName);
+ }
+
private void runUnregisterPhoneAccount() throws RemoteException {
final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
mTelecomService.unregisterPhoneAccount(handle);
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 41c2e6c..c2e441b 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -24296,7 +24296,8 @@
HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$RangeOption;ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
HSPLandroid/icu/util/CodePointMap;->iterator()Ljava/util/Iterator;
-HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;HSPLandroid/icu/util/Currency$1;-><init>()V
+HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;
+HSPLandroid/icu/util/Currency$1;-><init>()V
HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/String;Ljava/lang/Void;)Landroid/icu/util/Currency;
HSPLandroid/icu/util/Currency$CurrencyUsage;-><init>(Ljava/lang/String;I)V
@@ -32940,8 +32941,8 @@
HSPLandroid/view/IWindowManager;->isWindowTraceEnabled()Z
HSPLandroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
HSPLandroid/view/IWindowManager;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z;I)V
+HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
HSPLandroid/view/IWindowManager;->prepareAppTransition(IZ)V
HSPLandroid/view/IWindowManager;->reenableKeyguard(Landroid/os/IBinder;)V
HSPLandroid/view/IWindowManager;->refreshScreenCaptureDisabled(I)V
@@ -51954,6 +51955,22 @@
HSPLlibcore/timezone/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
HSPLlibcore/timezone/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)Z
HSPLlibcore/timezone/TimeZoneFinder;->processXml(Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLlibcore/timezone/ZoneInfoDB$TzData$1;->create(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->close()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->finalize()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->getAvailableIDs()[Ljava/lang/String;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->getBufferIterator(Ljava/lang/String;)Llibcore/io/BufferIterator;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->hasTimeZone(Ljava/lang/String;)Z
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->loadData(Ljava/lang/String;)Z
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->loadTzDataWithFallback([Ljava/lang/String;)Llibcore/timezone/ZoneInfoDB$TzData;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->makeTimeZone(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->makeTimeZoneUncached(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readHeader()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readIndex(Llibcore/io/BufferIterator;II)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readZoneTab(Llibcore/io/BufferIterator;II)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->validateOffset(II)V
+HSPLlibcore/timezone/ZoneInfoDB;->getInstance()Llibcore/timezone/ZoneInfoDB$TzData;
HSPLlibcore/util/BasicLruCache;-><init>(I)V
HSPLlibcore/util/BasicLruCache;->create(Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/util/BasicLruCache;->entryEvicted(Ljava/lang/Object;Ljava/lang/Object;)V
@@ -52007,22 +52024,6 @@
HSPLlibcore/util/ZoneInfo;->hashCode()I
HSPLlibcore/util/ZoneInfo;->inDaylightTime(Ljava/util/Date;)Z
HSPLlibcore/util/ZoneInfo;->readTimeZone(Ljava/lang/String;Llibcore/io/BufferIterator;J)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLlibcore/util/ZoneInfoDB$TzData$1;->create(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->close()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->finalize()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->getAvailableIDs()[Ljava/lang/String;
-HSPLlibcore/util/ZoneInfoDB$TzData;->getBufferIterator(Ljava/lang/String;)Llibcore/io/BufferIterator;
-HSPLlibcore/util/ZoneInfoDB$TzData;->hasTimeZone(Ljava/lang/String;)Z
-HSPLlibcore/util/ZoneInfoDB$TzData;->loadData(Ljava/lang/String;)Z
-HSPLlibcore/util/ZoneInfoDB$TzData;->loadTzDataWithFallback([Ljava/lang/String;)Llibcore/util/ZoneInfoDB$TzData;
-HSPLlibcore/util/ZoneInfoDB$TzData;->makeTimeZone(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->makeTimeZoneUncached(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->readHeader()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->readIndex(Llibcore/io/BufferIterator;II)V
-HSPLlibcore/util/ZoneInfoDB$TzData;->readZoneTab(Llibcore/io/BufferIterator;II)V
-HSPLlibcore/util/ZoneInfoDB$TzData;->validateOffset(II)V
-HSPLlibcore/util/ZoneInfoDB;->getInstance()Llibcore/util/ZoneInfoDB$TzData;
HSPLorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
HSPLorg/apache/harmony/dalvik/ddmc/ChunkHandler;->putString(Ljava/nio/ByteBuffer;Ljava/lang/String;)V
HSPLorg/apache/harmony/dalvik/ddmc/ChunkHandler;->type(Ljava/lang/String;)I
@@ -63464,6 +63465,9 @@
Llibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;
Llibcore/timezone/TimeZoneFinder;
+Llibcore/timezone/ZoneInfoDB$TzData$1;
+Llibcore/timezone/ZoneInfoDB$TzData;
+Llibcore/timezone/ZoneInfoDB;
Llibcore/util/BasicLruCache;
Llibcore/util/CharsetUtils;
Llibcore/util/CollectionUtils;
@@ -63478,9 +63482,6 @@
Llibcore/util/ZoneInfo$OffsetInterval;
Llibcore/util/ZoneInfo$WallTime;
Llibcore/util/ZoneInfo;
-Llibcore/util/ZoneInfoDB$TzData$1;
-Llibcore/util/ZoneInfoDB$TzData;
-Llibcore/util/ZoneInfoDB;
Lorg/apache/harmony/dalvik/NativeTestTarget;
Lorg/apache/harmony/dalvik/ddmc/Chunk;
Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;
diff --git a/config/hiddenapi-max-sdk-p-blacklist.txt b/config/hiddenapi-greylist-max-p.txt
similarity index 100%
rename from config/hiddenapi-max-sdk-p-blacklist.txt
rename to config/hiddenapi-greylist-max-p.txt
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-greylist.txt
similarity index 90%
rename from config/hiddenapi-light-greylist.txt
rename to config/hiddenapi-greylist.txt
index f78506b..579ef93 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -51,6 +51,7 @@
Landroid/app/backup/IFullBackupRestoreObserver$Stub;-><init>()V
Landroid/app/backup/IRestoreObserver$Stub;-><init>()V
Landroid/app/DownloadManager;->restartDownload([J)V
+Landroid/app/IActivityController$Stub;-><init>()V
Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration;
Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
@@ -62,25 +63,35 @@
Landroid/app/IActivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IActivityManager;
Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I
Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I
+Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
+Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
Landroid/app/IActivityManager;->checkPermission(Ljava/lang/String;II)I
+Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
Landroid/app/IActivityManager;->finishHeavyWeightApp()V
Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V
Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
Landroid/app/IActivityManager;->getAllStackInfos()Ljava/util/List;
Landroid/app/IActivityManager;->getConfiguration()Landroid/content/res/Configuration;
+Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
+Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
Landroid/app/IActivityManager;->getIntentForIntentSender(Landroid/content/IIntentSender;)Landroid/content/Intent;
Landroid/app/IActivityManager;->getIntentSender(ILjava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I[Landroid/content/Intent;[Ljava/lang/String;ILandroid/os/Bundle;I)Landroid/content/IIntentSender;
Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
Landroid/app/IActivityManager;->getLaunchedFromUid(Landroid/os/IBinder;)I
+Landroid/app/IActivityManager;->getLockTaskModeState()I
Landroid/app/IActivityManager;->getMemoryInfo(Landroid/app/ActivityManager$MemoryInfo;)V
Landroid/app/IActivityManager;->getPackageProcessState(Ljava/lang/String;Ljava/lang/String;)I
Landroid/app/IActivityManager;->getProcessLimit()I
+Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
Landroid/app/IActivityManager;->getProcessPss([I)[J
Landroid/app/IActivityManager;->getProviderMimeType(Landroid/net/Uri;I)Ljava/lang/String;
+Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
+Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
Landroid/app/IActivityManager;->getServices(II)Ljava/util/List;
Landroid/app/IActivityManager;->getTaskBounds(I)Landroid/graphics/Rect;
Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
+Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
Landroid/app/IActivityManager;->handleApplicationStrictModeViolation(Landroid/os/IBinder;ILandroid/os/StrictMode$ViolationInfo;)V
Landroid/app/IActivityManager;->hang(Landroid/os/IBinder;Z)V
Landroid/app/IActivityManager;->isInLockTaskMode()Z
@@ -98,9 +109,11 @@
Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
Landroid/app/IActivityManager;->registerProcessObserver(Landroid/app/IProcessObserver;)V
Landroid/app/IActivityManager;->registerReceiver(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/IIntentReceiver;Landroid/content/IntentFilter;Ljava/lang/String;II)Landroid/content/Intent;
+Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
Landroid/app/IActivityManager;->registerUserSwitchObserver(Landroid/app/IUserSwitchObserver;Ljava/lang/String;)V
Landroid/app/IActivityManager;->removeContentProviderExternal(Ljava/lang/String;Landroid/os/IBinder;)V
Landroid/app/IActivityManager;->removeStack(I)V
+Landroid/app/IActivityManager;->removeTask(I)Z
Landroid/app/IActivityManager;->requestBugReport(I)V
Landroid/app/IActivityManager;->resizeDockedStack(Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
Landroid/app/IActivityManager;->resizeStack(ILandroid/graphics/Rect;ZZZI)V
@@ -120,8 +133,12 @@
Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
Landroid/app/IActivityManager;->setTaskResizeable(II)V
Landroid/app/IActivityManager;->shutdown(I)Z
+Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
+Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
+Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
Landroid/app/IActivityManager;->startBinderTracking()Z
Landroid/app/IActivityManager;->startInstrumentation(Landroid/content/ComponentName;Ljava/lang/String;ILandroid/os/Bundle;Landroid/app/IInstrumentationWatcher;Landroid/app/IUiAutomationConnection;ILjava/lang/String;)Z
+Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
Landroid/app/IActivityManager;->startSystemLockTaskMode(I)V
Landroid/app/IActivityManager;->startUserInBackground(I)Z
Landroid/app/IActivityManager;->stopAppSwitches()V
@@ -144,12 +161,16 @@
Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
Landroid/app/IAlarmManager;->getNextAlarmClock(I)Landroid/app/AlarmManager$AlarmClockInfo;
Landroid/app/IAlarmManager;->set(Ljava/lang/String;IJJJILandroid/app/PendingIntent;Landroid/app/IAlarmListener;Ljava/lang/String;Landroid/os/WorkSource;Landroid/app/AlarmManager$AlarmClockInfo;)V
+Landroid/app/IAlarmManager;->setTime(J)Z
Landroid/app/IApplicationThread;->scheduleBindService(Landroid/os/IBinder;Landroid/content/Intent;ZI)V
Landroid/app/IApplicationThread;->scheduleCreateService(Landroid/os/IBinder;Landroid/content/pm/ServiceInfo;Landroid/content/res/CompatibilityInfo;I)V
Landroid/app/IApplicationThread;->scheduleStopService(Landroid/os/IBinder;)V
Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
Landroid/app/IApplicationThread;->scheduleUnbindService(Landroid/os/IBinder;Landroid/content/Intent;)V
Landroid/app/IAppTask;->getTaskInfo()Landroid/app/ActivityManager$RecentTaskInfo;
+Landroid/app/IAssistDataReceiver$Stub;-><init>()V
+Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
+Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z
Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
Landroid/app/IInstrumentationWatcher$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IInstrumentationWatcher;
@@ -236,6 +257,7 @@
Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String;
Landroid/bluetooth/IBluetooth;->isEnabled()Z
+Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
Landroid/bluetooth/IBluetoothA2dp$Stub;-><init>()V
Landroid/bluetooth/IBluetoothA2dp$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothA2dp;
Landroid/bluetooth/IBluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
@@ -266,6 +288,10 @@
Landroid/bluetooth/IBluetoothManagerCallback$Stub;-><init>()V
Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap;
Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V
+Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
+Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
+Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
+Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
Landroid/content/ContentProviderProxy;->mRemote:Landroid/os/IBinder;
Landroid/content/IClipboard$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard;
@@ -312,11 +338,13 @@
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V
+Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I
Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I
@@ -325,6 +353,7 @@
Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V
Landroid/content/pm/IPackageDeleteObserver2$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver2;
Landroid/content/pm/IPackageDeleteObserver2;->onPackageDeleted(Ljava/lang/String;ILjava/lang/String;)V
+Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
Landroid/content/pm/IPackageInstaller;->uninstall(Landroid/content/pm/VersionedPackage;Ljava/lang/String;ILandroid/content/IntentSender;I)V
Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;->mRemote:Landroid/os/IBinder;
@@ -364,11 +393,14 @@
Landroid/content/pm/IPackageManager;->clearPackagePreferredActivities(Ljava/lang/String;)V
Landroid/content/pm/IPackageManager;->currentToCanonicalPackageNames([Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->deleteApplicationCacheFiles(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
Landroid/content/pm/IPackageManager;->getApplicationEnabledSetting(Ljava/lang/String;I)I
+Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
Landroid/content/pm/IPackageManager;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->getBlockUninstallForUser(Ljava/lang/String;I)Z
Landroid/content/pm/IPackageManager;->getComponentEnabledSetting(Landroid/content/ComponentName;I)I
Landroid/content/pm/IPackageManager;->getFlagsForUid(I)I
+Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
Landroid/content/pm/IPackageManager;->getInstalledApplications(II)Landroid/content/pm/ParceledListSlice;
Landroid/content/pm/IPackageManager;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
Landroid/content/pm/IPackageManager;->getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String;
@@ -376,6 +408,7 @@
Landroid/content/pm/IPackageManager;->getInstrumentationInfo(Landroid/content/ComponentName;I)Landroid/content/pm/InstrumentationInfo;
Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
Landroid/content/pm/IPackageManager;->getNameForUid(I)Ljava/lang/String;
+Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
Landroid/content/pm/IPackageManager;->getPackageInstaller()Landroid/content/pm/IPackageInstaller;
Landroid/content/pm/IPackageManager;->getPackagesForUid(I)[Ljava/lang/String;
Landroid/content/pm/IPackageManager;->getPackageUid(Ljava/lang/String;II)I
@@ -415,6 +448,7 @@
Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I
+Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
Landroid/content/res/ConfigurationBoundResourceCache;-><init>()V
@@ -438,6 +472,7 @@
Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I
Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z
Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
+Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
@@ -447,7 +482,10 @@
Landroid/location/ICountryListener$Stub;-><init>()V
Landroid/location/IGeocodeProvider$Stub;-><init>()V
Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider;
+Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
+Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
Landroid/location/IGeofenceProvider$Stub;-><init>()V
+Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/location/ILocationListener$Stub;-><init>()V
@@ -460,6 +498,10 @@
Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List;
+Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
+Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
+Landroid/location/INetInitiatedListener$Stub;-><init>()V
+Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V
Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String;
Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String;
@@ -486,6 +528,17 @@
Landroid/media/MediaScanner$MyMediaScannerClient;-><init>(Landroid/media/MediaScanner;)V
Landroid/media/projection/IMediaProjectionManager;->hasProjectionPermission(ILjava/lang/String;)Z
Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager;
+Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
+Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
+Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties;
Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
@@ -499,6 +552,7 @@
Landroid/net/IConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties;
Landroid/net/IConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
Landroid/net/IConnectivityManager;->getAllNetworkInfo()[Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
Landroid/net/IConnectivityManager;->getLastTetherError(Ljava/lang/String;)I
Landroid/net/IConnectivityManager;->getNetworkInfo(I)Landroid/net/NetworkInfo;
Landroid/net/IConnectivityManager;->getTetherableIfaces()[Ljava/lang/String;
@@ -508,9 +562,12 @@
Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager;->reportInetCondition(II)V
Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
+Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
+Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
Landroid/net/INetworkPolicyListener$Stub;-><init>()V
Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
+Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo;
Landroid/net/INetworkPolicyManager;->getRestrictBackground()Z
Landroid/net/INetworkPolicyManager;->getUidPolicy(I)I
Landroid/net/INetworkPolicyManager;->setNetworkPolicies([Landroid/net/NetworkPolicy;)V
@@ -520,14 +577,19 @@
Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService;
Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
+Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
Landroid/net/INetworkStatsService;->forceUpdate()V
Landroid/net/INetworkStatsService;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats;
Landroid/net/INetworkStatsService;->getMobileIfaces()[Ljava/lang/String;
Landroid/net/INetworkStatsService;->openSession()Landroid/net/INetworkStatsSession;
Landroid/net/INetworkStatsService;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession;
Landroid/net/INetworkStatsSession;->close()V
+Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
+Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
Landroid/net/INetworkStatsSession;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats;
Landroid/net/INetworkStatsSession;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats;
+Landroid/net/InterfaceConfiguration;-><init>()V
+Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange;
Landroid/net/MobileLinkQualityInfo;-><init>()V
Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager;
Landroid/net/nsd/INsdManager;->getMessenger()Landroid/os/Messenger;
@@ -552,7 +614,12 @@
Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V
Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle;
+Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V
+Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable;
Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;)Landroid/os/AsyncResult;
+Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult;
+Landroid/os/AsyncResult;->result:Ljava/lang/Object;
+Landroid/os/AsyncResult;->userObj:Ljava/lang/Object;
Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
@@ -569,18 +636,24 @@
Landroid/os/BatteryManager;->EXTRA_MAX_CHARGING_VOLTAGE:Ljava/lang/String;
Landroid/os/BatteryStats$Counter;-><init>()V
Landroid/os/BatteryStats$Counter;->getCountLocked(I)I
+Landroid/os/BatteryStats$HistoryItem;-><init>()V
Landroid/os/BatteryStats$HistoryItem;->batteryHealth:B
+Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B
Landroid/os/BatteryStats$HistoryItem;->batteryPlugType:B
Landroid/os/BatteryStats$HistoryItem;->batteryStatus:B
Landroid/os/BatteryStats$HistoryItem;->batteryVoltage:C
Landroid/os/BatteryStats$HistoryItem;->clear()V
+Landroid/os/BatteryStats$HistoryItem;->cmd:B
Landroid/os/BatteryStats$HistoryItem;->CMD_UPDATE:B
Landroid/os/BatteryStats$HistoryItem;->next:Landroid/os/BatteryStats$HistoryItem;
Landroid/os/BatteryStats$HistoryItem;->same(Landroid/os/BatteryStats$HistoryItem;)Z
Landroid/os/BatteryStats$HistoryItem;->setTo(JBLandroid/os/BatteryStats$HistoryItem;)V
Landroid/os/BatteryStats$HistoryItem;->setTo(Landroid/os/BatteryStats$HistoryItem;)V
Landroid/os/BatteryStats$HistoryItem;->states2:I
+Landroid/os/BatteryStats$HistoryItem;->states:I
+Landroid/os/BatteryStats$HistoryItem;->time:J
Landroid/os/BatteryStats$Timer;-><init>()V
+Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I
Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStarts(I)I
@@ -604,6 +677,8 @@
Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
Landroid/os/BatteryStats$Uid$Sensor;->GPS:I
Landroid/os/BatteryStats$Uid$Wakelock;-><init>()V
+Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
+Landroid/os/BatteryStats$Uid;-><init>()V
Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer;
Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
Landroid/os/BatteryStats$Uid;->getMobileRadioActiveTime(I)J
@@ -616,6 +691,7 @@
Landroid/os/BatteryStats$Uid;->getWakelockStats()Landroid/util/ArrayMap;
Landroid/os/BatteryStats$Uid;->getWifiBatchedScanTime(IJI)J
Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
+Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
Landroid/os/BatteryStats;-><init>()V
Landroid/os/BatteryStats;->computeBatteryRealtime(JI)J
@@ -627,6 +703,7 @@
Landroid/os/BatteryStats;->getGlobalWifiRunningTime(JI)J
Landroid/os/BatteryStats;->getMobileRadioActiveTime(JI)J
Landroid/os/BatteryStats;->getNetworkActivityBytes(II)J
+Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
Landroid/os/BatteryStats;->getPhoneOnTime(JI)J
Landroid/os/BatteryStats;->getPhoneSignalStrengthTime(IJI)J
Landroid/os/BatteryStats;->getScreenBrightnessTime(IJI)J
@@ -640,6 +717,10 @@
Landroid/os/BatteryStats;->WAKE_TYPE_PARTIAL:I
Landroid/os/Binder;->execTransact(IJJI)Z
Landroid/os/Binder;->mObject:J
+Landroid/os/Broadcaster;-><init>()V
+Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
+Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
+Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String;
Landroid/os/Build;->getLong(Ljava/lang/String;)J
Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
@@ -713,6 +794,7 @@
Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File;
Landroid/os/Environment;->buildPaths([Ljava/io/File;[Ljava/lang/String;)[Ljava/io/File;
Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File;
+Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File;
Landroid/os/Environment;->initForCurrentUser()V
Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
@@ -733,12 +815,15 @@
Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
Landroid/os/FileUtils;->sync(Ljava/io/FileOutputStream;)Z
+Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
Landroid/os/Handler;-><init>(Z)V
Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
+Landroid/os/Handler;->getMain()Landroid/os/Handler;
Landroid/os/Handler;->getPostMessage(Ljava/lang/Runnable;Ljava/lang/Object;)Landroid/os/Message;
Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
Landroid/os/Handler;->mLooper:Landroid/os/Looper;
Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger;
+Landroid/os/HwBinder;->reportSyspropChanged()V
Landroid/os/HwParcel;-><init>(Z)V
Landroid/os/HwRemoteBinder;-><init>()V
Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -749,16 +834,26 @@
Landroid/os/IDeviceIdleController;->getAppIdTempWhitelist()[I
Landroid/os/IDeviceIdleController;->getFullPowerWhitelistExceptIdle()[Ljava/lang/String;
Landroid/os/INetworkManagementService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
+Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->disableNat(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->enableNat(Ljava/lang/String;Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->getInterfaceConfig(Ljava/lang/String;)Landroid/net/InterfaceConfiguration;
Landroid/os/INetworkManagementService;->getIpForwardingEnabled()Z
+Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
Landroid/os/INetworkManagementService;->isTetheringStarted()Z
Landroid/os/INetworkManagementService;->listTetheredInterfaces()[Ljava/lang/String;
+Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
+Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
+Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
Landroid/os/INetworkManagementService;->setIpForwardingEnabled(Z)V
+Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
Landroid/os/INetworkManagementService;->startTethering([Ljava/lang/String;)V
Landroid/os/INetworkManagementService;->stopTethering()V
Landroid/os/INetworkManagementService;->tetherInterface(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
Landroid/os/INetworkManagementService;->untetherInterface(Ljava/lang/String;)V
Landroid/os/IPermissionController$Stub$Proxy;->checkPermission(Ljava/lang/String;II)Z
Landroid/os/IPermissionController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPermissionController;
@@ -767,12 +862,15 @@
Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I
Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I
+Landroid/os/IPowerManager;->goToSleep(JII)V
Landroid/os/IPowerManager;->isInteractive()Z
Landroid/os/IPowerManager;->nap(J)V
+Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
Landroid/os/IPowerManager;->releaseWakeLock(Landroid/os/IBinder;I)V
Landroid/os/IPowerManager;->userActivity(JII)V
Landroid/os/IPowerManager;->wakeUp(JLjava/lang/String;Ljava/lang/String;)V
Landroid/os/IRecoverySystem$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IRecoverySystem;
+Landroid/os/IRemoteCallback$Stub;-><init>()V
Landroid/os/IRemoteCallback;->sendResult(Landroid/os/Bundle;)V
Landroid/os/IServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/IServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
@@ -798,6 +896,7 @@
Landroid/os/Message;->markInUse()V
Landroid/os/Message;->next:Landroid/os/Message;
Landroid/os/Message;->recycleUnchecked()V
+Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
Landroid/os/Message;->target:Landroid/os/Handler;
Landroid/os/Message;->toString(J)Ljava/lang/String;
Landroid/os/Message;->when:J
@@ -817,13 +916,16 @@
Landroid/os/Parcel;->mNativePtr:J
Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V
Landroid/os/Parcel;->readArraySet(Ljava/lang/ClassLoader;)Landroid/util/ArraySet;
+Landroid/os/Parcel;->readBlob()[B
Landroid/os/Parcel;->readCharSequence()Ljava/lang/CharSequence;
Landroid/os/Parcel;->readCreator(Landroid/os/Parcelable$Creator;Ljava/lang/ClassLoader;)Landroid/os/Parcelable;
Landroid/os/Parcel;->readExceptionCode()I
Landroid/os/Parcel;->readParcelableCreator(Ljava/lang/ClassLoader;)Landroid/os/Parcelable$Creator;
Landroid/os/Parcel;->readRawFileDescriptor()Ljava/io/FileDescriptor;
+Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V
Landroid/os/Parcel;->writeArraySet(Landroid/util/ArraySet;)V
+Landroid/os/Parcel;->writeBlob([B)V
Landroid/os/Parcel;->writeCharSequence(Ljava/lang/CharSequence;)V
Landroid/os/Parcel;->writeParcelableCreator(Landroid/os/Parcelable;)V
Landroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V
@@ -890,15 +992,27 @@
Landroid/os/Process;->VPN_UID:I
Landroid/os/Process;->WIFI_UID:I
Landroid/os/RecoverySystem;->verifyPackageCompatibility(Ljava/io/InputStream;)Z
+Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
+Landroid/os/Registrant;->clear()V
Landroid/os/Registrant;->getHandler()Landroid/os/Handler;
Landroid/os/Registrant;->messageForRegistrant()Landroid/os/Message;
+Landroid/os/Registrant;->notifyRegistrant()V
+Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
Landroid/os/Registrant;->notifyResult(Ljava/lang/Object;)V
+Landroid/os/RegistrantList;-><init>()V
Landroid/os/RegistrantList;->add(Landroid/os/Handler;ILjava/lang/Object;)V
+Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
+Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
Landroid/os/RegistrantList;->get(I)Ljava/lang/Object;
+Landroid/os/RegistrantList;->notifyRegistrants()V
+Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
Landroid/os/RegistrantList;->notifyResult(Ljava/lang/Object;)V
+Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
+Landroid/os/RegistrantList;->removeCleared()V
Landroid/os/RegistrantList;->size()I
Landroid/os/RemoteCallback;->mHandler:Landroid/os/Handler;
Landroid/os/RemoteCallbackList;->mCallbacks:Landroid/util/ArrayMap;
+Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
Landroid/os/SELinux;->checkSELinuxAccess(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
Landroid/os/SELinux;->getContext()Ljava/lang/String;
Landroid/os/SELinux;->getFileContext(Ljava/lang/String;)Ljava/lang/String;
@@ -920,6 +1034,7 @@
Landroid/os/ServiceManagerProxy;->getService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/ServiceManagerProxy;->mRemote:Landroid/os/IBinder;
Landroid/os/ServiceSpecificException;-><init>(ILjava/lang/String;)V
+Landroid/os/ServiceSpecificException;->errorCode:I
Landroid/os/SharedMemory;->getFd()I
Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String;
Landroid/os/StatFs;->mStat:Landroid/system/StructStatVfs;
@@ -927,6 +1042,7 @@
Landroid/os/storage/IObbActionListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IObbActionListener;
Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
+Landroid/os/storage/StorageEventListener;-><init>()V
Landroid/os/StrictMode$Span;->finish()V
Landroid/os/StrictMode$ThreadPolicy;->mask:I
Landroid/os/StrictMode$VmPolicy$Builder;->mMask:I
@@ -954,8 +1070,12 @@
Landroid/os/SystemProperties;->native_get_long(Ljava/lang/String;J)J
Landroid/os/SystemProperties;->native_set(Ljava/lang/String;Ljava/lang/String;)V
Landroid/os/SystemProperties;->PROP_NAME_MAX:I
+Landroid/os/SystemProperties;->reportSyspropChanged()V
Landroid/os/SystemProperties;->sChangeCallbacks:Ljava/util/ArrayList;
Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/SystemService;->start(Ljava/lang/String;)V
+Landroid/os/SystemService;->stop(Ljava/lang/String;)V
+Landroid/os/SystemVibrator;-><init>()V
Landroid/os/SystemVibrator;-><init>(Landroid/content/Context;)V
Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
@@ -994,6 +1114,7 @@
Landroid/os/UserHandle;->getUid(II)I
Landroid/os/UserHandle;->getUserId(I)I
Landroid/os/UserHandle;->isIsolated(I)Z
+Landroid/os/UserHandle;->isSameApp(II)Z
Landroid/os/UserHandle;->mHandle:I
Landroid/os/UserHandle;->MU_ENABLED:Z
Landroid/os/UserHandle;->OWNER:Landroid/os/UserHandle;
@@ -1023,6 +1144,7 @@
Landroid/os/UserManager;->getUserStartRealtime()J
Landroid/os/UserManager;->getUserUnlockRealtime()J
Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
Landroid/os/UserManager;->isDeviceInDemoMode(Landroid/content/Context;)Z
Landroid/os/UserManager;->isGuestUser(I)Z
Landroid/os/UserManager;->isLinkedUser()Z
@@ -1072,6 +1194,7 @@
Landroid/R$styleable;->CalendarView_weekDayTextAppearance:I
Landroid/R$styleable;->CalendarView_weekNumberColor:I
Landroid/R$styleable;->CalendarView_weekSeparatorLineColor:I
+Landroid/R$styleable;->CheckBoxPreference:[I
Landroid/R$styleable;->CheckedTextView:[I
Landroid/R$styleable;->CheckedTextView_checkMark:I
Landroid/R$styleable;->CompoundButton:[I
@@ -1382,7 +1505,18 @@
Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
+Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
+Landroid/telephony/ims/compat/ImsService;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
Landroid/telephony/JapanesePhoneNumberFormatter;->format(Landroid/text/Editable;)V
+Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
+Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
+Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
Landroid/telephony/SmsCbCmasInfo;->getCategory()I
Landroid/telephony/SmsCbCmasInfo;->getCertainty()I
Landroid/telephony/SmsCbCmasInfo;->getMessageClass()I
@@ -1424,10 +1558,21 @@
Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;-><init>()V
Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
+Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
Landroid/view/IDockedStackListener$Stub;-><init>()V
Landroid/view/IGraphicsStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/view/IGraphicsStats$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IGraphicsStats;
+Landroid/view/IRecentsAnimationController;->finish(Z)V
+Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot;
Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V
+Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V
+Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
+Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V
+Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
+Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V
+Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
+Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
+Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
Landroid/view/IRotationWatcher$Stub;-><init>()V
Landroid/view/IWindow$Stub;-><init>()V
Landroid/view/IWindow$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindow;
@@ -1435,10 +1580,13 @@
Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
Landroid/view/IWindowManager$Stub$Proxy;->getDockedStackSide()I
Landroid/view/IWindowManager$Stub$Proxy;->getInitialDisplayDensity(I)I
-Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar()Z
+Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar(I)Z
Landroid/view/IWindowManager$Stub$Proxy;->watchRotation(Landroid/view/IRotationWatcher;I)I
Landroid/view/IWindowManager$Stub;-><init>()V
Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
+Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
+Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
+Landroid/view/IWindowManager;->endProlongedAnimations()V
Landroid/view/IWindowManager;->executeAppTransition()V
Landroid/view/IWindowManager;->freezeRotation(I)V
Landroid/view/IWindowManager;->getAnimationScale(I)F
@@ -1447,21 +1595,24 @@
Landroid/view/IWindowManager;->getDockedStackSide()I
Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I
Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V
-Landroid/view/IWindowManager;->hasNavigationBar()Z
+Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
+Landroid/view/IWindowManager;->hasNavigationBar(I)Z
Landroid/view/IWindowManager;->isKeyguardLocked()Z
Landroid/view/IWindowManager;->isKeyguardSecure()Z
Landroid/view/IWindowManager;->isSafeModeEnabled()Z
Landroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
Landroid/view/IWindowManager;->registerDockedStackListener(Landroid/view/IDockedStackListener;)V
Landroid/view/IWindowManager;->removeRotationWatcher(Landroid/view/IRotationWatcher;)V
Landroid/view/IWindowManager;->setAnimationScale(IF)V
Landroid/view/IWindowManager;->setAnimationScales([F)V
Landroid/view/IWindowManager;->setInTouchMode(Z)V
+Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
Landroid/view/IWindowManager;->setShelfHeight(ZI)V
Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
Landroid/view/IWindowManager;->thawRotation()V
-Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession;
Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
Landroid/view/IWindowSession;->getInTouchMode()Z
@@ -1497,6 +1648,7 @@
Lcom/android/ims/ImsCall;->reject(I)V
Lcom/android/ims/ImsCall;->terminate(I)V
Lcom/android/ims/ImsConfigListener$Stub;-><init>()V
+Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
Lcom/android/ims/ImsEcbm;->exitEmergencyCallbackMode()V
Lcom/android/ims/ImsManager;->getConfigInterface()Lcom/android/ims/ImsConfig;
Lcom/android/ims/ImsManager;->getInstance(Landroid/content/Context;I)Lcom/android/ims/ImsManager;
@@ -1506,12 +1658,48 @@
Lcom/android/ims/ImsUtInterface;->queryCallForward(ILjava/lang/String;Landroid/os/Message;)V
Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V
Lcom/android/ims/internal/IImsCallSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsCallSession;
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V
Lcom/android/ims/internal/IImsEcbm$Stub;-><init>()V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnected()V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V
Lcom/android/ims/internal/IImsService$Stub;-><init>()V
Lcom/android/ims/internal/IImsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsService;
Lcom/android/ims/internal/IImsUt$Stub;-><init>()V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
Lcom/android/ims/internal/IImsVideoCallCallback;->changeCallDataUsage(J)V
Lcom/android/ims/internal/IImsVideoCallCallback;->changeCameraCapabilities(Landroid/telecom/VideoProfile$CameraCapabilities;)V
Lcom/android/ims/internal/IImsVideoCallCallback;->changePeerDimensions(II)V
@@ -1522,7 +1710,52 @@
Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V
Lcom/android/ims/internal/ImsVideoCallProviderWrapper;-><init>(Lcom/android/ims/internal/IImsVideoCallProvider;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V
+Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V
+Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
+Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I
+Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService;
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService;
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
Lcom/android/internal/app/AlertActivity;-><init>()V
Lcom/android/internal/app/AlertActivity;->mAlert:Lcom/android/internal/app/AlertController;
Lcom/android/internal/app/AlertActivity;->mAlertParams:Lcom/android/internal/app/AlertController$AlertParams;
@@ -1553,6 +1786,7 @@
Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startOperation:I
Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startWatchingMode:I
Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_stopWatchingMode:I
+Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
Lcom/android/internal/app/IAppOpsService;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List;
Lcom/android/internal/app/IAppOpsService;->getPackagesForOps([I)Ljava/util/List;
Lcom/android/internal/app/IAppOpsService;->resetAllModes(ILjava/lang/String;)V
@@ -1600,6 +1834,7 @@
Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
+Lcom/android/internal/content/PackageMonitor;-><init>()V
Lcom/android/internal/database/SortCursor;-><init>([Landroid/database/Cursor;Ljava/lang/String;)V
Lcom/android/internal/database/SortCursor;->mCursor:Landroid/database/Cursor;
Lcom/android/internal/database/SortCursor;->mCursors:[Landroid/database/Cursor;
@@ -1613,6 +1848,14 @@
Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;)V
Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
+Lcom/android/internal/location/ILocationProvider;->disable()V
+Lcom/android/internal/location/ILocationProvider;->enable()V
+Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
+Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
+Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
+Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
+Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
Lcom/android/internal/logging/MetricsLogger;-><init>()V
Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
Lcom/android/internal/net/VpnConfig;-><init>()V
@@ -1982,6 +2225,7 @@
Lcom/android/internal/R$styleable;->MenuGroup:[I
Lcom/android/internal/R$styleable;->MenuItem:[I
Lcom/android/internal/R$styleable;->MenuView:[I
+Lcom/android/internal/R$styleable;->NumberPicker:[I
Lcom/android/internal/R$styleable;->PopupWindow:[I
Lcom/android/internal/R$styleable;->PopupWindow_popupAnimationStyle:I
Lcom/android/internal/R$styleable;->PopupWindow_popupBackground:I
@@ -2107,6 +2351,7 @@
Lcom/android/internal/R$styleable;->TextView_typeface:I
Lcom/android/internal/R$styleable;->TextView_width:I
Lcom/android/internal/R$styleable;->Theme:[I
+Lcom/android/internal/R$styleable;->TwoLineListItem:[I
Lcom/android/internal/R$styleable;->View:[I
Lcom/android/internal/R$styleable;->ViewAnimator:[I
Lcom/android/internal/R$styleable;->ViewFlipper:[I
@@ -2615,7 +2860,6 @@
Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V
Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mApnContexts:Ljava/util/HashMap;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause;
@@ -2697,7 +2941,6 @@
Lcom/android/internal/telephony/dataconnection/DcTracker;->onSetUserDataEnabled(Z)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->registerForAllDataDisconnected(Landroid/os/Handler;ILjava/lang/Object;)V
Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
@@ -2711,7 +2954,6 @@
Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->updateRecords()V
Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
@@ -3130,9 +3372,9 @@
Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I
Lcom/android/internal/telephony/ITelephony;->getCallState()I
Lcom/android/internal/telephony/ITelephony;->getDataActivity()I
+Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
Lcom/android/internal/telephony/ITelephony;->getDataState()I
Lcom/android/internal/telephony/ITelephony;->getNetworkType()I
-Lcom/android/internal/telephony/ITelephony;->getVoiceMessageCount()I
Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->handlePinMmiForSubscriber(ILjava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z
@@ -3203,7 +3445,6 @@
Lcom/android/internal/telephony/Phone;->isWifiCallingEnabled()Z
Lcom/android/internal/telephony/Phone;->mCi:Lcom/android/internal/telephony/CommandsInterface;
Lcom/android/internal/telephony/Phone;->mContext:Landroid/content/Context;
-Lcom/android/internal/telephony/Phone;->mDcTracker:Lcom/android/internal/telephony/dataconnection/DcTracker;
Lcom/android/internal/telephony/Phone;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
Lcom/android/internal/telephony/Phone;->mImsPhone:Lcom/android/internal/telephony/Phone;
Lcom/android/internal/telephony/Phone;->mMmiRegistrants:Landroid/os/RegistrantList;
@@ -3296,7 +3537,6 @@
Lcom/android/internal/telephony/ProxyController;->mRadioCapabilitySessionId:I
Lcom/android/internal/telephony/ProxyController;->mSetRadioAccessFamilyStatus:[I
Lcom/android/internal/telephony/ProxyController;->mUniqueIdGenerator:Ljava/util/concurrent/atomic/AtomicInteger;
-Lcom/android/internal/telephony/ProxyController;->registerForAllDataDisconnected(ILandroid/os/Handler;ILjava/lang/Object;)V
Lcom/android/internal/telephony/ProxyController;->sendRadioCapabilityRequest(IIIILjava/lang/String;II)V
Lcom/android/internal/telephony/ProxyController;->sProxyController:Lcom/android/internal/telephony/ProxyController;
Lcom/android/internal/telephony/RadioCapability;->getRadioAccessFamily()I
@@ -3396,7 +3636,6 @@
Lcom/android/internal/telephony/ServiceStateTracker;->notifyDataRegStateRilRadioTechnologyChanged()V
Lcom/android/internal/telephony/ServiceStateTracker;->notifySignalStrength()Z
Lcom/android/internal/telephony/ServiceStateTracker;->pollState()V
-Lcom/android/internal/telephony/ServiceStateTracker;->powerOffRadioSafely(Lcom/android/internal/telephony/dataconnection/DcTracker;)V
Lcom/android/internal/telephony/ServiceStateTracker;->reRegisterNetwork(Landroid/os/Message;)V
Lcom/android/internal/telephony/ServiceStateTracker;->resetServiceStateInIwlanMode()V
Lcom/android/internal/telephony/ServiceStateTracker;->setOperatorIdd(Ljava/lang/String;)V
@@ -4368,7 +4607,6 @@
Lcom/google/android/util/AbstractMessageParser$Token$Type;->SMILEY:Lcom/google/android/util/AbstractMessageParser$Token$Type;
Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
Lcom/google/android/util/AbstractMessageParser$Token$Type;->YOUTUBE_VIDEO:Lcom/google/android/util/AbstractMessageParser$Token$Type;
-Lcom/sun/nio/file/ExtendedWatchEventModifier;->FILE_TREE:Lcom/sun/nio/file/ExtendedWatchEventModifier;
Lgov/nist/core/Debug;->printStackTrace(Ljava/lang/Exception;)V
Lgov/nist/core/GenericObject;-><init>()V
Lgov/nist/core/GenericObject;->dbgPrint()V
@@ -4508,178 +4746,9 @@
Lgov/nist/javax/sip/address/SipUri;->setUserParam(Ljava/lang/String;)V
Lgov/nist/javax/sip/parser/URLParser;-><init>(Ljava/lang/String;)V
Lgov/nist/javax/sip/parser/URLParser;->sipURL(Z)Lgov/nist/javax/sip/address/SipUri;
-Ljava/lang/DexCache;->dexFile:J
-Ljava/lang/invoke/SerializedLambda;-><init>(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
-Ljava/lang/invoke/SerializedLambda;->getCapturedArg(I)Ljava/lang/Object;
-Ljava/lang/invoke/SerializedLambda;->getCapturedArgCount()I
-Ljava/lang/invoke/SerializedLambda;->getCapturingClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodName()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodSignature()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplMethodKind()I
-Ljava/lang/invoke/SerializedLambda;->getImplMethodName()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplMethodSignature()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getInstantiatedMethodType()Ljava/lang/String;
-Ljava/lang/UNIXProcess;->pid:I
-Ljava/net/AddressCache$AddressCacheEntry;-><init>(Ljava/lang/Object;)V
-Ljava/net/AddressCache$AddressCacheEntry;->expiryNanos:J
-Ljava/net/AddressCache$AddressCacheEntry;->value:Ljava/lang/Object;
-Ljava/net/AddressCache$AddressCacheKey;->mHostname:Ljava/lang/String;
-Ljava/net/AddressCache;->cache:Llibcore/util/BasicLruCache;
-Ljava/net/Inet6AddressImpl;->addressCache:Ljava/net/AddressCache;
-Ljava/net/PlainSocketImpl;-><init>()V
-Ljava/nio/DirectByteBuffer;->cleaner()Lsun/misc/Cleaner;
-Ljava/nio/file/FileTreeWalker;->followLinks:Z
-Ljava/nio/file/FileTreeWalker;->linkOptions:[Ljava/nio/file/LinkOption;
-Ljava/nio/file/FileTreeWalker;->maxDepth:I
-Ljava/util/zip/ZipFile$ZipEntryIterator;->nextElement()Ljava/util/zip/ZipEntry;
Ljunit/framework/TestCase;->fName:Ljava/lang/String;
Ljunit/framework/TestSuite;->isPublicTestMethod(Ljava/lang/reflect/Method;)Z
Ljunit/framework/TestSuite;->isTestMethod(Ljava/lang/reflect/Method;)Z
-Llibcore/icu/DateIntervalFormat;->formatDateRange(JJILjava/lang/String;)Ljava/lang/String;
-Llibcore/icu/ICU;->CACHED_PATTERNS:Llibcore/util/BasicLruCache;
-Llibcore/icu/ICU;->getBestDateTimePattern(Ljava/lang/String;Ljava/util/Locale;)Ljava/lang/String;
-Llibcore/icu/ICU;->getBestDateTimePatternNative(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-Llibcore/icu/ICU;->getDateFormatOrder(Ljava/lang/String;)[C
-Llibcore/icu/LocaleData;->firstDayOfWeek:Ljava/lang/Integer;
-Llibcore/icu/LocaleData;->get(Ljava/util/Locale;)Llibcore/icu/LocaleData;
-Llibcore/icu/LocaleData;->longStandAloneWeekdayNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->mapInvalidAndNullLocales(Ljava/util/Locale;)Ljava/util/Locale;
-Llibcore/icu/LocaleData;->minimalDaysInFirstWeek:Ljava/lang/Integer;
-Llibcore/icu/LocaleData;->shortMonthNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->shortStandAloneMonthNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->shortStandAloneWeekdayNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->timeFormat_Hm:Ljava/lang/String;
-Llibcore/icu/LocaleData;->timeFormat_hm:Ljava/lang/String;
-Llibcore/icu/LocaleData;->today:Ljava/lang/String;
-Llibcore/icu/LocaleData;->tomorrow:Ljava/lang/String;
-Llibcore/icu/LocaleData;->zeroDigit:C
-Llibcore/icu/TimeZoneNames;->forLocale(Ljava/util/Locale;)[Ljava/lang/String;
-Llibcore/io/AsynchronousCloseMonitor;->signalBlockedThreads(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;-><init>(Llibcore/io/Os;)V
-Llibcore/io/BlockGuardOs;->chmod(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->chown(Ljava/lang/String;II)V
-Llibcore/io/BlockGuardOs;->close(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;->fchmod(Ljava/io/FileDescriptor;I)V
-Llibcore/io/BlockGuardOs;->fchown(Ljava/io/FileDescriptor;II)V
-Llibcore/io/BlockGuardOs;->fdatasync(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;->fstat(Ljava/io/FileDescriptor;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->fstatvfs(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;
-Llibcore/io/BlockGuardOs;->lchown(Ljava/lang/String;II)V
-Llibcore/io/BlockGuardOs;->link(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->lseek(Ljava/io/FileDescriptor;JI)J
-Llibcore/io/BlockGuardOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->mkdir(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->mkfifo(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
-Llibcore/io/BlockGuardOs;->posix_fallocate(Ljava/io/FileDescriptor;JJ)V
-Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
-Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;[BIIJ)I
-Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
-Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;[BIIJ)I
-Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
-Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;[BII)I
-Llibcore/io/BlockGuardOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/BlockGuardOs;->readv(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
-Llibcore/io/BlockGuardOs;->realpath(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/BlockGuardOs;->remove(Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
-Llibcore/io/BlockGuardOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
-Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;[BII)I
-Llibcore/io/BlockGuardOs;->writev(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
-Llibcore/io/BufferIterator;->readByte()B
-Llibcore/io/BufferIterator;->readByteArray([BII)V
-Llibcore/io/BufferIterator;->readInt()I
-Llibcore/io/BufferIterator;->readIntArray([III)V
-Llibcore/io/BufferIterator;->seek(I)V
-Llibcore/io/BufferIterator;->skip(I)V
-Llibcore/io/DropBox;->addText(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;-><init>(Llibcore/io/Os;)V
-Llibcore/io/ForwardingOs;->access(Ljava/lang/String;I)Z
-Llibcore/io/ForwardingOs;->chmod(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->chown(Ljava/lang/String;II)V
-Llibcore/io/ForwardingOs;->getenv(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/ForwardingOs;->lchown(Ljava/lang/String;II)V
-Llibcore/io/ForwardingOs;->link(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/ForwardingOs;->mkdir(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->mkfifo(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
-Llibcore/io/ForwardingOs;->os:Llibcore/io/Os;
-Llibcore/io/ForwardingOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/ForwardingOs;->remove(Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->removexattr(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
-Llibcore/io/ForwardingOs;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Llibcore/io/ForwardingOs;->setxattr(Ljava/lang/String;Ljava/lang/String;[BI)V
-Llibcore/io/ForwardingOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/ForwardingOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
-Llibcore/io/ForwardingOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->sysconf(I)J
-Llibcore/io/ForwardingOs;->unlink(Ljava/lang/String;)V
-Llibcore/io/IoBridge;->isConnected(Ljava/io/FileDescriptor;Ljava/net/InetAddress;III)Z
-Llibcore/io/IoUtils;->closeQuietly(Ljava/io/FileDescriptor;)V
-Llibcore/io/IoUtils;->closeQuietly(Ljava/lang/AutoCloseable;)V
-Llibcore/io/IoUtils;->closeQuietly(Ljava/net/Socket;)V
-Llibcore/io/IoUtils;->readFileAsByteArray(Ljava/lang/String;)[B
-Llibcore/io/IoUtils;->readFileAsString(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/IoUtils;->setBlocking(Ljava/io/FileDescriptor;Z)V
-Llibcore/io/MemoryMappedFile;->bigEndianIterator()Llibcore/io/BufferIterator;
-Llibcore/io/MemoryMappedFile;->mmapRO(Ljava/lang/String;)Llibcore/io/MemoryMappedFile;
-Llibcore/io/Os;->chmod(Ljava/lang/String;I)V
-Llibcore/io/Os;->close(Ljava/io/FileDescriptor;)V
-Llibcore/io/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V
-Llibcore/io/Os;->gai_strerror(I)Ljava/lang/String;
-Llibcore/io/Os;->remove(Ljava/lang/String;)V
-Llibcore/io/Os;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
-Llibcore/io/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Llibcore/io/Os;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/Os;->strerror(I)Ljava/lang/String;
-Llibcore/io/Os;->sysconf(I)J
-Llibcore/io/Streams;->readAsciiLine(Ljava/io/InputStream;)Ljava/lang/String;
-Llibcore/io/Streams;->readFully(Ljava/io/InputStream;)[B
-Llibcore/io/Streams;->readFully(Ljava/io/InputStream;[B)V
-Llibcore/io/Streams;->readSingleByte(Ljava/io/InputStream;)I
-Llibcore/io/Streams;->skipAll(Ljava/io/InputStream;)V
-Llibcore/io/Streams;->writeSingleByte(Ljava/io/OutputStream;I)V
-Llibcore/net/event/NetworkEventDispatcher;->addListener(Llibcore/net/event/NetworkEventListener;)V
-Llibcore/net/event/NetworkEventDispatcher;->getInstance()Llibcore/net/event/NetworkEventDispatcher;
-Llibcore/net/event/NetworkEventListener;-><init>()V
-Llibcore/net/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Llibcore/net/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Llibcore/net/MimeUtils;->guessExtensionFromMimeType(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/net/MimeUtils;->guessMimeTypeFromExtension(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/net/NetworkSecurityPolicy;->isCleartextTrafficPermitted()Z
-Llibcore/util/BasicLruCache;-><init>(I)V
-Llibcore/util/BasicLruCache;->evictAll()V
-Llibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Llibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-Llibcore/util/EmptyArray;->BYTE:[B
-Llibcore/util/EmptyArray;->INT:[I
-Llibcore/util/EmptyArray;->OBJECT:[Ljava/lang/Object;
-Llibcore/util/ZoneInfoDB$TzData;-><init>()V
-Lorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
-Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;->CHUNK_ORDER:Ljava/nio/ByteOrder;
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;->broadcast(I)V
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;->sendChunk(Lorg/apache/harmony/dalvik/ddmc/Chunk;)V
-Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;->getThreadStats()[B
-Lorg/apache/harmony/xml/dom/ElementImpl;->localName:Ljava/lang/String;
-Lorg/apache/harmony/xml/ExpatAttributes;-><init>()V
-Lorg/apache/harmony/xml/ExpatParser$EntityParser;->depth:I
-Lorg/apache/harmony/xml/ExpatParser;-><init>(Ljava/lang/String;Lorg/apache/harmony/xml/ExpatReader;ZLjava/lang/String;Ljava/lang/String;)V
-Lorg/apache/harmony/xml/ExpatParser;->append([BII)V
-Lorg/apache/harmony/xml/ExpatParser;->append([CII)V
-Lorg/apache/harmony/xml/ExpatParser;->attributes:Lorg/apache/harmony/xml/ExpatAttributes;
-Lorg/apache/harmony/xml/ExpatParser;->cloneAttributes()Lorg/xml/sax/Attributes;
-Lorg/apache/harmony/xml/ExpatParser;->finish()V
-Lorg/apache/harmony/xml/ExpatParser;->xmlReader:Lorg/apache/harmony/xml/ExpatReader;
-Lorg/apache/harmony/xml/ExpatReader;-><init>()V
-Lorg/apache/harmony/xml/ExpatReader;->contentHandler:Lorg/xml/sax/ContentHandler;
Lorg/apache/xalan/extensions/ExpressionContext;->getContextNode()Lorg/w3c/dom/Node;
Lorg/apache/xalan/extensions/ExpressionContext;->getErrorListener()Ljavax/xml/transform/ErrorListener;
Lorg/apache/xalan/extensions/ExpressionContext;->getVariableOrParam(Lorg/apache/xml/utils/QName;)Lorg/apache/xpath/objects/XObject;
@@ -5358,431 +5427,3 @@
Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutput(Ljava/io/Writer;)V
Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutputProperty(Ljava/lang/String;Ljava/lang/String;)V
Lorg/ccil/cowan/tagsoup/XMLWriter;->setPrefix(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/xml/sax/helpers/NamespaceSupport$Context;-><init>(Lorg/xml/sax/helpers/NamespaceSupport;)V
-Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;-><init>(Lorg/xml/sax/helpers/ParserAdapter;)V
-Lsun/misc/ASCIICaseInsensitiveComparator;->CASE_INSENSITIVE_ORDER:Ljava/util/Comparator;
-Lsun/misc/ASCIICaseInsensitiveComparator;->lowerCaseHashCode(Ljava/lang/String;)I
-Lsun/misc/BASE64Decoder;-><init>()V
-Lsun/misc/BASE64Decoder;->pem_convert_array:[B
-Lsun/misc/BASE64Encoder;-><init>()V
-Lsun/misc/BASE64Encoder;->pem_array:[C
-Lsun/misc/CEFormatException;-><init>(Ljava/lang/String;)V
-Lsun/misc/CEStreamExhausted;-><init>()V
-Lsun/misc/CharacterDecoder;-><init>()V
-Lsun/misc/CharacterEncoder;-><init>()V
-Lsun/misc/CharacterEncoder;->encodeBuffer([B)Ljava/lang/String;
-Lsun/misc/CharacterEncoder;->encodeBufferPrefix(Ljava/io/OutputStream;)V
-Lsun/misc/CharacterEncoder;->pStream:Ljava/io/PrintStream;
-Lsun/misc/Cleaner;->create(Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner;
-Lsun/misc/FloatingDecimal;->$assertionsDisabled:Z
-Lsun/misc/FloatingDecimal;->getHexDigit(Ljava/lang/String;I)I
-Lsun/misc/FloatingDecimal;->stripLeadingZeros(Ljava/lang/String;)Ljava/lang/String;
-Lsun/misc/FormattedFloatingDecimal$Form;->COMPATIBLE:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal$Form;->DECIMAL_FLOAT:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal$Form;->SCIENTIFIC:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal;->$assertionsDisabled:Z
-Lsun/misc/FpUtils;->$assertionsDisabled:Z
-Lsun/misc/FpUtils;->rawCopySign(DD)D
-Lsun/misc/HexDumpEncoder;-><init>()V
-Lsun/misc/HexDumpEncoder;->currentByte:I
-Lsun/misc/HexDumpEncoder;->offset:I
-Lsun/misc/HexDumpEncoder;->thisLine:[B
-Lsun/misc/HexDumpEncoder;->thisLineLength:I
-Lsun/misc/IOUtils;->readFully(Ljava/io/InputStream;IZ)[B
-Lsun/misc/JarIndex;-><init>([Ljava/lang/String;)V
-Lsun/misc/JarIndex;->write(Ljava/io/OutputStream;)V
-Lsun/misc/MessageUtils;-><init>()V
-Lsun/misc/MetaIndex;->forJar(Ljava/io/File;)Lsun/misc/MetaIndex;
-Lsun/misc/MetaIndex;->registerDirectory(Ljava/io/File;)V
-Lsun/misc/VM;->maxDirectMemory()J
-Lsun/net/ftp/FtpClient;-><init>()V
-Lsun/net/util/IPAddressUtil;->isIPv4LiteralAddress(Ljava/lang/String;)Z
-Lsun/net/util/IPAddressUtil;->isIPv6LiteralAddress(Ljava/lang/String;)Z
-Lsun/net/www/MessageHeader;-><init>()V
-Lsun/net/www/MessageHeader;-><init>(Ljava/io/InputStream;)V
-Lsun/net/www/MessageHeader;->add(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/MessageHeader;->findValue(Ljava/lang/String;)Ljava/lang/String;
-Lsun/net/www/MessageHeader;->prepend(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/MessageHeader;->print(Ljava/io/PrintStream;)V
-Lsun/net/www/MessageHeader;->set(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/ParseUtil;->decode(Ljava/lang/String;)Ljava/lang/String;
-Lsun/net/www/ParseUtil;->encodePath(Ljava/lang/String;Z)Ljava/lang/String;
-Lsun/net/www/ParseUtil;->fileToEncodedURL(Ljava/io/File;)Ljava/net/URL;
-Lsun/net/www/URLConnection;-><init>(Ljava/net/URL;)V
-Lsun/net/www/URLConnection;->setProperties(Lsun/net/www/MessageHeader;)V
-Lsun/nio/ch/DirectBuffer;->address()J
-Lsun/nio/ch/FileChannelImpl;->unmap0(JJ)I
-Lsun/nio/ch/SelectorImpl;->publicSelectedKeys:Ljava/util/Set;
-Lsun/nio/ch/SelectorImpl;->selectedKeys:Ljava/util/Set;
-Lsun/nio/cs/HistoricallyNamedCharset;->historicalName()Ljava/lang/String;
-Lsun/nio/cs/ThreadLocalCoders;->decoderFor(Ljava/lang/Object;)Ljava/nio/charset/CharsetDecoder;
-Lsun/nio/fs/BasicFileAttributesHolder;->get()Ljava/nio/file/attribute/BasicFileAttributes;
-Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/Class;)V
-Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/String;)V
-Lsun/reflect/misc/ReflectUtil;->isPackageAccessible(Ljava/lang/Class;)Z
-Lsun/reflect/misc/ReflectUtil;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
-Lsun/reflect/Reflection;->ensureMemberAccess(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)V
-Lsun/reflect/Reflection;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
-Lsun/security/action/GetBooleanAction;-><init>(Ljava/lang/String;)V
-Lsun/security/action/GetIntegerAction;-><init>(Ljava/lang/String;I)V
-Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;)V
-Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/jca/GetInstance$Instance;->impl:Ljava/lang/Object;
-Lsun/security/jca/GetInstance$Instance;->provider:Ljava/security/Provider;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/security/Provider;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/JCAUtil;->getSecureRandom()Ljava/security/SecureRandom;
-Lsun/security/jca/ProviderConfig;->argument:Ljava/lang/String;
-Lsun/security/jca/ProviderConfig;->CL_STRING:[Ljava/lang/Class;
-Lsun/security/jca/ProviderConfig;->disableLoad()V
-Lsun/security/jca/ProviderConfig;->hasArgument()Z
-Lsun/security/jca/ProviderList;->getService(Ljava/lang/String;Ljava/lang/String;)Ljava/security/Provider$Service;
-Lsun/security/jca/Providers;->getProviderList()Lsun/security/jca/ProviderList;
-Lsun/security/jca/Providers;->startJarVerification()Ljava/lang/Object;
-Lsun/security/jca/Providers;->stopJarVerification(Ljava/lang/Object;)V
-Lsun/security/pkcs/ContentInfo;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
-Lsun/security/pkcs/ContentInfo;-><init>([B)V
-Lsun/security/pkcs/ContentInfo;->DATA_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/ContentInfo;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/pkcs/ContentInfo;->getData()[B
-Lsun/security/pkcs/ParsingException;-><init>(Ljava/lang/String;)V
-Lsun/security/pkcs/PKCS7;-><init>([B)V
-Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Ljava/security/cert/X509CRL;[Lsun/security/pkcs/SignerInfo;)V
-Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Lsun/security/pkcs/SignerInfo;)V
-Lsun/security/pkcs/PKCS7;->encodeSignedData(Ljava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS7;->getCertificates()[Ljava/security/cert/X509Certificate;
-Lsun/security/pkcs/PKCS7;->getContentInfo()Lsun/security/pkcs/ContentInfo;
-Lsun/security/pkcs/PKCS7;->getSignerInfos()[Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS7;->verify(Lsun/security/pkcs/SignerInfo;[B)Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS7;->verify([B)[Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS8Key;-><init>()V
-Lsun/security/pkcs/PKCS8Key;->algid:Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/PKCS8Key;->encodedKey:[B
-Lsun/security/pkcs/PKCS8Key;->key:[B
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/lang/Object;)V
-Lsun/security/pkcs/PKCS9Attribute;->CONTENT_TYPE_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->derEncode(Ljava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS9Attribute;->EMAIL_ADDRESS_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->getValue()Ljava/lang/Object;
-Lsun/security/pkcs/PKCS9Attribute;->MESSAGE_DIGEST_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->SIGNING_TIME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;Z)V
-Lsun/security/pkcs/PKCS9Attributes;-><init>([Lsun/security/pkcs/PKCS9Attribute;)V
-Lsun/security/pkcs/PKCS9Attributes;->encode(BLjava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS9Attributes;->getAttribute(Ljava/lang/String;)Lsun/security/pkcs/PKCS9Attribute;
-Lsun/security/pkcs/PKCS9Attributes;->getAttributeValue(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Object;
-Lsun/security/pkcs/PKCS9Attributes;->getDerEncoding()[B
-Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/PKCS9Attributes;Lsun/security/x509/AlgorithmId;[BLsun/security/pkcs/PKCS9Attributes;)V
-Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/x509/AlgorithmId;[B)V
-Lsun/security/pkcs/SignerInfo;->getCertificate(Lsun/security/pkcs/PKCS7;)Ljava/security/cert/X509Certificate;
-Lsun/security/pkcs/SignerInfo;->getCertificateChain(Lsun/security/pkcs/PKCS7;)Ljava/util/ArrayList;
-Lsun/security/pkcs/SignerInfo;->getDigestAlgorithmId()Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/SignerInfo;->getDigestEncryptionAlgorithmId()Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/SignerInfo;->getEncryptedDigest()[B
-Lsun/security/provider/certpath/X509CertificatePair;->clearCache()V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;)V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/util/List;)V
-Lsun/security/provider/certpath/X509CertPath;->certs:Ljava/util/List;
-Lsun/security/provider/certpath/X509CertPath;->getEncodingsStatic()Ljava/util/Iterator;
-Lsun/security/provider/X509Factory;->addToCache(Lsun/security/util/Cache;[BLjava/lang/Object;)V
-Lsun/security/provider/X509Factory;->certCache:Lsun/security/util/Cache;
-Lsun/security/provider/X509Factory;->crlCache:Lsun/security/util/Cache;
-Lsun/security/provider/X509Factory;->getFromCache(Lsun/security/util/Cache;[B)Ljava/lang/Object;
-Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509Certificate;)Lsun/security/x509/X509CertImpl;
-Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509CRL;)Lsun/security/x509/X509CRLImpl;
-Lsun/security/timestamp/TimestampToken;-><init>([B)V
-Lsun/security/timestamp/TimestampToken;->getDate()Ljava/util/Date;
-Lsun/security/timestamp/TimestampToken;->getHashAlgorithm()Lsun/security/x509/AlgorithmId;
-Lsun/security/timestamp/TimestampToken;->getHashedMessage()[B
-Lsun/security/timestamp/TimestampToken;->getNonce()Ljava/math/BigInteger;
-Lsun/security/util/BitArray;-><init>(I[B)V
-Lsun/security/util/BitArray;->toByteArray()[B
-Lsun/security/util/Cache;-><init>()V
-Lsun/security/util/Cache;->clear()V
-Lsun/security/util/Cache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Lsun/security/util/Cache;->newHardMemoryCache(I)Lsun/security/util/Cache;
-Lsun/security/util/Cache;->put(Ljava/lang/Object;Ljava/lang/Object;)V
-Lsun/security/util/Debug;->getInstance(Ljava/lang/String;)Lsun/security/util/Debug;
-Lsun/security/util/Debug;->println()V
-Lsun/security/util/Debug;->println(Ljava/lang/String;)V
-Lsun/security/util/Debug;->toHexString(Ljava/math/BigInteger;)Ljava/lang/String;
-Lsun/security/util/DerIndefLenConverter;-><init>()V
-Lsun/security/util/DerIndefLenConverter;->convert([B)[B
-Lsun/security/util/DerIndefLenConverter;->data:[B
-Lsun/security/util/DerIndefLenConverter;->dataPos:I
-Lsun/security/util/DerIndefLenConverter;->dataSize:I
-Lsun/security/util/DerIndefLenConverter;->isIndefinite(I)Z
-Lsun/security/util/DerIndefLenConverter;->newData:[B
-Lsun/security/util/DerIndefLenConverter;->numOfTotalLenBytes:I
-Lsun/security/util/DerIndefLenConverter;->parseLength()I
-Lsun/security/util/DerIndefLenConverter;->parseTag()V
-Lsun/security/util/DerIndefLenConverter;->parseValue(I)V
-Lsun/security/util/DerIndefLenConverter;->writeLengthAndValue()V
-Lsun/security/util/DerIndefLenConverter;->writeTag()V
-Lsun/security/util/DerInputStream;-><init>([B)V
-Lsun/security/util/DerInputStream;->available()I
-Lsun/security/util/DerInputStream;->getBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerInputStream;->getBitString()[B
-Lsun/security/util/DerInputStream;->getDerValue()Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getInteger()I
-Lsun/security/util/DerInputStream;->getOctetString()[B
-Lsun/security/util/DerInputStream;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/DerInputStream;->getSequence(I)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getSet(I)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getSet(IZ)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getUTCTime()Ljava/util/Date;
-Lsun/security/util/DerInputStream;->getUTF8String()Ljava/lang/String;
-Lsun/security/util/DerInputStream;->mark(I)V
-Lsun/security/util/DerInputStream;->peekByte()I
-Lsun/security/util/DerInputStream;->reset()V
-Lsun/security/util/DerInputStream;->subStream(IZ)Lsun/security/util/DerInputStream;
-Lsun/security/util/DerInputStream;->tag:B
-Lsun/security/util/DerOutputStream;-><init>()V
-Lsun/security/util/DerOutputStream;-><init>(I)V
-Lsun/security/util/DerOutputStream;->putBitString([B)V
-Lsun/security/util/DerOutputStream;->putBoolean(Z)V
-Lsun/security/util/DerOutputStream;->putDerValue(Lsun/security/util/DerValue;)V
-Lsun/security/util/DerOutputStream;->putIA5String(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->putInteger(I)V
-Lsun/security/util/DerOutputStream;->putInteger(Ljava/math/BigInteger;)V
-Lsun/security/util/DerOutputStream;->putNull()V
-Lsun/security/util/DerOutputStream;->putOctetString([B)V
-Lsun/security/util/DerOutputStream;->putOID(Lsun/security/util/ObjectIdentifier;)V
-Lsun/security/util/DerOutputStream;->putOrderedSetOf(B[Lsun/security/util/DerEncoder;)V
-Lsun/security/util/DerOutputStream;->putPrintableString(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->putSequence([Lsun/security/util/DerValue;)V
-Lsun/security/util/DerOutputStream;->putUTCTime(Ljava/util/Date;)V
-Lsun/security/util/DerOutputStream;->putUTF8String(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->write(BLsun/security/util/DerOutputStream;)V
-Lsun/security/util/DerOutputStream;->write(B[B)V
-Lsun/security/util/DerValue;-><init>(B[B)V
-Lsun/security/util/DerValue;-><init>(Ljava/io/InputStream;)V
-Lsun/security/util/DerValue;-><init>(Ljava/lang/String;)V
-Lsun/security/util/DerValue;-><init>([B)V
-Lsun/security/util/DerValue;-><init>([BII)V
-Lsun/security/util/DerValue;->buffer:Lsun/security/util/DerInputBuffer;
-Lsun/security/util/DerValue;->createTag(BZB)B
-Lsun/security/util/DerValue;->data:Lsun/security/util/DerInputStream;
-Lsun/security/util/DerValue;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/util/DerValue;->getAsString()Ljava/lang/String;
-Lsun/security/util/DerValue;->getBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerValue;->getBitString()[B
-Lsun/security/util/DerValue;->getData()Lsun/security/util/DerInputStream;
-Lsun/security/util/DerValue;->getDataBytes()[B
-Lsun/security/util/DerValue;->getOctetString()[B
-Lsun/security/util/DerValue;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/DerValue;->getPositiveBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerValue;->getUnalignedBitString()Lsun/security/util/BitArray;
-Lsun/security/util/DerValue;->isConstructed()Z
-Lsun/security/util/DerValue;->isContextSpecific()Z
-Lsun/security/util/DerValue;->isContextSpecific(B)Z
-Lsun/security/util/DerValue;->isPrintableStringChar(C)Z
-Lsun/security/util/DerValue;->resetTag(B)V
-Lsun/security/util/DerValue;->tag:B
-Lsun/security/util/DerValue;->toByteArray()[B
-Lsun/security/util/DerValue;->toDerInputStream()Lsun/security/util/DerInputStream;
-Lsun/security/util/ManifestDigester$Entry;->digest(Ljava/security/MessageDigest;)[B
-Lsun/security/util/ManifestDigester$Entry;->digestWorkaround(Ljava/security/MessageDigest;)[B
-Lsun/security/util/ManifestDigester;-><init>([B)V
-Lsun/security/util/ManifestDigester;->get(Ljava/lang/String;Z)Lsun/security/util/ManifestDigester$Entry;
-Lsun/security/util/ManifestDigester;->manifestDigest(Ljava/security/MessageDigest;)[B
-Lsun/security/util/MemoryCache$HardCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;J)V
-Lsun/security/util/MemoryCache$SoftCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;JLjava/lang/ref/ReferenceQueue;)V
-Lsun/security/util/ObjectIdentifier;-><init>(Ljava/lang/String;)V
-Lsun/security/util/ObjectIdentifier;-><init>([I)V
-Lsun/security/util/ObjectIdentifier;->equals(Lsun/security/util/ObjectIdentifier;)Z
-Lsun/security/util/ObjectIdentifier;->newInternal([I)Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/PropertyExpander;->expand(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/util/ResourcesMgr;->getString(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/util/SecurityConstants;->CREATE_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->GET_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->MODIFY_THREADGROUP_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->MODIFY_THREAD_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SignatureFileVerifier;->isBlockOrSF(Ljava/lang/String;)Z
-Lsun/security/x509/AccessDescription;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/AccessDescription;->getAccessLocation()Lsun/security/x509/GeneralName;
-Lsun/security/x509/AccessDescription;->getAccessMethod()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;-><init>()V
-Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;)V
-Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/security/AlgorithmParameters;)V
-Lsun/security/x509/AlgorithmId;->derEncode(Ljava/io/OutputStream;)V
-Lsun/security/x509/AlgorithmId;->DSA_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->EC_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->encode()[B
-Lsun/security/x509/AlgorithmId;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/AlgorithmId;->equals(Lsun/security/x509/AlgorithmId;)Z
-Lsun/security/x509/AlgorithmId;->getAlgorithmId(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->getDigAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/x509/AlgorithmId;->getEncAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/x509/AlgorithmId;->getEncodedParams()[B
-Lsun/security/x509/AlgorithmId;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->getParameters()Ljava/security/AlgorithmParameters;
-Lsun/security/x509/AlgorithmId;->MD2_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->MD5_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->params:Lsun/security/util/DerValue;
-Lsun/security/x509/AlgorithmId;->parse(Lsun/security/util/DerValue;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->RSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->sha1WithRSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA256_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA384_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA512_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AttributeNameEnumeration;-><init>()V
-Lsun/security/x509/AVA;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
-Lsun/security/x509/AVA;->getDerValue()Lsun/security/util/DerValue;
-Lsun/security/x509/AVA;->getObjectIdentifier()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVA;->getValueString()Ljava/lang/String;
-Lsun/security/x509/AVA;->toRFC2253CanonicalString()Ljava/lang/String;
-Lsun/security/x509/AVAComparator;->INSTANCE:Ljava/util/Comparator;
-Lsun/security/x509/AVAKeyword;->getOID(Ljava/lang/String;ILjava/util/Map;)Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVAKeyword;->isCompliant(I)Z
-Lsun/security/x509/AVAKeyword;->keyword:Ljava/lang/String;
-Lsun/security/x509/AVAKeyword;->keywordMap:Ljava/util/Map;
-Lsun/security/x509/AVAKeyword;->oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVAKeyword;->oidMap:Ljava/util/Map;
-Lsun/security/x509/CertificateAlgorithmId;-><init>(Lsun/security/x509/AlgorithmId;)V
-Lsun/security/x509/CertificateExtensions;-><init>()V
-Lsun/security/x509/CertificateExtensions;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/CertificateExtensions;->encode(Ljava/io/OutputStream;Z)V
-Lsun/security/x509/CertificateExtensions;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/CertificateExtensions;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/x509/CertificateIssuerName;-><init>(Lsun/security/x509/X500Name;)V
-Lsun/security/x509/CertificateSerialNumber;-><init>(I)V
-Lsun/security/x509/CertificateSerialNumber;-><init>(Ljava/math/BigInteger;)V
-Lsun/security/x509/CertificateSubjectName;-><init>(Lsun/security/x509/X500Name;)V
-Lsun/security/x509/CertificateSubjectName;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/CertificateValidity;-><init>(Ljava/util/Date;Ljava/util/Date;)V
-Lsun/security/x509/CertificateVersion;-><init>(I)V
-Lsun/security/x509/CertificateX509Key;-><init>(Ljava/security/PublicKey;)V
-Lsun/security/x509/CRLDistributionPointsExtension;->encodeThis()V
-Lsun/security/x509/CRLNumberExtension;-><init>(Ljava/lang/Boolean;Ljava/lang/Object;)V
-Lsun/security/x509/CRLNumberExtension;->encodeThis()V
-Lsun/security/x509/CRLNumberExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/Extension;-><init>(Lsun/security/x509/Extension;)V
-Lsun/security/x509/Extension;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/Extension;->getExtensionId()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/GeneralName;-><init>(Lsun/security/x509/GeneralNameInterface;)V
-Lsun/security/x509/GeneralName;->getName()Lsun/security/x509/GeneralNameInterface;
-Lsun/security/x509/GeneralName;->getType()I
-Lsun/security/x509/GeneralNames;-><init>()V
-Lsun/security/x509/GeneralNames;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/GeneralNames;->add(Lsun/security/x509/GeneralName;)Lsun/security/x509/GeneralNames;
-Lsun/security/x509/GeneralNames;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/GeneralNames;->isEmpty()Z
-Lsun/security/x509/KeyIdentifier;-><init>(Ljava/security/PublicKey;)V
-Lsun/security/x509/KeyIdentifier;->getIdentifier()[B
-Lsun/security/x509/KeyIdentifier;->octetString:[B
-Lsun/security/x509/KeyUsageExtension;-><init>([Z)V
-Lsun/security/x509/KeyUsageExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/NetscapeCertTypeExtension;-><init>([B)V
-Lsun/security/x509/NetscapeCertTypeExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/OIDMap$OIDInfo;->clazz:Ljava/lang/Class;
-Lsun/security/x509/OIDMap;->getClass(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Class;
-Lsun/security/x509/OIDMap;->nameMap:Ljava/util/Map;
-Lsun/security/x509/OIDMap;->oidMap:Ljava/util/Map;
-Lsun/security/x509/PKIXExtensions;->CertificateIssuer_Id:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/SerialNumber;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/SubjectAlternativeNameExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/SubjectKeyIdentifierExtension;-><init>([B)V
-Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/UniqueIdentity;->encode(Lsun/security/util/DerOutputStream;B)V
-Lsun/security/x509/URIName;->getName()Ljava/lang/String;
-Lsun/security/x509/URIName;->getScheme()Ljava/lang/String;
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X500Name;-><init>([B)V
-Lsun/security/x509/X500Name;->allAvas()Ljava/util/List;
-Lsun/security/x509/X500Name;->asX500Name(Ljavax/security/auth/x500/X500Principal;)Lsun/security/x509/X500Name;
-Lsun/security/x509/X500Name;->asX500Principal()Ljavax/security/auth/x500/X500Principal;
-Lsun/security/x509/X500Name;->commonName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->countryName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->DNQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->DOMAIN_COMPONENT_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/X500Name;->GENERATIONQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->getCommonName()Ljava/lang/String;
-Lsun/security/x509/X500Name;->GIVENNAME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->INITIALS_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->ipAddress_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->isEmpty()Z
-Lsun/security/x509/X500Name;->localityName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->orgName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->orgUnitName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->SERIALNUMBER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->stateName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->streetAddress_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->SURNAME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->title_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->userid_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/x509/X509CertInfo;)V
-Lsun/security/x509/X509CertImpl;-><init>([B)V
-Lsun/security/x509/X509CertImpl;->algId:Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/X509CertImpl;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/X509CertImpl;->getEncodedInternal()[B
-Lsun/security/x509/X509CertImpl;->parse(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CertImpl;->readOnly:Z
-Lsun/security/x509/X509CertImpl;->sign(Ljava/security/PrivateKey;Ljava/lang/String;)V
-Lsun/security/x509/X509CertImpl;->signature:[B
-Lsun/security/x509/X509CertImpl;->signedCert:[B
-Lsun/security/x509/X509CertInfo;-><init>()V
-Lsun/security/x509/X509CertInfo;-><init>([B)V
-Lsun/security/x509/X509CertInfo;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/X509CertInfo;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/x509/X509CRLEntryImpl;->getExtension(Lsun/security/util/ObjectIdentifier;)Lsun/security/x509/Extension;
-Lsun/security/x509/X509CRLImpl;-><init>(Ljava/io/InputStream;)V
-Lsun/security/x509/X509CRLImpl;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CRLImpl;-><init>([B)V
-Lsun/security/x509/X509CRLImpl;->getEncodedInternal()[B
-Lsun/security/x509/X509Key;-><init>()V
-Lsun/security/x509/X509Key;->algid:Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/X509Key;->encodedKey:[B
-Lsun/security/x509/X509Key;->key:[B
-Lsun/security/x509/X509Key;->parse(Lsun/security/util/DerValue;)Ljava/security/PublicKey;
-Lsun/security/x509/X509Key;->unusedBits:I
-Lsun/util/calendar/AbstractCalendar;->getDayOfWeekDateOnOrBefore(JI)J
-Lsun/util/calendar/AbstractCalendar;->getTimeOfDayValue(Lsun/util/calendar/CalendarDate;)J
-Lsun/util/calendar/BaseCalendar$Date;->getNormalizedYear()I
-Lsun/util/calendar/BaseCalendar$Date;->setNormalizedYear(I)V
-Lsun/util/calendar/CalendarDate;->getDayOfMonth()I
-Lsun/util/calendar/CalendarDate;->getMonth()I
-Lsun/util/calendar/CalendarDate;->getTimeOfDay()J
-Lsun/util/calendar/CalendarDate;->getYear()I
-Lsun/util/calendar/CalendarDate;->setDate(III)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setDayOfMonth(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setHours(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setMillis(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setMinutes(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setSeconds(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarSystem;->forName(Ljava/lang/String;)Lsun/util/calendar/CalendarSystem;
-Lsun/util/calendar/CalendarSystem;->getGregorianCalendar()Lsun/util/calendar/Gregorian;
-Lsun/util/calendar/CalendarSystem;->getTime(Lsun/util/calendar/CalendarDate;)J
-Lsun/util/calendar/CalendarSystem;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarSystem;->validate(Lsun/util/calendar/CalendarDate;)Z
-Lsun/util/calendar/CalendarUtils;->floorDivide(II)I
-Lsun/util/calendar/CalendarUtils;->floorDivide(JJ)J
-Lsun/util/calendar/CalendarUtils;->mod(II)I
-Lsun/util/calendar/CalendarUtils;->mod(JJ)J
-Lsun/util/calendar/Era;-><init>(Ljava/lang/String;Ljava/lang/String;JZ)V
-Lsun/util/calendar/Era;->getAbbreviation()Ljava/lang/String;
-Lsun/util/calendar/Era;->getName()Ljava/lang/String;
-Lsun/util/calendar/Era;->getSinceDate()Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/ImmutableGregorianDate;->unsupported()V
-Lsun/util/calendar/LocalGregorianCalendar$Date;->getNormalizedYear()I
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setEra(Lsun/util/calendar/Era;)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setNormalizedYear(I)V
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setYear(I)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar;->normalize(Lsun/util/calendar/CalendarDate;)Z
-Lsun/util/calendar/LocalGregorianCalendar;->validate(Lsun/util/calendar/CalendarDate;)Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
deleted file mode 100644
index 575ba34..0000000
--- a/config/hiddenapi-vendor-list.txt
+++ /dev/null
@@ -1,247 +0,0 @@
-Landroid/app/IActivityController$Stub;-><init>()V
-Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
-Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
-Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
-Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
-Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
-Landroid/app/IActivityManager;->getLockTaskModeState()I
-Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
-Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
-Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
-Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
-Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
-Landroid/app/IActivityManager;->removeTask(I)Z
-Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
-Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
-Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
-Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
-Landroid/app/IAlarmManager;->setTime(J)Z
-Landroid/app/IAssistDataReceiver$Stub;-><init>()V
-Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
-Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
-Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
-Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
-Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
-Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
-Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
-Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
-Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
-Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
-Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
-Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
-Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
-Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
-Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
-Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
-Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
-Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
-Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
-Landroid/location/INetInitiatedListener$Stub;-><init>()V
-Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
-Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
-Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
-Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
-Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
-Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
-Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
-Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo;
-Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
-Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
-Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
-Landroid/net/InterfaceConfiguration;-><init>()V
-Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange;
-Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V
-Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable;
-Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult;
-Landroid/os/AsyncResult;->result:Ljava/lang/Object;
-Landroid/os/AsyncResult;->userObj:Ljava/lang/Object;
-Landroid/os/BatteryStats$HistoryItem;-><init>()V
-Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B
-Landroid/os/BatteryStats$HistoryItem;->cmd:B
-Landroid/os/BatteryStats$HistoryItem;->states:I
-Landroid/os/BatteryStats$HistoryItem;->time:J
-Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
-Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
-Landroid/os/BatteryStats$Uid;-><init>()V
-Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
-Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
-Landroid/os/Broadcaster;-><init>()V
-Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
-Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
-Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
-Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
-Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
-Landroid/os/Handler;->getMain()Landroid/os/Handler;
-Landroid/os/HwBinder;->reportSyspropChanged()V
-Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
-Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
-Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
-Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
-Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
-Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
-Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
-Landroid/os/IPowerManager;->goToSleep(JII)V
-Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
-Landroid/os/IRemoteCallback$Stub;-><init>()V
-Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
-Landroid/os/Parcel;->readBlob()[B
-Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
-Landroid/os/Parcel;->writeBlob([B)V
-Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/Registrant;->clear()V
-Landroid/os/Registrant;->notifyRegistrant()V
-Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
-Landroid/os/RegistrantList;-><init>()V
-Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
-Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/RegistrantList;->notifyRegistrants()V
-Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
-Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
-Landroid/os/RegistrantList;->removeCleared()V
-Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
-Landroid/os/ServiceSpecificException;->errorCode:I
-Landroid/os/storage/StorageEventListener;-><init>()V
-Landroid/os/SystemProperties;->reportSyspropChanged()V
-Landroid/os/SystemService;->start(Ljava/lang/String;)V
-Landroid/os/SystemService;->stop(Ljava/lang/String;)V
-Landroid/os/SystemVibrator;-><init>()V
-Landroid/os/UserHandle;->isSameApp(II)Z
-Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
-Landroid/R$styleable;->CheckBoxPreference:[I
-Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
-Landroid/telephony/ims/compat/ImsService;-><init>()V
-Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
-Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
-Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
-Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
-Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
-Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
-Landroid/view/IRecentsAnimationController;->finish(Z)V
-Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot;
-Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V
-Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
-Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V
-Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
-Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V
-Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
-Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
-Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
-Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
-Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
-Landroid/view/IWindowManager;->endProlongedAnimations()V
-Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
-Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
-Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V
-Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V
-Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
-Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I
-Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService;
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService;
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
-Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
-Lcom/android/internal/content/PackageMonitor;-><init>()V
-Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->disable()V
-Lcom/android/internal/location/ILocationProvider;->enable()V
-Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
-Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
-Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
-Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
-Lcom/android/internal/R$styleable;->NumberPicker:[I
-Lcom/android/internal/R$styleable;->TwoLineListItem:[I
-Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 550e795..3095925 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4117,6 +4117,10 @@
com.android.internal.util.VirtualRefBasePtr
com.android.internal.util.XmlUtils
com.android.internal.util.XmlUtils$WriteMapCallback
+com.android.internal.util.function.NonaConsumer
+com.android.internal.util.function.NonaFunction
+com.android.internal.util.function.OctConsumer
+com.android.internal.util.function.OctFunction
com.android.internal.util.function.HeptConsumer
com.android.internal.util.function.HeptFunction
com.android.internal.util.function.HexConsumer
@@ -6172,6 +6176,9 @@
libcore.reflect.Types
libcore.reflect.WildcardTypeImpl
libcore.timezone.TimeZoneDataFiles
+libcore.timezone.ZoneInfoDB
+libcore.timezone.ZoneInfoDB$TzData
+libcore.timezone.ZoneInfoDB$TzData$1
libcore.util.BasicLruCache
libcore.util.CharsetUtils
libcore.util.CollectionUtils
@@ -6184,9 +6191,6 @@
libcore.util.ZoneInfo
libcore.util.ZoneInfo$CheckedArithmeticException
libcore.util.ZoneInfo$WallTime
-libcore.util.ZoneInfoDB
-libcore.util.ZoneInfoDB$TzData
-libcore.util.ZoneInfoDB$TzData$1
org.apache.harmony.dalvik.NativeTestTarget
org.apache.harmony.dalvik.ddmc.Chunk
org.apache.harmony.dalvik.ddmc.ChunkHandler
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 83fab7e..f3f065a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -122,7 +122,7 @@
import android.view.autofill.AutofillPopupWindow;
import android.view.autofill.IAutofillWindowPresenter;
import android.view.intelligence.ContentCaptureEvent;
-import android.view.intelligence.IntelligenceManager;
+import android.view.intelligence.ContentCaptureManager;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -824,9 +824,11 @@
/** The autofill manager. Always access via {@link #getAutofillManager()}. */
@Nullable private AutofillManager mAutofillManager;
- /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */
- @Nullable private IntelligenceManager mIntelligenceManager;
+ /** The content capture manager. Always access via {@link #getContentCaptureManager()}. */
+ @Nullable private ContentCaptureManager mContentCaptureManager;
+ private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
+ new ArrayList<Application.ActivityLifecycleCallbacks>();
static final class NonConfigurationInstances {
Object activity;
@@ -1014,39 +1016,39 @@
}
/**
- * (Creates, sets, and ) returns the intelligence manager
+ * (Creates, sets, and ) returns the content capture manager
*
- * @return The intelligence manager
+ * @return The content capture manager
*/
- @NonNull private IntelligenceManager getIntelligenceManager() {
- if (mIntelligenceManager == null) {
- mIntelligenceManager = getSystemService(IntelligenceManager.class);
+ @NonNull private ContentCaptureManager getContentCaptureManager() {
+ if (mContentCaptureManager == null) {
+ mContentCaptureManager = getSystemService(ContentCaptureManager.class);
}
- return mIntelligenceManager;
+ return mContentCaptureManager;
}
- private void notifyIntelligenceManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
- final IntelligenceManager im = getIntelligenceManager();
- if (im == null || !im.isContentCaptureEnabled()) {
+ private void notifyContentCaptureManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
+ final ContentCaptureManager cm = getContentCaptureManager();
+ if (cm == null || !cm.isContentCaptureEnabled()) {
return;
}
switch (event) {
case ContentCaptureEvent.TYPE_ACTIVITY_CREATED:
//TODO(b/111276913): decide whether the InteractionSessionId should be
// saved / restored in the activity bundle.
- im.onActivityCreated(mToken, getComponentName());
+ cm.onActivityCreated(mToken, getComponentName());
break;
case ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED:
- im.onActivityDestroyed();
+ cm.onActivityDestroyed();
break;
case ContentCaptureEvent.TYPE_ACTIVITY_STARTED:
case ContentCaptureEvent.TYPE_ACTIVITY_RESUMED:
case ContentCaptureEvent.TYPE_ACTIVITY_PAUSED:
case ContentCaptureEvent.TYPE_ACTIVITY_STOPPED:
- im.onActivityLifecycleEvent(event);
+ cm.onActivityLifecycleEvent(event);
break;
default:
- Log.w(TAG, "notifyIntelligenceManagerIfNeeded(): invalid type " + event);
+ Log.w(TAG, "notifyContentCaptureManagerIfNeeded(): invalid type " + event);
}
}
@@ -1055,6 +1057,7 @@
super.attachBaseContext(newBase);
if (newBase != null) {
newBase.setAutofillClient(this);
+ newBase.setContentCaptureSupported(true);
}
}
@@ -1064,6 +1067,294 @@
return this;
}
+ /** @hide */
+ @Override
+ public boolean isContentCaptureSupported() {
+ return true;
+ }
+
+ /**
+ * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives
+ * lifecycle callbacks for only this Activity.
+ * <p>
+ * In relation to any
+ * {@link Application#registerActivityLifecycleCallbacks Application registered callbacks},
+ * the callbacks registered here will always occur nested within those callbacks. This means:
+ * <ul>
+ * <li>Pre events will first be sent to Application registered callbacks, then to callbacks
+ * registered here.</li>
+ * <li>{@link Application.ActivityLifecycleCallbacks#onActivityCreated(Activity, Bundle)},
+ * {@link Application.ActivityLifecycleCallbacks#onActivityStarted(Activity)}, and
+ * {@link Application.ActivityLifecycleCallbacks#onActivityResumed(Activity)} will
+ * be sent first to Application registered callbacks, then to callbacks registered here.
+ * For all other events, callbacks registered here will be sent first.</li>
+ * <li>Post events will first be sent to callbacks registered here, then to
+ * Application registered callbacks.</li>
+ * </ul>
+ * <p>
+ * If multiple callbacks are registered here, they receive events in a first in (up through
+ * {@link Application.ActivityLifecycleCallbacks#onActivityPostResumed}, last out
+ * ordering.
+ * <p>
+ * It is strongly recommended to register this in the constructor of your Activity to ensure
+ * you get all available callbacks. As this callback is associated with only this Activity,
+ * it is not usually necessary to {@link #unregisterActivityLifecycleCallbacks unregister} it
+ * unless you specifically do not want to receive further lifecycle callbacks.
+ *
+ * @param callback The callback instance to register
+ */
+ public void registerActivityLifecycleCallbacks(
+ @NonNull Application.ActivityLifecycleCallbacks callback) {
+ synchronized (mActivityLifecycleCallbacks) {
+ mActivityLifecycleCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Unregister an {@link Application.ActivityLifecycleCallbacks} previously registered
+ * with {@link #registerActivityLifecycleCallbacks}. It will not receive any further
+ * callbacks.
+ *
+ * @param callback The callback instance to unregister
+ * @see #registerActivityLifecycleCallbacks
+ */
+ public void unregisterActivityLifecycleCallbacks(
+ @NonNull Application.ActivityLifecycleCallbacks callback) {
+ synchronized (mActivityLifecycleCallbacks) {
+ mActivityLifecycleCallbacks.remove(callback);
+ }
+ }
+
+ private void dispatchActivityPreCreated(@Nullable Bundle savedInstanceState) {
+ getApplication().dispatchActivityPreCreated(this, savedInstanceState);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreCreated(this,
+ savedInstanceState);
+ }
+ }
+ }
+
+ private void dispatchActivityCreated(@Nullable Bundle savedInstanceState) {
+ getApplication().dispatchActivityCreated(this, savedInstanceState);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this,
+ savedInstanceState);
+ }
+ }
+ }
+
+ private void dispatchActivityPostCreated(@Nullable Bundle savedInstanceState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostCreated(this,
+ savedInstanceState);
+ }
+ }
+ getApplication().dispatchActivityPostCreated(this, savedInstanceState);
+ }
+
+ private void dispatchActivityPreStarted() {
+ getApplication().dispatchActivityPreStarted(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStarted(this);
+ }
+ }
+ }
+
+ private void dispatchActivityStarted() {
+ getApplication().dispatchActivityStarted(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(this);
+ }
+ }
+ }
+
+ private void dispatchActivityPostStarted() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPostStarted(this);
+ }
+ }
+ getApplication().dispatchActivityPostStarted(this);
+ }
+
+ private void dispatchActivityPreResumed() {
+ getApplication().dispatchActivityPreResumed(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreResumed(this);
+ }
+ }
+ }
+
+ private void dispatchActivityResumed() {
+ getApplication().dispatchActivityResumed(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityResumed(this);
+ }
+ }
+ }
+
+ private void dispatchActivityPostResumed() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostResumed(this);
+ }
+ }
+ getApplication().dispatchActivityPostResumed(this);
+ }
+
+ private void dispatchActivityPrePaused() {
+ getApplication().dispatchActivityPrePaused(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPrePaused(this);
+ }
+ }
+ }
+
+ private void dispatchActivityPaused() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPaused(this);
+ }
+ }
+ getApplication().dispatchActivityPaused(this);
+ }
+
+ private void dispatchActivityPostPaused() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostPaused(this);
+ }
+ }
+ getApplication().dispatchActivityPostPaused(this);
+ }
+
+ private void dispatchActivityPreStopped() {
+ getApplication().dispatchActivityPreStopped(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStopped(this);
+ }
+ }
+ }
+
+ private void dispatchActivityStopped() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStopped(this);
+ }
+ }
+ getApplication().dispatchActivityStopped(this);
+ }
+
+ private void dispatchActivityPostStopped() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPostStopped(this);
+ }
+ }
+ getApplication().dispatchActivityPostStopped(this);
+ }
+
+ private void dispatchActivityPreSaveInstanceState(@NonNull Bundle outState) {
+ getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPreSaveInstanceState(this, outState);
+ }
+ }
+ }
+
+ private void dispatchActivitySaveInstanceState(@NonNull Bundle outState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivitySaveInstanceState(this, outState);
+ }
+ }
+ getApplication().dispatchActivitySaveInstanceState(this, outState);
+ }
+
+ private void dispatchActivityPostSaveInstanceState(@NonNull Bundle outState) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPostSaveInstanceState(this, outState);
+ }
+ }
+ getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+ }
+
+ private void dispatchActivityPreDestroyed() {
+ getApplication().dispatchActivityPreDestroyed(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPreDestroyed(this);
+ }
+ }
+ }
+
+ private void dispatchActivityDestroyed() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityDestroyed(this);
+ }
+ }
+ getApplication().dispatchActivityDestroyed(this);
+ }
+
+ private void dispatchActivityPostDestroyed() {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = callbacks.length - 1; i >= 0; i--) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityPostDestroyed(this);
+ }
+ }
+ getApplication().dispatchActivityPostDestroyed(this);
+ }
+
+ private Object[] collectActivityLifecycleCallbacks() {
+ Object[] callbacks = null;
+ synchronized (mActivityLifecycleCallbacks) {
+ if (mActivityLifecycleCallbacks.size() > 0) {
+ callbacks = mActivityLifecycleCallbacks.toArray();
+ }
+ }
+ return callbacks;
+ }
+
/**
* Called when the activity is starting. This is where most initialization
* should go: calling {@link #setContentView(int)} to inflate the
@@ -1119,14 +1410,14 @@
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
- getApplication().dispatchActivityCreated(this, savedInstanceState);
+ dispatchActivityCreated(savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mRestoredFromBundle = savedInstanceState != null;
mCalled = true;
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
}
/**
@@ -1355,12 +1646,12 @@
mFragments.doLoaderStart();
- getApplication().dispatchActivityStarted(this);
+ dispatchActivityStarted();
if (mAutoFillResetNeeded) {
getAutofillManager().onVisibleForAutofill();
}
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
}
/**
@@ -1426,7 +1717,7 @@
@CallSuper
protected void onResume() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
- getApplication().dispatchActivityResumed(this);
+ dispatchActivityResumed();
mActivityTransitionState.onResume(this, isTopOfTask());
enableAutofillCompatibilityIfNeeded();
if (mAutoFillResetNeeded) {
@@ -1451,7 +1742,7 @@
}
}
}
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
mCalled = true;
}
@@ -1642,13 +1933,13 @@
* @param outState The bundle to save the state to.
*/
final void performSaveInstanceState(@NonNull Bundle outState) {
- getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+ dispatchActivityPreSaveInstanceState(outState);
onSaveInstanceState(outState);
saveManagedDialogs(outState);
mActivityTransitionState.saveState(outState);
storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
- getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+ dispatchActivityPostSaveInstanceState(outState);
}
/**
@@ -1662,13 +1953,13 @@
*/
final void performSaveInstanceState(@NonNull Bundle outState,
@NonNull PersistableBundle outPersistentState) {
- getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+ dispatchActivityPreSaveInstanceState(outState);
onSaveInstanceState(outState, outPersistentState);
saveManagedDialogs(outState);
storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
", " + outPersistentState);
- getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+ dispatchActivityPostSaveInstanceState(outState);
}
/**
@@ -1731,7 +2022,7 @@
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
- getApplication().dispatchActivitySaveInstanceState(this, outState);
+ dispatchActivitySaveInstanceState(outState);
}
/**
@@ -1831,7 +2122,7 @@
@CallSuper
protected void onPause() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
- getApplication().dispatchActivityPaused(this);
+ dispatchActivityPaused();
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
@@ -1845,7 +2136,7 @@
mAutoFillIgnoreFirstResumePause = false;
}
}
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
mCalled = true;
}
@@ -2015,7 +2306,7 @@
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
mActivityTransitionState.onStop();
- getApplication().dispatchActivityStopped(this);
+ dispatchActivityStopped();
mTranslucentCallback = null;
mCalled = true;
@@ -2034,7 +2325,7 @@
getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
}
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
}
}
@@ -2104,9 +2395,9 @@
mActionBar.onDestroy();
}
- getApplication().dispatchActivityDestroyed(this);
+ dispatchActivityDestroyed();
- notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
+ notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
}
@@ -6522,7 +6813,7 @@
}
void dumpIntelligenceManager(String prefix, PrintWriter writer) {
- final IntelligenceManager im = getIntelligenceManager();
+ final ContentCaptureManager im = getContentCaptureManager();
if (im != null) {
im.dump(prefix, writer);
} else {
@@ -7284,7 +7575,7 @@
@UnsupportedAppUsage
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
- getApplication().dispatchActivityPreCreated(this, icicle);
+ dispatchActivityPreCreated(icicle);
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
@@ -7299,7 +7590,7 @@
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
- getApplication().dispatchActivityPostCreated(this, icicle);
+ dispatchActivityPostCreated(icicle);
}
final void performNewIntent(@NonNull Intent intent) {
@@ -7308,7 +7599,7 @@
}
final void performStart(String reason) {
- getApplication().dispatchActivityPreStarted(this);
+ dispatchActivityPreStarted();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
@@ -7351,7 +7642,7 @@
}
mActivityTransitionState.enterReady(this);
- getApplication().dispatchActivityPostStarted(this);
+ dispatchActivityPostStarted();
}
/**
@@ -7406,7 +7697,7 @@
}
final void performResume(boolean followedByPause, String reason) {
- getApplication().dispatchActivityPreResumed(this);
+ dispatchActivityPreResumed();
performRestart(true /* start */, reason);
mFragments.execPendingActions();
@@ -7456,11 +7747,11 @@
"Activity " + mComponent.toShortString() +
" did not call through to super.onPostResume()");
}
- getApplication().dispatchActivityPostResumed(this);
+ dispatchActivityPostResumed();
}
final void performPause() {
- getApplication().dispatchActivityPrePaused(this);
+ dispatchActivityPrePaused();
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
mCalled = false;
@@ -7473,7 +7764,7 @@
"Activity " + mComponent.toShortString() +
" did not call through to super.onPause()");
}
- getApplication().dispatchActivityPostPaused(this);
+ dispatchActivityPostPaused();
}
final void performUserLeaving() {
@@ -7489,7 +7780,7 @@
mCanEnterPictureInPicture = false;
if (!mStopped) {
- getApplication().dispatchActivityPreStopped(this);
+ dispatchActivityPreStopped();
if (mWindow != null) {
mWindow.closeAllPanels();
}
@@ -7524,13 +7815,13 @@
}
mStopped = true;
- getApplication().dispatchActivityPostStopped(this);
+ dispatchActivityPostStopped();
}
mResumed = false;
}
final void performDestroy() {
- getApplication().dispatchActivityPreDestroyed(this);
+ dispatchActivityPreDestroyed();
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
@@ -7540,7 +7831,7 @@
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
- getApplication().dispatchActivityPostDestroyed(this);
+ dispatchActivityPostDestroyed();
}
final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode,
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1edd7f5..af3da0c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -243,6 +243,8 @@
public abstract void ensureBootCompleted();
public abstract void updateOomLevelsForDisplay(int displayId);
public abstract boolean isActivityStartsLoggingEnabled();
+ /** Returns true if the background activity starts is enabled. */
+ public abstract boolean isBackgroundActivityStartsEnabled();
public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
/** Input dispatch timeout to a window, start the ANR process. */
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 56ccf6f..6fdf7c8 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -433,4 +433,18 @@
}
return sb.toString();
}
+
+ /**
+ * Clears launch params for the given package.
+ * @param packageNames the names of the packages of which the launch params are to be cleared
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public void clearLaunchParamsForPackages(List<String> packageNames) {
+ try {
+ getService().clearLaunchParamsForPackages(packageNames);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index c879db8..2c435a2 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -31,10 +31,11 @@
import android.util.Log;
import android.view.IWindowManager;
import android.view.InputDevice;
-import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.SurfaceControl;
import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -59,12 +60,16 @@
private VirtualDisplay mVirtualDisplay;
private final SurfaceView mSurfaceView;
- private Surface mSurface;
+
+ /**
+ * This is the root surface for the VirtualDisplay. The VirtualDisplay child surfaces will be
+ * re-parented to this surface. This will also be a child of the SurfaceView's SurfaceControl.
+ */
+ private SurfaceControl mRootSurfaceControl;
private final SurfaceCallback mSurfaceCallback;
private StateCallback mActivityViewCallback;
- private IActivityManager mActivityManager;
private IActivityTaskManager mActivityTaskManager;
private IInputForwarder mInputForwarder;
// Temp container to store view coordinates on screen.
@@ -75,6 +80,9 @@
private final CloseGuard mGuard = CloseGuard.get();
private boolean mOpened; // Protected by mGuard.
+ private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+ private Surface mTmpSurface = new Surface();
+
@UnsupportedAppUsage
public ActivityView(Context context) {
this(context, null /* attrs */);
@@ -87,7 +95,6 @@
public ActivityView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mActivityManager = ActivityManager.getService();
mActivityTaskManager = ActivityTaskManager.getService();
mSurfaceView = new SurfaceView(context);
mSurfaceCallback = new SurfaceCallback();
@@ -283,9 +290,14 @@
return super.onGenericMotionEvent(event);
}
- private boolean injectInputEvent(InputEvent event) {
+ private boolean injectInputEvent(MotionEvent event) {
if (mInputForwarder != null) {
try {
+ // The touch event that the ActivityView gets is in View space, but the event needs
+ // to get forwarded in screen space. This offsets the touch event by the location
+ // the ActivityView is on screen and sends it to the input forwarder.
+ getLocationOnScreen(mLocationOnScreen);
+ event.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
return mInputForwarder.forwardEvent(event);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
@@ -297,14 +309,19 @@
private class SurfaceCallback implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
- mSurface = mSurfaceView.getHolder().getSurface();
+ mTmpSurface = new Surface();
if (mVirtualDisplay == null) {
- initVirtualDisplay();
+ initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
if (mVirtualDisplay != null && mActivityViewCallback != null) {
mActivityViewCallback.onActivityViewReady(ActivityView.this);
}
} else {
- mVirtualDisplay.setSurface(surfaceHolder.getSurface());
+ // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
+ // whether it has a surface. Setting a fake surface here so DisplayManager will
+ // consider this display on.
+ mVirtualDisplay.setSurface(mTmpSurface);
+ mTmpTransaction.reparent(mRootSurfaceControl,
+ mSurfaceView.getSurfaceControl().getHandle()).apply();
}
updateLocation();
}
@@ -319,8 +336,8 @@
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
- mSurface.release();
- mSurface = null;
+ mTmpSurface.release();
+ mTmpSurface = null;
if (mVirtualDisplay != null) {
mVirtualDisplay.setSurface(null);
}
@@ -328,7 +345,7 @@
}
}
- private void initVirtualDisplay() {
+ private void initVirtualDisplay(SurfaceSession surfaceSession) {
if (mVirtualDisplay != null) {
throw new IllegalStateException("Trying to initialize for the second time.");
}
@@ -336,9 +353,13 @@
final int width = mSurfaceView.getWidth();
final int height = mSurfaceView.getHeight();
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+ // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
+ // whether it has a surface. Setting a fake surface here so DisplayManager will consider
+ // this display on.
mVirtualDisplay = displayManager.createVirtualDisplay(
DISPLAY_NAME + "@" + System.identityHashCode(this),
- width, height, getBaseDisplayDensity(), mSurface,
+ width, height, getBaseDisplayDensity(), mTmpSurface,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
| DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
if (mVirtualDisplay == null) {
@@ -348,11 +369,20 @@
final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+
+ mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+ .setContainerLayer(true)
+ .setName(DISPLAY_NAME)
+ .build();
+
try {
+ wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
wm.dontOverrideDisplayInfo(displayId);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
+
+ mTmpTransaction.show(mRootSurfaceControl).apply();
mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
mTaskStackListener = new TaskStackListenerImpl();
try {
@@ -392,9 +422,9 @@
displayReleased = false;
}
- if (mSurface != null) {
- mSurface.release();
- mSurface = null;
+ if (mTmpSurface != null) {
+ mTmpSurface.release();
+ mTmpSurface = null;
}
if (displayReleased && mActivityViewCallback != null) {
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 9b13420..2f0f14aa 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -35,7 +35,7 @@
import android.util.Log;
import android.util.proto.ProtoOutputStream;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
import java.io.IOException;
import java.lang.annotation.Retention;
diff --git a/core/java/android/app/AppDetailsActivity.java b/core/java/android/app/AppDetailsActivity.java
index cd36e63..b71af88 100644
--- a/core/java/android/app/AppDetailsActivity.java
+++ b/core/java/android/app/AppDetailsActivity.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.TestApi;
import android.content.Intent;
import android.os.Bundle;
@@ -24,7 +25,9 @@
*
* @hide
*/
+@TestApi
public class AppDetailsActivity extends Activity {
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8a797dc..7312b2c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2981,4 +2981,13 @@
throw e.rethrowAsRuntimeException();
}
}
+
+ @Override
+ public void sendDeviceCustomizationReadyBroadcast() {
+ try {
+ mPM.sendDeviceCustomizationReadyBroadcast();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 28ecb27..6f0b6c8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -217,6 +217,8 @@
private AutofillClient mAutofillClient = null;
private boolean mIsAutofillCompatEnabled;
+ private boolean mIsContentCaptureSupported = false;
+
private final Object mSync = new Object();
@GuardedBy("mSync")
@@ -2376,6 +2378,18 @@
mIsAutofillCompatEnabled = autofillCompatEnabled;
}
+ /** @hide */
+ @Override
+ public boolean isContentCaptureSupported() {
+ return mIsContentCaptureSupported;
+ }
+
+ /** @hide */
+ @Override
+ public void setContentCaptureSupported(boolean supported) {
+ mIsContentCaptureSupported = supported;
+ }
+
@UnsupportedAppUsage
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e83bcd0..88fb025 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -96,6 +96,7 @@
String callingPackage);
void unregisterUidObserver(in IUidObserver observer);
boolean isUidActive(int uid, String callingPackage);
+ int getUidProcessState(int uid, in String callingPackage);
// =============== End of transactions used on native side as well ============================
// Special low-level communication with activity manager.
@@ -379,8 +380,6 @@
void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
int getPackageProcessState(in String packageName, in String callingPackage);
void updateDeviceOwner(in String packageName);
- int getUidProcessState(int uid, in String callingPackage);
-
// Start of N transactions
// Start Binder transaction tracking for all applications.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 09b77d5..777a494 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -445,4 +445,9 @@
void setPackageScreenCompatMode(in String packageName, int mode);
boolean getPackageAskScreenCompat(in String packageName);
void setPackageAskScreenCompat(in String packageName, boolean ask);
+
+ /**
+ * Clears launch params for given packages.
+ */
+ void clearLaunchParamsForPackages(in List<String> packageNames);
}
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index ce88809..e116d98 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -43,8 +43,6 @@
*/
void onUidIdle(int uid, boolean disabled);
- // =============== End of transactions used on native side as well ============================
-
/**
* General report of a state change of an uid.
*
@@ -55,6 +53,8 @@
*/
void onUidStateChanged(int uid, int procState, long procStateSeq);
+ // =============== End of transactions used on native side as well ============================
+
/**
* Report when the cached state of a uid has changed.
* If true, a uid has become cached -- that is, it has some active processes that are
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 00547b4..3a2038d 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -159,5 +159,5 @@
/**
* Called from SystemUI when it shows the AoD UI.
*/
- oneway void setInAmbientMode(boolean inAmbientMode, boolean animated);
+ oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 3f10754..719bba0 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -696,6 +696,23 @@
return loaders;
}
+ private StrictMode.ThreadPolicy allowThreadDiskReads() {
+ if (mActivityThread == null) {
+ // When LoadedApk is used without an ActivityThread (usually in a
+ // zygote context), don't call into StrictMode, as it initializes
+ // the binder subsystem, which we don't want.
+ return null;
+ }
+
+ return StrictMode.allowThreadDiskReads();
+ }
+
+ private void setThreadPolicy(StrictMode.ThreadPolicy policy) {
+ if (mActivityThread != null && policy != null) {
+ StrictMode.setThreadPolicy(policy);
+ }
+ }
+
private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
if (mPackageName.equals("android")) {
// Note: This branch is taken for system server and we don't need to setup
@@ -718,8 +735,11 @@
// Avoid the binder call when the package is the current application package.
// The activity manager will perform ensure that dexopt is performed before
- // spinning up the process.
- if (!Objects.equals(mPackageName, ActivityThread.currentPackageName()) && mIncludeCode) {
+ // spinning up the process. Similarly, don't call into binder when we don't
+ // have an ActivityThread object.
+ if (mActivityThread != null
+ && !Objects.equals(mPackageName, ActivityThread.currentPackageName())
+ && mIncludeCode) {
try {
ActivityThread.getPackageManager().notifyPackageUse(mPackageName,
PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE);
@@ -790,12 +810,12 @@
// mIncludeCode == false).
if (!mIncludeCode) {
if (mDefaultClassLoader == null) {
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(
"" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
librarySearchPath, libraryPermittedPath, mBaseClassLoader,
null /* classLoaderName */);
- StrictMode.setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldPolicy);
mAppComponentFactory = AppComponentFactory.DEFAULT;
}
@@ -822,7 +842,7 @@
if (mDefaultClassLoader == null) {
// Temporarily disable logging of disk reads on the Looper thread
// as this is early and necessary.
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
@@ -834,18 +854,18 @@
mApplicationInfo.classLoaderName, sharedLibraries);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
- StrictMode.setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldPolicy);
// Setup the class loader paths for profiling.
needToSetupJitProfiles = true;
}
if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) {
// Temporarily disable logging of disk reads on the Looper thread as this is necessary
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
try {
ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, libPaths);
} finally {
- StrictMode.setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldPolicy);
}
}
@@ -879,11 +899,11 @@
extraLibPaths.add("/product/lib" + abiSuffix);
}
if (!extraLibPaths.isEmpty()) {
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
try {
ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, extraLibPaths);
} finally {
- StrictMode.setThreadPolicy(oldPolicy);
+ setThreadPolicy(oldPolicy);
}
}
diff --git a/core/java/android/app/Notification.aidl b/core/java/android/app/Notification.aidl
index 9d8129c..8a7156e 100644
--- a/core/java/android/app/Notification.aidl
+++ b/core/java/android/app/Notification.aidl
@@ -17,3 +17,4 @@
package android.app;
parcelable Notification;
+parcelable Notification.Action;
\ No newline at end of file
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 75b56f3..aa1b5af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1693,11 +1693,30 @@
}
/**
+ * Throws an NPE if we are building a contextual action missing one of the fields
+ * necessary to display the action.
+ */
+ private void checkContextualActionNullFields() {
+ if (mSemanticAction != SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) return;
+
+ if (mIcon == null) {
+ throw new NullPointerException("Contextual Actions must contain a valid icon");
+ }
+
+ if (mIntent == null) {
+ throw new NullPointerException(
+ "Contextual Actions must contain a valid PendingIntent");
+ }
+ }
+
+ /**
* Combine all of the options that have been set and return a new {@link Action}
* object.
* @return the built action
*/
public Action build() {
+ checkContextualActionNullFields();
+
ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
RemoteInput[] previousDataInputs =
(RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
@@ -3848,6 +3867,9 @@
* The system UI may choose to display a heads-up notification, instead of
* launching this intent, while the user is using the device.
* </p>
+ * <p>Apps targeting {@link Build.VERSION_CODES#Q} and above will have to request
+ * a permission ({@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}) in order to
+ * use full screen intents.</p>
*
* @param intent The pending intent to launch.
* @param highPriority Passing true will cause this notification to be sent
@@ -4436,19 +4458,27 @@
return bitmap;
}
- private void bindProfileBadge(RemoteViews contentView) {
+ private void bindProfileBadge(RemoteViews contentView, StandardTemplateParams p) {
Bitmap profileBadge = getProfileBadge();
if (profileBadge != null) {
contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
- if (isColorized()) {
+ if (isColorized(p)) {
contentView.setDrawableTint(R.id.profile_badge, false,
- getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP);
+ getPrimaryTextColor(p), PorterDuff.Mode.SRC_ATOP);
}
}
}
+ private void bindAlertedIcon(RemoteViews contentView, StandardTemplateParams p) {
+ contentView.setDrawableTint(
+ R.id.alerted_icon,
+ false /* targetBackground */,
+ getNeutralColor(p),
+ PorterDuff.Mode.SRC_ATOP);
+ }
+
/**
* @hide
*/
@@ -4507,16 +4537,6 @@
result);
}
- /**
- * @param hasProgress whether the progress bar should be shown and set
- * @param result
- */
- private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
- TemplateBindResult result) {
- return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
- .fillTextsFrom(this), result);
- }
-
private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
TemplateBindResult result) {
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
@@ -4524,15 +4544,15 @@
resetStandardTemplate(contentView);
final Bundle ex = mN.extras;
- updateBackgroundColor(contentView);
- bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
+ updateBackgroundColor(contentView, p);
+ bindNotificationHeader(contentView, p);
bindLargeIconAndReply(contentView, p, result);
- boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
+ boolean showProgress = handleProgressBar(contentView, ex, p);
if (p.title != null) {
contentView.setViewVisibility(R.id.title, View.VISIBLE);
contentView.setTextViewText(R.id.title, processTextSpans(p.title));
if (!p.ambient) {
- setTextViewColorPrimary(contentView, R.id.title);
+ setTextViewColorPrimary(contentView, R.id.title, p);
}
contentView.setViewLayoutWidth(R.id.title, showProgress
? ViewGroup.LayoutParams.WRAP_CONTENT
@@ -4543,7 +4563,7 @@
: com.android.internal.R.id.text;
contentView.setTextViewText(textId, processTextSpans(p.text));
if (!p.ambient) {
- setTextViewColorSecondary(contentView, textId);
+ setTextViewColorSecondary(contentView, textId, p);
}
contentView.setViewVisibility(textId, View.VISIBLE);
}
@@ -4560,8 +4580,9 @@
return text;
}
- private void setTextViewColorPrimary(RemoteViews contentView, int id) {
- ensureColors();
+ private void setTextViewColorPrimary(RemoteViews contentView, int id,
+ StandardTemplateParams p) {
+ ensureColors(p);
contentView.setTextColor(id, mPrimaryTextColor);
}
@@ -4570,42 +4591,63 @@
}
/**
- * @return the primary text color
+ * Return the primary text color using the existing template params
* @hide
*/
@VisibleForTesting
public int getPrimaryTextColor() {
- ensureColors();
+ return getPrimaryTextColor(mParams);
+ }
+
+ /**
+ * @param p the template params to inflate this with
+ * @return the primary text color
+ * @hide
+ */
+ @VisibleForTesting
+ public int getPrimaryTextColor(StandardTemplateParams p) {
+ ensureColors(p);
return mPrimaryTextColor;
}
/**
- * @return the secondary text color
+ * Return the secondary text color using the existing template params
* @hide
*/
@VisibleForTesting
public int getSecondaryTextColor() {
- ensureColors();
+ return getSecondaryTextColor(mParams);
+ }
+
+ /**
+ * @param p the template params to inflate this with
+ * @return the secondary text color
+ * @hide
+ */
+ @VisibleForTesting
+ public int getSecondaryTextColor(StandardTemplateParams p) {
+ ensureColors(p);
return mSecondaryTextColor;
}
- private void setTextViewColorSecondary(RemoteViews contentView, int id) {
- ensureColors();
+ private void setTextViewColorSecondary(RemoteViews contentView, int id,
+ StandardTemplateParams p) {
+ ensureColors(p);
contentView.setTextColor(id, mSecondaryTextColor);
}
- private void ensureColors() {
- int backgroundColor = getBackgroundColor();
+ private void ensureColors(StandardTemplateParams p) {
+ int backgroundColor = getBackgroundColor(p);
if (mPrimaryTextColor == COLOR_INVALID
|| mSecondaryTextColor == COLOR_INVALID
|| mTextColorsAreForBackground != backgroundColor) {
mTextColorsAreForBackground = backgroundColor;
- if (!hasForegroundColor() || !isColorized()) {
+ if (!hasForegroundColor() || !isColorized(p)) {
mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
backgroundColor, mInNightMode);
mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
backgroundColor, mInNightMode);
- if (backgroundColor != COLOR_DEFAULT && isColorized()) {
+ if (backgroundColor != COLOR_DEFAULT && isColorized(p)) {
mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
mPrimaryTextColor, backgroundColor, 4.5);
mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
@@ -4673,10 +4715,11 @@
}
}
- private void updateBackgroundColor(RemoteViews contentView) {
- if (isColorized()) {
+ private void updateBackgroundColor(RemoteViews contentView,
+ StandardTemplateParams p) {
+ if (isColorized(p)) {
contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
- getBackgroundColor());
+ getBackgroundColor(p));
} else {
// Clear it!
contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
@@ -4699,19 +4742,20 @@
remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
}
- private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
+ private boolean handleProgressBar(RemoteViews contentView, Bundle ex,
+ StandardTemplateParams p) {
final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
final int progress = ex.getInt(EXTRA_PROGRESS, 0);
final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
- if (hasProgress && (max != 0 || ind)) {
+ if (p.hasProgress && (max != 0 || ind)) {
contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
contentView.setProgressBar(
R.id.progress, max, progress, ind);
contentView.setProgressBackgroundTintList(
R.id.progress, ColorStateList.valueOf(mContext.getColor(
R.color.notification_progress_background_color)));
- if (mN.color != COLOR_DEFAULT) {
- ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
+ if (getRawColor(p) != COLOR_DEFAULT) {
+ ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor(p));
contentView.setProgressTintList(R.id.progress, colorStateList);
contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
}
@@ -4724,8 +4768,8 @@
private void bindLargeIconAndReply(RemoteViews contentView, StandardTemplateParams p,
TemplateBindResult result) {
- boolean largeIconShown = bindLargeIcon(contentView, p.hideLargeIcon || p.ambient);
- boolean replyIconShown = bindReplyIcon(contentView, p.hideReplyIcon || p.ambient);
+ boolean largeIconShown = bindLargeIcon(contentView, p);
+ boolean replyIconShown = bindReplyIcon(contentView, p);
contentView.setViewVisibility(R.id.right_icon_container,
largeIconShown || replyIconShown ? View.VISIBLE : View.GONE);
int marginEnd = calculateMarginEnd(largeIconShown, replyIconShown);
@@ -4773,15 +4817,15 @@
* Bind the large icon.
* @return if the largeIcon is visible
*/
- private boolean bindLargeIcon(RemoteViews contentView, boolean hideLargeIcon) {
+ private boolean bindLargeIcon(RemoteViews contentView, StandardTemplateParams p) {
if (mN.mLargeIcon == null && mN.largeIcon != null) {
mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
}
- boolean showLargeIcon = mN.mLargeIcon != null && !hideLargeIcon;
+ boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon && !p.ambient;
if (showLargeIcon) {
contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
- processLargeLegacyIcon(mN.mLargeIcon, contentView);
+ processLargeLegacyIcon(mN.mLargeIcon, contentView, p);
}
return showLargeIcon;
}
@@ -4790,8 +4834,8 @@
* Bind the reply icon.
* @return if the reply icon is visible
*/
- private boolean bindReplyIcon(RemoteViews contentView, boolean hideReplyIcon) {
- boolean actionVisible = !hideReplyIcon;
+ private boolean bindReplyIcon(RemoteViews contentView, StandardTemplateParams p) {
+ boolean actionVisible = !p.hideReplyIcon && !p.ambient;
Action action = null;
if (actionVisible) {
action = findReplyAction();
@@ -4801,7 +4845,7 @@
contentView.setViewVisibility(R.id.reply_icon_action, View.VISIBLE);
contentView.setDrawableTint(R.id.reply_icon_action,
false /* targetBackground */,
- getNeutralColor(),
+ getNeutralColor(p),
PorterDuff.Mode.SRC_ATOP);
contentView.setOnClickPendingIntent(R.id.reply_icon_action, action.actionIntent);
contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
@@ -4828,41 +4872,42 @@
return null;
}
- private void bindNotificationHeader(RemoteViews contentView, boolean ambient,
- CharSequence secondaryHeaderText) {
- bindSmallIcon(contentView, ambient);
- bindHeaderAppName(contentView, ambient);
- if (!ambient) {
+ private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) {
+ bindSmallIcon(contentView, p);
+ bindHeaderAppName(contentView, p);
+ if (!p.ambient) {
// Ambient view does not have these
- bindHeaderText(contentView);
- bindHeaderTextSecondary(contentView, secondaryHeaderText);
- bindHeaderChronometerAndTime(contentView);
- bindProfileBadge(contentView);
+ bindHeaderText(contentView, p);
+ bindHeaderTextSecondary(contentView, p);
+ bindHeaderChronometerAndTime(contentView, p);
+ bindProfileBadge(contentView, p);
+ bindAlertedIcon(contentView, p);
}
- bindActivePermissions(contentView, ambient);
- bindExpandButton(contentView);
+ bindActivePermissions(contentView, p);
+ bindExpandButton(contentView, p);
mN.mUsesStandardHeader = true;
}
- private void bindActivePermissions(RemoteViews contentView, boolean ambient) {
- int color = ambient ? resolveAmbientColor() : getNeutralColor();
+ private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
+ int color = p.ambient ? resolveAmbientColor(p) : getNeutralColor(p);
contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
}
- private void bindExpandButton(RemoteViews contentView) {
- int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
+ private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
+ int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
contentView.setDrawableTint(R.id.expand_button, false, color,
PorterDuff.Mode.SRC_ATOP);
contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
color);
}
- private void bindHeaderChronometerAndTime(RemoteViews contentView) {
+ private void bindHeaderChronometerAndTime(RemoteViews contentView,
+ StandardTemplateParams p) {
if (showsTimeOrChronometer()) {
contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
- setTextViewColorSecondary(contentView, R.id.time_divider);
+ setTextViewColorSecondary(contentView, R.id.time_divider, p);
if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
contentView.setLong(R.id.chronometer, "setBase",
@@ -4870,11 +4915,11 @@
contentView.setBoolean(R.id.chronometer, "setStarted", true);
boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
contentView.setChronometerCountDown(R.id.chronometer, countsDown);
- setTextViewColorSecondary(contentView, R.id.chronometer);
+ setTextViewColorSecondary(contentView, R.id.chronometer, p);
} else {
contentView.setViewVisibility(R.id.time, View.VISIBLE);
contentView.setLong(R.id.time, "setTime", mN.when);
- setTextViewColorSecondary(contentView, R.id.time);
+ setTextViewColorSecondary(contentView, R.id.time, p);
}
} else {
// We still want a time to be set but gone, such that we can show and hide it
@@ -4883,36 +4928,36 @@
}
}
- private void bindHeaderText(RemoteViews contentView) {
- CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
- if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
+ private void bindHeaderText(RemoteViews contentView, StandardTemplateParams p) {
+ CharSequence summaryText = p.summaryText;
+ if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet
&& mStyle.hasSummaryInHeader()) {
- headerText = mStyle.mSummaryText;
+ summaryText = mStyle.mSummaryText;
}
- if (headerText == null
+ if (summaryText == null
&& mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
&& mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
- headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
+ summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
}
- if (headerText != null) {
+ if (summaryText != null) {
// TODO: Remove the span entirely to only have the string with propper formating.
contentView.setTextViewText(R.id.header_text, processTextSpans(
- processLegacyText(headerText)));
- setTextViewColorSecondary(contentView, R.id.header_text);
+ processLegacyText(summaryText)));
+ setTextViewColorSecondary(contentView, R.id.header_text, p);
contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
- setTextViewColorSecondary(contentView, R.id.header_text_divider);
+ setTextViewColorSecondary(contentView, R.id.header_text_divider, p);
}
}
- private void bindHeaderTextSecondary(RemoteViews contentView, CharSequence secondaryText) {
- if (!TextUtils.isEmpty(secondaryText)) {
+ private void bindHeaderTextSecondary(RemoteViews contentView, StandardTemplateParams p) {
+ if (!TextUtils.isEmpty(p.headerTextSecondary)) {
contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
- processLegacyText(secondaryText)));
- setTextViewColorSecondary(contentView, R.id.header_text_secondary);
+ processLegacyText(p.headerTextSecondary)));
+ setTextViewColorSecondary(contentView, R.id.header_text_secondary, p);
contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
contentView.setViewVisibility(R.id.header_text_secondary_divider, View.VISIBLE);
- setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider);
+ setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider, p);
}
}
@@ -4950,23 +4995,27 @@
return String.valueOf(name);
}
- private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
+ private void bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) {
contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
- if (isColorized() && !ambient) {
- setTextViewColorPrimary(contentView, R.id.app_name_text);
+ if (isColorized(p)) {
+ setTextViewColorPrimary(contentView, R.id.app_name_text, p);
} else {
contentView.setTextColor(R.id.app_name_text,
- ambient ? resolveAmbientColor() : getSecondaryTextColor());
+ p.ambient ? resolveAmbientColor(p) : getSecondaryTextColor(p));
}
}
- private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
+ private boolean isColorized(StandardTemplateParams p) {
+ return p.allowColorization && !p.ambient && mN.isColorized();
+ }
+
+ private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
if (mN.mSmallIcon == null && mN.icon != 0) {
mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
}
contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
- processSmallIconColor(mN.mSmallIcon, contentView, ambient);
+ processSmallIconColor(mN.mSmallIcon, contentView, p);
}
/**
@@ -5041,8 +5090,7 @@
boolean actionHasValidInput = hasValidRemoteInput(action);
validRemoteInput |= actionHasValidInput;
- final RemoteViews button = generateActionButton(action, emphazisedMode,
- p.ambient);
+ final RemoteViews button = generateActionButton(action, emphazisedMode, p);
if (actionHasValidInput && !emphazisedMode) {
// Clear the drawable
button.setInt(R.id.action0, "setBackgroundResource", 0);
@@ -5063,20 +5111,20 @@
View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_1,
processTextSpans(replyText[0]));
- setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
+ setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
big.setViewVisibility(R.id.notification_material_reply_progress,
showSpinner ? View.VISIBLE : View.GONE);
big.setProgressIndeterminateTintList(
R.id.notification_material_reply_progress,
ColorStateList.valueOf(
- isColorized() ? getPrimaryTextColor() : resolveContrastColor()));
+ isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p)));
if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])
&& p.maxRemoteInputHistory > 1) {
big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_2,
processTextSpans(replyText[1]));
- setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
+ setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])
&& p.maxRemoteInputHistory > 2) {
@@ -5084,7 +5132,7 @@
R.id.notification_material_reply_text_3, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_3,
processTextSpans(replyText[2]));
- setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
+ setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
}
}
}
@@ -5175,18 +5223,23 @@
* @hide
*/
public RemoteViews makeNotificationHeader(boolean ambient) {
- Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
- mN.extras.putBoolean(EXTRA_COLORIZED, false);
+ return makeNotificationHeader(mParams.reset().ambient(ambient).fillTextsFrom(this));
+ }
+
+ /**
+ * Construct a RemoteViews for the final notification header only. This will not be
+ * colorized.
+ *
+ * @param p the template params to inflate this with
+ */
+ private RemoteViews makeNotificationHeader(StandardTemplateParams p) {
+ // Headers on their own are never colorized
+ p.disallowColorization();
RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
- ambient ? R.layout.notification_template_ambient_header
+ p.ambient ? R.layout.notification_template_ambient_header
: R.layout.notification_template_header);
resetNotificationHeader(header);
- bindNotificationHeader(header, ambient, null);
- if (colorized != null) {
- mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
- } else {
- mN.extras.remove(EXTRA_COLORIZED);
- }
+ bindNotificationHeader(header, p);
return header;
}
@@ -5329,24 +5382,15 @@
* @hide
*/
public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
- int color = mN.color;
- mN.color = COLOR_DEFAULT;
- CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
- if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
- CharSequence newSummary = createSummaryText();
- if (!TextUtils.isEmpty(newSummary)) {
- mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
- }
+ StandardTemplateParams p = mParams.reset()
+ .forceDefaultColor()
+ .ambient(false)
+ .fillTextsFrom(this);
+ if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
+ p.summaryText(createSummaryText());
}
-
- RemoteViews header = makeNotificationHeader(false /* ambient */);
+ RemoteViews header = makeNotificationHeader(p);
header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
- if (summary != null) {
- mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
- } else {
- mN.extras.remove(EXTRA_SUB_TEXT);
- }
- mN.color = color;
return header;
}
@@ -5375,7 +5419,7 @@
}
private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
- boolean ambient) {
+ StandardTemplateParams p) {
final boolean tombstone = (action.actionIntent == null);
RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
emphazisedMode ? getEmphasizedActionLayoutResource()
@@ -5392,7 +5436,7 @@
// change the background bgColor
CharSequence title = action.title;
ColorStateList[] outResultColor = null;
- int background = resolveBackgroundColor();
+ int background = resolveBackgroundColor(p);
if (isLegacy()) {
title = ContrastColorUtil.clearColorSpans(title);
} else {
@@ -5400,7 +5444,7 @@
title = ensureColorSpanContrast(title, background, outResultColor);
}
button.setTextViewText(R.id.action0, processTextSpans(title));
- setTextViewColorPrimary(button, R.id.action0);
+ setTextViewColorPrimary(button, R.id.action0, p);
int rippleColor;
boolean hasColorOverride = outResultColor != null && outResultColor[0] != null;
if (hasColorOverride) {
@@ -5411,11 +5455,12 @@
background, mInNightMode);
button.setTextColor(R.id.action0, textColor);
rippleColor = textColor;
- } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
- rippleColor = resolveContrastColor();
+ } else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p)
+ && mTintActionButtons) {
+ rippleColor = resolveContrastColor(p);
button.setTextColor(R.id.action0, rippleColor);
} else {
- rippleColor = getPrimaryTextColor();
+ rippleColor = getPrimaryTextColor(p);
}
// We only want about 20% alpha for the ripple
rippleColor = (rippleColor & 0x00ffffff) | 0x33000000;
@@ -5427,13 +5472,15 @@
} else {
button.setTextViewText(R.id.action0, processTextSpans(
processLegacyText(action.title)));
- if (isColorized() && !ambient) {
- setTextViewColorPrimary(button, R.id.action0);
- } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
+ if (isColorized(p)) {
+ setTextViewColorPrimary(button, R.id.action0, p);
+ } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) {
button.setTextColor(R.id.action0,
- ambient ? resolveAmbientColor() : resolveContrastColor());
+ p.ambient ? resolveAmbientColor(p) : resolveContrastColor(p));
}
}
+ button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
+ mActions.indexOf(action));
return button;
}
@@ -5539,15 +5586,15 @@
* Apply any necessariy colors to the small icon
*/
private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
- boolean ambient) {
+ StandardTemplateParams p) {
boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
int color;
- if (ambient) {
- color = resolveAmbientColor();
- } else if (isColorized()) {
- color = getPrimaryTextColor();
+ if (p.ambient) {
+ color = resolveAmbientColor(p);
+ } else if (isColorized(p)) {
+ color = getPrimaryTextColor(p);
} else {
- color = resolveContrastColor();
+ color = resolveContrastColor(p);
}
if (colorable) {
contentView.setDrawableTint(R.id.icon, false, color,
@@ -5563,11 +5610,12 @@
* if it's grayscale).
*/
// TODO: also check bounds, transparency, that sort of thing.
- private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
+ private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView,
+ StandardTemplateParams p) {
if (largeIcon != null && isLegacy()
&& getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
// resolve color will fall back to the default when legacy
- contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(),
+ contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(p),
PorterDuff.Mode.SRC_ATOP);
}
}
@@ -5578,29 +5626,43 @@
}
}
- int resolveContrastColor() {
- if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
+ int resolveContrastColor(StandardTemplateParams p) {
+ int rawColor = getRawColor(p);
+ if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
return mCachedContrastColor;
}
int color;
int background = mContext.getColor(
com.android.internal.R.color.notification_material_background_color);
- if (mN.color == COLOR_DEFAULT) {
- ensureColors();
+ if (rawColor == COLOR_DEFAULT) {
+ ensureColors(p);
color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode);
} else {
- color = ContrastColorUtil.resolveContrastColor(mContext, mN.color,
+ color = ContrastColorUtil.resolveContrastColor(mContext, rawColor,
background, mInNightMode);
}
if (Color.alpha(color) < 255) {
// alpha doesn't go well for color filters, so let's blend it manually
color = ContrastColorUtil.compositeColors(color, background);
}
- mCachedContrastColorIsFor = mN.color;
+ mCachedContrastColorIsFor = rawColor;
return mCachedContrastColor = color;
}
+ /**
+ * Return the raw color of this Notification, which doesn't necessarily satisfy contrast.
+ *
+ * @see #resolveContrastColor(StandardTemplateParams) for the contrasted color
+ * @param p the template params to inflate this with
+ */
+ private int getRawColor(StandardTemplateParams p) {
+ if (p.forceDefaultColor) {
+ return COLOR_DEFAULT;
+ }
+ return mN.color;
+ }
+
int resolveNeutralColor() {
if (mNeutralColor != COLOR_INVALID) {
return mNeutralColor;
@@ -5616,13 +5678,14 @@
return mNeutralColor;
}
- int resolveAmbientColor() {
- if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
+ int resolveAmbientColor(StandardTemplateParams p) {
+ int rawColor = getRawColor(p);
+ if (mCachedAmbientColorIsFor == rawColor && mCachedAmbientColorIsFor != COLOR_INVALID) {
return mCachedAmbientColor;
}
- final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, mN.color);
+ final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, rawColor);
- mCachedAmbientColorIsFor = mN.color;
+ mCachedAmbientColorIsFor = rawColor;
return mCachedAmbientColor = contrasted;
}
@@ -5854,9 +5917,9 @@
return R.layout.notification_material_action_tombstone;
}
- private int getBackgroundColor() {
- if (isColorized()) {
- return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
+ private int getBackgroundColor(StandardTemplateParams p) {
+ if (isColorized(p)) {
+ return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p);
} else {
return COLOR_DEFAULT;
}
@@ -5864,10 +5927,11 @@
/**
* Gets a neutral color that can be used for icons or similar that should not stand out.
+ * @param p the template params to inflate this with
*/
- private int getNeutralColor() {
- if (isColorized()) {
- return getSecondaryTextColor();
+ private int getNeutralColor(StandardTemplateParams p) {
+ if (isColorized(p)) {
+ return getSecondaryTextColor(p);
} else {
return resolveNeutralColor();
}
@@ -5875,9 +5939,10 @@
/**
* Same as getBackgroundColor but also resolved the default color to the background.
+ * @param p the template params to inflate this with
*/
- private int resolveBackgroundColor() {
- int backgroundColor = getBackgroundColor();
+ private int resolveBackgroundColor(StandardTemplateParams p) {
+ int backgroundColor = getBackgroundColor(p);
if (backgroundColor == COLOR_DEFAULT) {
backgroundColor = mContext.getColor(
com.android.internal.R.color.notification_material_background_color);
@@ -5885,10 +5950,6 @@
return backgroundColor;
}
- private boolean isColorized() {
- return mN.isColorized();
- }
-
private boolean shouldTintActionButtons() {
return mTintActionButtons;
}
@@ -5914,7 +5975,7 @@
mBackgroundColor = backgroundColor;
mForegroundColor = foregroundColor;
mTextColorsAreForBackground = COLOR_INVALID;
- ensureColors();
+ ensureColors(mParams.reset().fillTextsFrom(this));
}
/**
@@ -6182,30 +6243,30 @@
}
protected RemoteViews getStandardView(int layoutId) {
- return getStandardView(layoutId, null);
+ StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder);
+ return getStandardView(layoutId, p, null);
}
+
/**
* Get the standard view for this style.
*
- * @param layoutId The layout id to use
+ * @param layoutId The layout id to use.
+ * @param p the params for this inflation.
* @param result The result where template bind information is saved.
* @return A remoteView for this style.
* @hide
*/
- protected RemoteViews getStandardView(int layoutId, TemplateBindResult result) {
+ protected RemoteViews getStandardView(int layoutId, StandardTemplateParams p,
+ TemplateBindResult result) {
checkBuilder();
- // Nasty.
- CharSequence oldBuilderContentTitle =
- mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
if (mBigContentTitle != null) {
- mBuilder.setContentTitle(mBigContentTitle);
+ p.title = mBigContentTitle;
}
- RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, result);
-
- mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
+ RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, p,
+ result);
if (mBigContentTitle != null && mBigContentTitle.equals("")) {
contentView.setViewVisibility(R.id.line1, View.GONE);
@@ -6500,12 +6561,13 @@
mBuilder.mN.largeIcon = null;
}
+ StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder);
RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(),
- null /* result */);
+ p, null /* result */);
if (mSummaryTextSet) {
contentView.setTextViewText(R.id.text, mBuilder.processTextSpans(
mBuilder.processLegacyText(mSummaryText)));
- mBuilder.setTextViewColorSecondary(contentView, R.id.text);
+ mBuilder.setTextViewColorSecondary(contentView, R.id.text, p);
contentView.setViewVisibility(R.id.text, View.VISIBLE);
}
mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
@@ -6698,24 +6760,24 @@
* @hide
*/
public RemoteViews makeBigContentView() {
-
- // Nasty
- CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
- mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
-
+ StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null);
TemplateBindResult result = new TemplateBindResult();
- RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), result);
+ RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), p,
+ result);
contentView.setInt(R.id.big_text, "setImageEndMargin", result.getIconMarginEnd());
- mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
-
CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
if (TextUtils.isEmpty(bigTextText)) {
// In case the bigtext is null / empty fall back to the normal text to avoid a weird
// experience
- bigTextText = mBuilder.processLegacyText(text);
+ bigTextText = mBuilder.processLegacyText(
+ mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT));
}
- applyBigTextContentView(mBuilder, contentView, bigTextText);
+ contentView.setTextViewText(R.id.big_text, mBuilder.processTextSpans(bigTextText));
+ mBuilder.setTextViewColorSecondary(contentView, R.id.big_text, p);
+ contentView.setViewVisibility(R.id.big_text,
+ TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
+ contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.hasLargeIcon());
return contentView;
}
@@ -6733,14 +6795,6 @@
return !Objects.equals(String.valueOf(getBigText()), String.valueOf(newS.getBigText()));
}
- static void applyBigTextContentView(Builder builder,
- RemoteViews contentView, CharSequence bigTextText) {
- contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
- builder.setTextViewColorSecondary(contentView, R.id.big_text);
- contentView.setViewVisibility(R.id.big_text,
- TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
- contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
- }
}
/**
@@ -7224,24 +7278,26 @@
isOneToOne = !isGroupConversation();
}
TemplateBindResult bindResult = new TemplateBindResult();
+ StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).title(
+ conversationTitle).text(null)
+ .hideLargeIcon(hideRightIcons || isOneToOne)
+ .hideReplyIcon(hideRightIcons)
+ .headerTextSecondary(conversationTitle);
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
mBuilder.getMessagingLayoutResource(),
- mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
- .hideLargeIcon(hideRightIcons || isOneToOne)
- .hideReplyIcon(hideRightIcons)
- .headerTextSecondary(conversationTitle),
+ p,
bindResult);
addExtras(mBuilder.mN.extras);
// also update the end margin if there is an image
contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
bindResult.getIconMarginEnd());
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
- mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
- : mBuilder.resolveContrastColor());
+ mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
+ : mBuilder.resolveContrastColor(p));
contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor",
- mBuilder.getPrimaryTextColor());
+ mBuilder.getPrimaryTextColor(p));
contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor",
- mBuilder.getSecondaryTextColor());
+ mBuilder.getSecondaryTextColor(p));
contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
displayImagesAtEnd);
contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
@@ -7705,15 +7761,9 @@
* @hide
*/
public RemoteViews makeBigContentView() {
- // Remove the content text so it disappears unless you have a summary
- // Nasty
- CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
- mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
-
+ StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null);
TemplateBindResult result = new TemplateBindResult();
- RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), result);
-
- mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
+ RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), p, result);
int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
@@ -7760,7 +7810,7 @@
contentView.setViewVisibility(rowIds[i], View.VISIBLE);
contentView.setTextViewText(rowIds[i],
mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
- mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
+ mBuilder.setTextViewColorSecondary(contentView, rowIds[i], p);
contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
handleInboxImageMargin(contentView, rowIds[i], first,
result.getIconMarginEnd());
@@ -7997,7 +8047,7 @@
}
private void bindMediaActionButton(RemoteViews container, @IdRes int buttonId,
- Action action, int color) {
+ Action action, StandardTemplateParams p) {
final boolean tombstone = (action.actionIntent == null);
container.setViewVisibility(buttonId, View.VISIBLE);
container.setImageViewIcon(buttonId, action.getIcon());
@@ -8008,8 +8058,8 @@
Configuration currentConfig = resources.getConfiguration();
boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
- int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
- ? color
+ int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized(p)
+ ? getActionColor(p)
: ContrastColorUtil.resolveColor(mBuilder.mContext,
Notification.COLOR_DEFAULT, inNightMode);
@@ -8031,8 +8081,10 @@
}
private RemoteViews makeMediaContentView() {
+ StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom(
+ mBuilder);
RemoteViews view = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_media, false, /* hasProgress */
+ R.layout.notification_template_material_media, p,
null /* result */);
final int numActions = mBuilder.mActions.size();
@@ -8047,7 +8099,7 @@
for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) {
if (i < numActionsToShow) {
final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
- bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, getActionColor());
+ bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, p);
} else {
view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
@@ -8062,9 +8114,9 @@
return view;
}
- private int getActionColor() {
- return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
- : mBuilder.resolveContrastColor();
+ private int getActionColor(StandardTemplateParams p) {
+ return mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
+ : mBuilder.resolveContrastColor(p);
}
private RemoteViews makeMediaBigContentView() {
@@ -8076,13 +8128,14 @@
if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
return null;
}
+ StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom(
+ mBuilder);
RemoteViews big = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_big_media, false, null /* result */);
+ R.layout.notification_template_material_big_media, p , null /* result */);
for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) {
if (i < actionCount) {
- bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i),
- getActionColor());
+ bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p);
} else {
big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
@@ -8210,7 +8263,7 @@
customContent = customContent.clone();
remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
- remoteViews.setReapplyDisallowed();
+ remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
}
// also update the end margin if there is an image
Resources resources = mBuilder.mContext.getResources();
@@ -8338,10 +8391,10 @@
// Need to clone customContent before adding, because otherwise it can no longer be
// parceled independently of remoteViews.
customContent = customContent.clone();
- customContent.overrideTextColors(mBuilder.getPrimaryTextColor());
+ customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams));
remoteViews.removeAllViews(id);
remoteViews.addView(id, customContent);
- remoteViews.setReapplyDisallowed();
+ remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
}
return remoteViews;
}
@@ -9881,17 +9934,23 @@
CharSequence title;
CharSequence text;
CharSequence headerTextSecondary;
+ CharSequence summaryText;
int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
boolean hideLargeIcon;
boolean hideReplyIcon;
+ boolean allowColorization = true;
+ boolean forceDefaultColor = false;
final StandardTemplateParams reset() {
hasProgress = true;
ambient = false;
title = null;
text = null;
+ summaryText = null;
headerTextSecondary = null;
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
+ allowColorization = true;
+ forceDefaultColor = false;
return this;
}
@@ -9910,6 +9969,11 @@
return this;
}
+ final StandardTemplateParams summaryText(CharSequence text) {
+ this.summaryText = text;
+ return this;
+ }
+
final StandardTemplateParams headerTextSecondary(CharSequence text) {
this.headerTextSecondary = text;
return this;
@@ -9925,6 +9989,16 @@
return this;
}
+ final StandardTemplateParams disallowColorization() {
+ this.allowColorization = false;
+ return this;
+ }
+
+ final StandardTemplateParams forceDefaultColor() {
+ this.forceDefaultColor = true;
+ return this;
+ }
+
final StandardTemplateParams ambient(boolean ambient) {
Preconditions.checkState(title == null && text == null, "must set ambient before text");
this.ambient = ambient;
@@ -9941,6 +10015,7 @@
text = extras.getCharSequence(EXTRA_TEXT);
}
this.text = b.processLegacyText(text, ambient);
+ this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT);
return this;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 89ec19b..25fa897 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1424,7 +1424,62 @@
return other.priorityCategories == priorityCategories
&& other.priorityCallSenders == priorityCallSenders
&& other.priorityMessageSenders == priorityMessageSenders
- && other.suppressedVisualEffects == suppressedVisualEffects;
+ && suppressedVisualEffectsEqual(suppressedVisualEffects,
+ other.suppressedVisualEffects);
+ }
+
+
+ private boolean suppressedVisualEffectsEqual(int suppressedEffects,
+ int otherSuppressedVisualEffects) {
+ if (suppressedEffects == otherSuppressedVisualEffects) {
+ return true;
+ }
+
+ if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0) {
+ suppressedEffects |= SUPPRESSED_EFFECT_PEEK;
+ }
+ if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0) {
+ suppressedEffects |= SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ suppressedEffects |= SUPPRESSED_EFFECT_LIGHTS;
+ suppressedEffects |= SUPPRESSED_EFFECT_AMBIENT;
+ }
+
+ if ((otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0) {
+ otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
+ }
+ if ((otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0) {
+ otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS;
+ otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
+ }
+
+ if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON)
+ != (otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_ON)) {
+ int currSuppressedEffects = (suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0
+ ? otherSuppressedVisualEffects : suppressedEffects;
+ if ((currSuppressedEffects & SUPPRESSED_EFFECT_PEEK) == 0) {
+ return false;
+ }
+ }
+
+ if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF)
+ != (otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_OFF)) {
+ int currSuppressedEffects = (suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0
+ ? otherSuppressedVisualEffects : suppressedEffects;
+ if ((currSuppressedEffects & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0
+ || (currSuppressedEffects & SUPPRESSED_EFFECT_LIGHTS) == 0
+ || (currSuppressedEffects & SUPPRESSED_EFFECT_AMBIENT) == 0) {
+ return false;
+ }
+ }
+
+ int thisWithoutOldEffects = suppressedEffects
+ & ~SUPPRESSED_EFFECT_SCREEN_ON
+ & ~SUPPRESSED_EFFECT_SCREEN_OFF;
+ int otherWithoutOldEffects = otherSuppressedVisualEffects
+ & ~SUPPRESSED_EFFECT_SCREEN_ON
+ & ~SUPPRESSED_EFFECT_SCREEN_OFF;
+ return thisWithoutOldEffects == otherWithoutOldEffects;
}
@Override
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index 9df4fff..95d5d19 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -32,13 +32,11 @@
public final long rssInBytes;
public final long cacheInBytes;
public final long swapInBytes;
- // TODO(rslawik): Delete this field once ProcessMemoryHighWaterMark is ready.
- public final long rssHighWatermarkInBytes;
public final long startTimeNanos;
public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
long pgmajfault, long rssInBytes, long cacheInBytes,
- long swapInBytes, long rssHighWatermarkInBytes, long startTimeNanos) {
+ long swapInBytes, long startTimeNanos) {
this.uid = uid;
this.processName = processName;
this.oomScore = oomScore;
@@ -47,7 +45,6 @@
this.rssInBytes = rssInBytes;
this.cacheInBytes = cacheInBytes;
this.swapInBytes = swapInBytes;
- this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
this.startTimeNanos = startTimeNanos;
}
@@ -60,7 +57,6 @@
rssInBytes = in.readLong();
cacheInBytes = in.readLong();
swapInBytes = in.readLong();
- rssHighWatermarkInBytes = in.readLong();
startTimeNanos = in.readLong();
}
@@ -91,7 +87,6 @@
parcel.writeLong(rssInBytes);
parcel.writeLong(cacheInBytes);
parcel.writeLong(swapInBytes);
- parcel.writeLong(rssHighWatermarkInBytes);
parcel.writeLong(startTimeNanos);
}
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6f71b7a..0404e80d 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -166,8 +166,8 @@
import android.view.autofill.AutofillManager;
import android.view.autofill.IAutoFillManager;
import android.view.inputmethod.InputMethodManager;
+import android.view.intelligence.ContentCaptureManager;
import android.view.intelligence.IIntelligenceManager;
-import android.view.intelligence.IntelligenceManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -200,6 +200,7 @@
private SystemServiceRegistry() { }
static {
+ //CHECKSTYLE:OFF IndentationCheck
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher<AccessibilityManager>() {
@Override
@@ -1059,15 +1060,20 @@
return new AutofillManager(ctx.getOuterContext(), service);
}});
- registerService(Context.INTELLIGENCE_MANAGER_SERVICE, IntelligenceManager.class,
- new CachedServiceFetcher<IntelligenceManager>() {
+ registerService(Context.CONTENT_CAPTURE_MANAGER_SERVICE, ContentCaptureManager.class,
+ new CachedServiceFetcher<ContentCaptureManager>() {
@Override
- public IntelligenceManager createService(ContextImpl ctx)
+ public ContentCaptureManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
// Get the services without throwing as this is an optional feature
- IBinder b = ServiceManager.getService(Context.INTELLIGENCE_MANAGER_SERVICE);
- IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b);
- return new IntelligenceManager(ctx.getOuterContext(), service);
+ Context outerContext = ctx.getOuterContext();
+ if (outerContext.isContentCaptureSupported()) {
+ IBinder b = ServiceManager
+ .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+ IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b);
+ return new ContentCaptureManager(outerContext, service);
+ }
+ return null;
}});
registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
@@ -1147,6 +1153,7 @@
throws ServiceNotFoundException {
return new RoleManager(ctx.getOuterContext());
}});
+ //CHECKSTYLE:ON IndentationCheck
}
/**
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 3ea3da2..f0f7d89 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.SystemApi;
import android.app.slice.Slice;
import android.content.ComponentName;
import android.content.Context;
@@ -330,7 +331,9 @@
* @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean)
* @see WallpaperService.Engine#isInAmbientMode()
* @return {@code true} if wallpaper can draw when in ambient mode.
+ * @hide
*/
+ @SystemApi
public boolean supportsAmbientMode() {
return mSupportsAmbientMode;
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 27471ca..27ae0b0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1898,23 +1898,33 @@
* @hide
*/
public static ComponentName getDefaultWallpaperComponent(Context context) {
+ ComponentName cn = null;
+
String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT);
if (!TextUtils.isEmpty(flat)) {
- final ComponentName cn = ComponentName.unflattenFromString(flat);
- if (cn != null) {
- return cn;
+ cn = ComponentName.unflattenFromString(flat);
+ }
+
+ if (cn == null) {
+ flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
+ if (!TextUtils.isEmpty(flat)) {
+ cn = ComponentName.unflattenFromString(flat);
}
}
- flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
- if (!TextUtils.isEmpty(flat)) {
- final ComponentName cn = ComponentName.unflattenFromString(flat);
- if (cn != null) {
- return cn;
+ // Check if the package exists
+ if (cn != null) {
+ try {
+ final PackageManager packageManager = context.getPackageManager();
+ packageManager.getPackageInfo(cn.getPackageName(),
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ } catch (PackageManager.NameNotFoundException e) {
+ cn = null;
}
}
- return null;
+ return cn;
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 00c1863..3a97284 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9992,4 +9992,140 @@
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Whitelists a package that is allowed to access cross profile calendar APIs.
+ *
+ * <p>Called by a profile owner of a managed profile.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName name of the package to be whitelisted.
+ * @throws SecurityException if {@code admin} is not a profile owner.
+ *
+ * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #getCrossProfileCalendarPackages(ComponentName)
+ */
+ public void addCrossProfileCalendarPackage(@NonNull ComponentName admin,
+ @NonNull String packageName) {
+ throwIfParentInstance("addCrossProfileCalendarPackage");
+ if (mService != null) {
+ try {
+ mService.addCrossProfileCalendarPackage(admin, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Removes a package that was allowed to access cross profile calendar APIs
+ * from the whitelist.
+ *
+ * <p>Called by a profile owner of a managed profile.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName name of the package to be removed from the whitelist.
+ * @return {@code true} if the package is successfully removed from the whitelist,
+ * {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a profile owner.
+ *
+ * @see #addCrossProfileCalendarPackage(ComponentName, String)
+ * @see #getCrossProfileCalendarPackages(ComponentName)
+ */
+ public boolean removeCrossProfileCalendarPackage(@NonNull ComponentName admin,
+ @NonNull String packageName) {
+ throwIfParentInstance("removeCrossProfileCalendarPackage");
+ if (mService != null) {
+ try {
+ return mService.removeCrossProfileCalendarPackage(admin, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+ *
+ * <p>Called by a profile owner of a managed profile.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @return the set of names of packages that were previously whitelisted via
+ * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an
+ * empty set if none have been whitelisted.
+ * @throws SecurityException if {@code admin} is not a profile owner.
+ *
+ * @see #addCrossProfileCalendarPackage(ComponentName, String)
+ * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ */
+ public @NonNull Set<String> getCrossProfileCalendarPackages(@NonNull ComponentName admin) {
+ throwIfParentInstance("getCrossProfileCalendarPackages");
+ if (mService != null) {
+ try {
+ return new ArraySet<>(mService.getCrossProfileCalendarPackages(admin));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return Collections.emptySet();
+ }
+
+ /**
+ * Returns if a package is whitelisted to access cross profile calendar APIs.
+ *
+ * <p>To query for a specific user, use
+ * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
+ * that user, and get a {@link DevicePolicyManager} from this context.
+ *
+ * @param packageName the name of the package
+ * @return {@code true} if the package is whitelisted to access cross profile calendar APIs.
+ * {@code false} otherwise.
+ *
+ * @see #addCrossProfileCalendarPackage(ComponentName, String)
+ * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #getCrossProfileCalendarPackages(ComponentName)
+ * @hide
+ */
+ public @NonNull boolean isPackageAllowedToAccessCalendar(@NonNull String packageName) {
+ throwIfParentInstance("isPackageAllowedToAccessCalendar");
+ if (mService != null) {
+ try {
+ return mService.isPackageAllowedToAccessCalendarForUser(packageName,
+ myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+ *
+ * <p>To query for a specific user, use
+ * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
+ * that user, and get a {@link DevicePolicyManager} from this context.
+ *
+ * @return the set of names of packages that were previously whitelisted via
+ * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an
+ * empty set if none have been whitelisted.
+ *
+ * @see #addCrossProfileCalendarPackage(ComponentName, String)
+ * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+ * @see #getCrossProfileCalendarPackages(ComponentName)
+ * @hide
+ */
+ public @NonNull Set<String> getCrossProfileCalendarPackages() {
+ throwIfParentInstance("getCrossProfileCalendarPackages");
+ if (mService != null) {
+ try {
+ return new ArraySet<>(mService.getCrossProfileCalendarPackagesForUser(
+ myUserId()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return Collections.emptySet();
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 60f79d6..fcf74ee 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -422,4 +422,10 @@
void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId);
void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener);
+
+ void addCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+ boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+ List<String> getCrossProfileCalendarPackages(in ComponentName admin);
+ boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
+ List<String> getCrossProfileCalendarPackagesForUser(int userHandle);
}
diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS
index 1c9a43a..673d85f 100644
--- a/core/java/android/app/backup/OWNERS
+++ b/core/java/android/app/backup/OWNERS
@@ -1,7 +1,6 @@
-artikz@google.com
+anniemeng@google.com
brufino@google.com
bryanmawhinney@google.com
ctate@google.com
jorlow@google.com
-mkarpinski@google.com
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 318dbee..c740c42 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -88,6 +88,7 @@
int mViewMode = VIEW_MODE_NOINIT;
int mLayoutId = -1;
private OnClickHandler mOnClickHandler;
+ private boolean mOnLightBackground;
private Executor mAsyncExecutor;
private CancellationSignal mLastExecutionSignal;
@@ -374,6 +375,15 @@
}
/**
+ * Sets whether the widget should is being displayed on a light/white background and use an
+ * alternate UI if available.
+ * @see RemoteViews#setLightBackgroundLayoutId(int)
+ */
+ public void setOnLightBackground(boolean useDarkTextLayout) {
+ mOnLightBackground = useDarkTextLayout;
+ }
+
+ /**
* Update the AppWidgetProviderInfo for this view, and reset it to the
* initial layout.
*/
@@ -413,6 +423,10 @@
mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
} else {
+ if (mOnLightBackground) {
+ remoteViews = remoteViews.getDarkTextViews();
+ }
+
if (mAsyncExecutor != null && useAsyncIfPossible) {
inflateAsync(remoteViews);
return;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 237082e..2f0b44f 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.EventLog;
/**
@@ -30,6 +31,8 @@
*/
public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
+ private static final int MAX_DESCRIPTOR_SIZE = 2048;
+
private final String mName;
private final String mDescription;
private final String mProvider;
@@ -55,6 +58,12 @@
mDescription = description;
mProvider = provider;
mSubclass = subclass;
+
+ if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) {
+ EventLog.writeEvent(0x534e4554, "119819889", -1, "");
+ throw new IllegalArgumentException("descriptors must be not null and shorter than "
+ + MAX_DESCRIPTOR_SIZE);
+ }
mDescriptors = descriptors.clone();
}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 11f8ab7..e3672a7 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -52,8 +52,7 @@
@RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
public final class BluetoothManager {
private static final String TAG = "BluetoothManager";
- private static final boolean DBG = true;
- private static final boolean VDBG = true;
+ private static final boolean DBG = false;
private final BluetoothAdapter mAdapter;
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index 7988008..2174255 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -116,6 +116,9 @@
*/
@Nullable
public byte[] getManufacturerSpecificData(int manufacturerId) {
+ if (mManufacturerSpecificData == null) {
+ return null;
+ }
return mManufacturerSpecificData.get(manufacturerId);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 437039d..7d5202d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -97,8 +97,7 @@
*
* @hide
*/
- public static final boolean DEPRECATE_DATA_COLUMNS = SystemProperties
- .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+ public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage();
/**
* Special filesystem path prefix which indicates that a path should be
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 72964c7..fe11acb 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -741,16 +741,22 @@
/** Return the name of this application's package. */
public abstract String getPackageName();
- /** @hide Return the name of the base context this context is derived from. */
+ /**
+ * @hide Return the name of the base context this context is derived from.
+ * This is the same as {@link #getOpPackageName()} except in
+ * cases where system components are loaded into other app processes, in which
+ * case {@link #getOpPackageName()} will be the name of the primary package in
+ * that process (so that app ops uid verification will work with the name).
+ */
@UnsupportedAppUsage
public abstract String getBasePackageName();
- /** @hide Return the package name that should be used for app ops calls from
- * this context. This is the same as {@link #getBasePackageName()} except in
- * cases where system components are loaded into other app processes, in which
- * case this will be the name of the primary package in that process (so that app
- * ops uid verification will work with the name). */
- @TestApi
+ /**
+ * Return the package name that should be used for {@link android.app.AppOpsManager} calls from
+ * this context, so that app ops manager's uid verification will work with the name.
+ * <p>
+ * This is not generally intended for third party application developers.
+ */
public abstract String getOpPackageName();
/** Return the full application info for this context's package. */
@@ -3928,12 +3934,13 @@
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
/**
- * Official published name of the intelligence service.
+ * Official published name of the smart suggestions service.
*
* @hide
* @see #getSystemService(String)
*/
- public static final String INTELLIGENCE_MANAGER_SERVICE = "intelligence";
+ // TODO(b/111276913): rename string (will require SELinux change first)
+ public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "intelligence";
/**
* Use with {@link #getSystemService(String)} to access the
@@ -5228,6 +5235,25 @@
}
/**
+ * Checks whether this context supports content capture.
+ *
+ * @hide
+ */
+ // NOTE: for now we just need to check if it's supported so we can optimize calls that can be
+ // skipped when it isn't. Eventually, we might need a full
+ // ContentCaptureManager.ContentCaptureClient interface (as it's done with AutofillClient).
+ //
+ public boolean isContentCaptureSupported() {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public void setContentCaptureSupported(@SuppressWarnings("unused") boolean supported) {
+ }
+
+ /**
* Throws an exception if the Context is using system resources,
* which are non-runtime-overlay-themable and may show inconsistent UI.
* @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 2db44b4..26ed3b7 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1049,4 +1049,20 @@
mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
}
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean isContentCaptureSupported() {
+ return mBase.isContentCaptureSupported();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setContentCaptureSupported(boolean supported) {
+ mBase.setContentCaptureSupported(supported);
+ }
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e7f0053..edfb3a7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1757,33 +1757,6 @@
"android.intent.action.MANAGE_APP_PERMISSIONS";
/**
- * Activity action: Launch UI to manage a specific permissions of an app.
- * <p>
- * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
- * will be managed by the launched UI.
- * </p>
- * <p>
- * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
- * that should be managed by the launched UI.
- * </p>
- * <p>
- * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
- * </p>
- * <p>
- * Output: Nothing.
- * </p>
- *
- * @see #EXTRA_PACKAGE_NAME
- * @see #EXTRA_PERMISSION_NAME
- *
- * @hide
- */
- @SystemApi
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_MANAGE_APP_PERMISSION =
- "android.intent.action.MANAGE_APP_PERMISSION";
-
- /**
* Activity action: Launch UI to manage permissions.
* <p>
* Input: Nothing.
@@ -4116,6 +4089,18 @@
*/
public static final String ACTION_DOCK_ACTIVE = "android.intent.action.DOCK_ACTIVE";
+ /**
+ * Broadcast Action: Indicates that a new device customization has been
+ * downloaded and applied (packages installed, runtime resource overlays
+ * enabled, xml files copied, ...), and that it is time for components that
+ * need to for example clear their caches to do so now.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_DEVICE_CUSTOMIZATION_READY =
+ "android.intent.action.DEVICE_CUSTOMIZATION_READY";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 7c3b5e4..98a135f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1770,7 +1770,7 @@
* is on the package whitelist.
*
* @param policy configured policy for this app, or {@link #HIDDEN_API_ENFORCEMENT_DEFAULT}
- * if nothing configured.
+ * if nothing configured.
* @hide
*/
public void maybeUpdateHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d0eff2e..dbea821 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -676,4 +676,6 @@
String getSystemTextClassifierPackageName();
boolean isPackageStateProtected(String packageName, int userId);
+
+ void sendDeviceCustomizationReadyBroadcast();
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index ecdd810..099d15a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -22,6 +22,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Overall information about the contents of a package. This corresponds
* to all of the information collected from AndroidManifest.xml.
@@ -204,7 +207,10 @@
* {@link PackageManager#GET_PERMISSIONS} was set. This list includes
* all permissions requested, even those that were not granted or known
* by the system at install time.
+ *
+ * @deprecated Use {@link #usesPermissions}
*/
+ @Deprecated
public String[] requestedPermissions;
/**
@@ -214,10 +220,23 @@
* {@link PackageManager#GET_PERMISSIONS} was set. Each value matches
* the corresponding entry in {@link #requestedPermissions}, and will have
* the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+ *
+ * @deprecated Use {@link #usesPermissions}
*/
+ @Deprecated
public int[] requestedPermissionsFlags;
/**
+ * Array of all {@link android.R.styleable#AndroidManifestUsesPermission
+ * <uses-permission>} tags included under <manifest>,
+ * or null if there were none. This is only filled in if the flag
+ * {@link PackageManager#GET_PERMISSIONS} was set. This list includes
+ * all permissions requested, even those that were not granted or known
+ * by the system at install time.
+ */
+ public UsesPermissionInfo[] usesPermissions;
+
+ /**
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
@@ -456,6 +475,7 @@
dest.writeTypedArray(permissions, parcelableFlags);
dest.writeStringArray(requestedPermissions);
dest.writeIntArray(requestedPermissionsFlags);
+ dest.writeTypedArray(usesPermissions, parcelableFlags);
dest.writeTypedArray(signatures, parcelableFlags);
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
@@ -520,6 +540,7 @@
permissions = source.createTypedArray(PermissionInfo.CREATOR);
requestedPermissions = source.createStringArray();
requestedPermissionsFlags = source.createIntArray();
+ usesPermissions = source.createTypedArray(UsesPermissionInfo.CREATOR);
signatures = source.createTypedArray(Signature.CREATOR);
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a4b724b..b7df2bf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5372,6 +5372,10 @@
public abstract void removePackageFromPreferred(String packageName);
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Retrieve the list of all currently configured preferred packages. The
* first package on the list is the most preferred, the last is the least
* preferred.
@@ -5380,6 +5384,7 @@
* @return A List of PackageInfo objects, one for each preferred
* application, in order of preference.
*/
+ @Deprecated
public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
/**
@@ -5406,11 +5411,16 @@
ComponentName[] set, ComponentName activity);
/**
+ * @deprecated This is a protected API that should not have been available
+ * to third party applications. It is the platform's responsibility for
+ * assigning preferred activities and this cannot be directly modified.
+ *
* Same as {@link #addPreferredActivity(IntentFilter, int,
ComponentName[], ComponentName)}, but with a specific userId to apply the preference
to.
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage
public void addPreferredActivityAsUser(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, @UserIdInt int userId) {
@@ -5444,6 +5454,10 @@
ComponentName[] set, ComponentName activity);
/**
+ * @deprecated This is a protected API that should not have been available
+ * to third party applications. It is the platform's responsibility for
+ * assigning preferred activities and this cannot be directly modified.
+ *
* Replaces an existing preferred activity mapping to the system, and if that were not present
* adds a new preferred activity. This will be used to automatically select the given activity
* component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
@@ -5459,6 +5473,7 @@
*
* @hide
*/
+ @Deprecated
@SystemApi
public void replacePreferredActivity(@NonNull IntentFilter filter, int match,
@NonNull List<ComponentName> set, @NonNull ComponentName activity) {
@@ -5476,6 +5491,10 @@
}
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Remove all preferred activity mappings, previously added with
* {@link #addPreferredActivity}, from the
* system whose activities are implemented in the given package name.
@@ -5484,9 +5503,14 @@
* @param packageName The name of the package whose preferred activity
* mappings are to be removed.
*/
+ @Deprecated
public abstract void clearPackagePreferredActivities(String packageName);
/**
+ * @deprecated This function no longer does anything; it was an old
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
+ *
* Retrieve all preferred activities, previously added with
* {@link #addPreferredActivity}, that are
* currently registered with the system.
@@ -5503,6 +5527,7 @@
* (the number of distinct IntentFilter records, not the number of unique
* activity components) that were found.
*/
+ @Deprecated
public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
@NonNull List<ComponentName> outActivities, String packageName);
@@ -6409,4 +6434,18 @@
"isPackageStateProtected not implemented in subclass");
}
+ /**
+ * Notify to the rest of the system that a new device configuration has
+ * been prepared and that it is time to refresh caches.
+ *
+ * @see android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY
+ *
+ * @hide
+ */
+ @SystemApi
+ public void sendDeviceCustomizationReadyBroadcast() {
+ throw new UnsupportedOperationException(
+ "sendDeviceCustomizationReadyBroadcast not implemented in subclass");
+ }
+
}
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 6f49cc4..b49c447 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.util.function.TriFunction;
@@ -37,6 +38,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.function.BiFunction;
+import java.util.function.Consumer;
/**
* Package manager local system service interface.
@@ -735,4 +737,22 @@
/** Returns {@code true} if the given user requires extra badging for icons. */
public abstract boolean userNeedsBadging(int userId);
+
+ /**
+ * Perform the given action for each package.
+ * Note that packages lock will be held while performin the actions.
+ *
+ * @param actionLocked action to be performed
+ */
+ public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked);
+
+ /** Returns the list of enabled components */
+ public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
+
+ /** Returns the list of disabled components */
+ public abstract ArraySet<String> getDisabledComponents(String packageName, int userId);
+
+ /** Returns whether the given package is enabled for the given user */
+ public abstract @PackageManager.EnabledState int getApplicationEnabledState(
+ String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c78960b..ac18dca 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -785,18 +785,23 @@
pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
}
}
- N = p.requestedPermissions.size();
+ N = p.usesPermissionInfos.size();
if (N > 0) {
pi.requestedPermissions = new String[N];
pi.requestedPermissionsFlags = new int[N];
+ pi.usesPermissions = new UsesPermissionInfo[N];
for (int i=0; i<N; i++) {
- final String perm = p.requestedPermissions.get(i);
+ UsesPermissionInfo info = p.usesPermissionInfos.get(i);
+ final String perm = info.getPermission();
pi.requestedPermissions[i] = perm;
+ int permissionFlags = 0;
// The notion of required permissions is deprecated but for compatibility.
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+ permissionFlags |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+ permissionFlags |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
+ pi.requestedPermissionsFlags[i] = permissionFlags;
+ pi.usesPermissions[i] = new UsesPermissionInfo(info, permissionFlags);
}
}
}
@@ -2114,12 +2119,12 @@
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {
- if (!parseUsesPermission(pkg, res, parser)) {
+ if (!parseUsesPermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
- if (!parseUsesPermission(pkg, res, parser)) {
+ if (!parseUsesPermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {
@@ -2442,7 +2447,7 @@
newPermsMsg.append(' ');
}
newPermsMsg.append(npi.name);
- pkg.requestedPermissions.add(npi.name);
+ addRequestedPermission(pkg, npi.name);
pkg.implicitPermissions.add(npi.name);
}
}
@@ -2463,7 +2468,7 @@
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!pkg.requestedPermissions.contains(perm)) {
- pkg.requestedPermissions.add(perm);
+ addRequestedPermission(pkg, perm);
pkg.implicitPermissions.add(perm);
}
}
@@ -2508,7 +2513,7 @@
// If the storage model feature flag is disabled, we need to fiddle
// around with permission definitions to return us to pre-Q behavior.
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
if ("android".equals(pkg.packageName)) {
final ArraySet<String> newGroups = new ArraySet<>();
newGroups.add(android.Manifest.permission_group.MEDIA_AURAL);
@@ -2543,13 +2548,13 @@
}
} else {
if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_AUDIO);
}
if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_VIDEO);
}
if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
- pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
+ addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES);
}
}
@@ -2589,6 +2594,14 @@
}
/**
+ * Helper method for adding a requested permission to a package outside of a uses-permission.
+ */
+ private void addRequestedPermission(Package pkg, String permission) {
+ pkg.requestedPermissions.add(permission);
+ pkg.usesPermissionInfos.add(new UsesPermissionInfo(permission));
+ }
+
+ /**
* Computes the targetSdkVersion to use at runtime. If the package is not
* compatible with this platform, populates {@code outError[0]} with an
* error message.
@@ -2845,8 +2858,8 @@
return certSha256Digests;
}
- private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
- throws XmlPullParserException, IOException {
+ private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
+ String[] outError) throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesPermission);
@@ -2870,6 +2883,44 @@
final String requiredNotfeature = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
+ int dataSentOffDevice = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSentOffDevice, 0);
+
+ int dataSharedWithThirdParty = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSharedWithThirdParty, 0);
+
+ int dataUsedForMonetization = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataUsedForMonetization, 0);
+
+ int retentionWeeks = -1;
+ int retention;
+
+ String rawRetention = sa.getString(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime);
+
+ if (rawRetention == null) {
+ retention = UsesPermissionInfo.RETENTION_UNDEFINED;
+ } else if ("notRetained".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_NOT_RETAINED;
+ } else if ("userSelected".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_USER_SELECTED;
+ } else if ("unlimited".equals(rawRetention)) {
+ retention = UsesPermissionInfo.RETENTION_UNLIMITED;
+ } else {
+ // A number of weeks was specified
+ retention = UsesPermissionInfo.RETENTION_SPECIFIED;
+ retentionWeeks = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime,
+ -1);
+
+ if (retentionWeeks < 0) {
+ outError[0] = "Bad value provided for dataRetentionTime.";
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ XmlUtils.skipCurrentTag(parser);
+ sa.recycle();
+ return false;
+ }
+ }
sa.recycle();
XmlUtils.skipCurrentTag(parser);
@@ -2902,6 +2953,10 @@
+ parser.getPositionDescription());
}
+ UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice,
+ dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks);
+ pkg.usesPermissionInfos.add(info);
+
return true;
}
@@ -3236,6 +3291,10 @@
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);
+ perm.info.usageInfoRequired = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestPermission_usageInfoRequired, 0)
+ != 0;
+
sa.recycle();
if (perm.info.protectionLevel == -1) {
@@ -5338,6 +5397,11 @@
s.info.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
}
if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestService_useAppZygote,
+ false)) {
+ s.info.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE;
+ }
+ if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestService_singleUser,
false)) {
s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
@@ -6365,6 +6429,9 @@
@UnsupportedAppUsage
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
+ public final ArrayList<UsesPermissionInfo> usesPermissionInfos =
+ new ArrayList<>();
+
/** Permissions requested but not in the manifest. */
public final ArrayList<String> implicitPermissions = new ArrayList<>();
@@ -6895,6 +6962,7 @@
dest.readStringList(requestedPermissions);
internStringArrayList(requestedPermissions);
+ dest.readParcelableList(usesPermissionInfos, boot);
dest.readStringList(implicitPermissions);
internStringArrayList(implicitPermissions);
protectedBroadcasts = dest.createStringArrayList();
@@ -7061,6 +7129,7 @@
dest.writeParcelableList(instrumentation, flags);
dest.writeStringList(requestedPermissions);
+ dest.writeParcelableList(usesPermissionInfos, flags);
dest.writeStringList(implicitPermissions);
dest.writeStringList(protectedBroadcasts);
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index e21c33a..be6ed51 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -21,7 +21,6 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
@@ -130,9 +129,6 @@
* </p>
*/
public boolean isMatch(ComponentInfo componentInfo, int flags) {
- if ((flags & MATCH_ALL) != 0) {
- return true;
- }
final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp();
final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
if (!isAvailable(flags)
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 60c06a1..d9d6b5f 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -308,6 +309,12 @@
*/
public CharSequence nonLocalizedDescription;
+ /**
+ * If {@code true} an application targeting {@link Build.VERSION_CODES#Q} <em>must</em>
+ * include permission data usage information in order to be able to be granted this permission.
+ */
+ public boolean usageInfoRequired;
+
/** @hide */
public static int fixProtectionLevel(int level) {
if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -394,6 +401,7 @@
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
+ usageInfoRequired = orig.usageInfoRequired;
}
/**
@@ -458,6 +466,7 @@
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
+ dest.writeInt(usageInfoRequired ? 1 : 0);
}
/** @hide */
@@ -498,5 +507,6 @@
descriptionRes = source.readInt();
requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ usageInfoRequired = source.readInt() != 0;
}
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 91f884c..ad2c72274 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -56,6 +56,23 @@
public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
/**
+ * Bit in {@link #flags}: If set, the service (which must be isolated)
+ * will be spawned from an Application Zygote, instead of the regular Zygote.
+ * The Application Zygote will pre-initialize the application's class loader,
+ * and call a static callback into the application to allow it to perform
+ * application-specific preloads (such as loading a shared library). Therefore,
+ * spawning from the Application Zygote will typically reduce the service
+ * launch time and reduce its memory usage. The downside of using this flag
+ * is that you will have an additional process (the app zygote itself) that
+ * is taking up memory. Whether actual memory usage is improved therefore
+ * strongly depends on the number of isolated services that an application
+ * starts, and how much memory those services save by preloading. Therefore,
+ * it is recommended to measure memory usage under typical workloads to
+ * determine whether it makes sense to use this flag.
+ */
+ public static final int FLAG_USE_APP_ZYGOTE = 0x0008;
+
+ /**
* Bit in {@link #flags} indicating if the service is visible to ephemeral applications.
* @hide
*/
diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java
new file mode 100644
index 0000000..d08548f
--- /dev/null
+++ b/core/java/android/content/pm/UsesPermissionInfo.java
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ */
+
+package android.content.pm;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.RetentionPolicy;
+/**
+ * Information you can retrive about a particular application requested permission. This
+ * corresponds to information collected from the AndroidManifest.xml's <uses-permission>
+ * tags.
+ */
+public final class UsesPermissionInfo extends PackageItemInfo implements Parcelable {
+
+ /**
+ * Flag for {@link #getFlags()}: the requested permission is currently granted to the
+ * application.
+ */
+ public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 1 << 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_REQUESTED_PERMISSION_GRANTED})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ /** An unset value for {@link #getDataSentOffDevice()},
+ * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
+ */
+ public static final int USAGE_UNDEFINED = 0;
+
+ /**
+ * A yes value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
+ * and {@link #getDataUsedForMonetization()} corresponding to the <code>yes</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_YES = 1;
+
+ /**
+ * A user triggered only value for {@link #getDataSentOffDevice()},
+ * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
+ * corresponding to the <code>userTriggered</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_USER_TRIGGERED = 2;
+
+ /**
+ * A no value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
+ * and {@link #getDataUsedForMonetization()} corresponding to the <code>no</code> value of
+ * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+ * and {@link android.R.attr#dataUsedForMonetization} attributes.
+ */
+ public static final int USAGE_NO = 3;
+
+ /** @hide */
+ @IntDef(prefix = {"USAGE_"}, value = {
+ USAGE_UNDEFINED,
+ USAGE_YES,
+ USAGE_USER_TRIGGERED,
+ USAGE_NO})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Usage {}
+
+ /**
+ * An unset value for {@link #getDataRetention}.
+ */
+ public static final int RETENTION_UNDEFINED = 0;
+
+ /**
+ * A data not retained value for {@link #getDataRetention()} corresponding to the
+ * <code>notRetained</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_NOT_RETAINED = 1;
+
+ /**
+ * A user selected value for {@link #getDataRetention()} corresponding to the
+ * <code>userSelected</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_USER_SELECTED = 2;
+
+ /**
+ * An unlimited value for {@link #getDataRetention()} corresponding to the
+ * <code>unlimited</code> value of {@link android.R.attr#dataRetentionTime}.
+ */
+ public static final int RETENTION_UNLIMITED = 3;
+
+ /**
+ * A specified value for {@link #getDataRetention()} corresponding to providing the number of
+ * weeks data is retained in {@link android.R.attr#dataRetentionTime}. The number of weeks
+ * is available in {@link #getDataRetentionWeeks()}.
+ */
+ public static final int RETENTION_SPECIFIED = 4;
+
+ /** @hide */
+ @IntDef(prefix = {"RETENTION_"}, value = {
+ RETENTION_UNDEFINED,
+ RETENTION_NOT_RETAINED,
+ RETENTION_USER_SELECTED,
+ RETENTION_UNLIMITED,
+ RETENTION_SPECIFIED})
+ @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+ public @interface Retention {}
+
+ private final String mPermission;
+ private final @Flags int mFlags;
+ private final @Usage int mDataSentOffDevice;
+ private final @Usage int mDataSharedWithThirdParty;
+ private final @Usage int mDataUsedForMonetization;
+ private final @Retention int mDataRetention;
+ private final int mDataRetentionWeeks;
+
+ /** @hide */
+ public UsesPermissionInfo(String permission) {
+ mPermission = permission;
+ mDataSentOffDevice = USAGE_UNDEFINED;
+ mDataSharedWithThirdParty = USAGE_UNDEFINED;
+ mDataUsedForMonetization = USAGE_UNDEFINED;
+ mDataRetention = RETENTION_UNDEFINED;
+ mDataRetentionWeeks = -1;
+ mFlags = 0;
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(String permission,
+ @Usage int dataSentOffDevice, @Usage int dataSharedWithThirdParty,
+ @Usage int dataUsedForMonetization, @Retention int dataRetention,
+ int dataRetentionWeeks) {
+ mPermission = permission;
+ mDataSentOffDevice = dataSentOffDevice;
+ mDataSharedWithThirdParty = dataSharedWithThirdParty;
+ mDataUsedForMonetization = dataUsedForMonetization;
+ mDataRetention = dataRetention;
+ mDataRetentionWeeks = dataRetentionWeeks;
+ mFlags = 0;
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(UsesPermissionInfo orig) {
+ this(orig, orig.mFlags);
+ }
+
+ /** @hide */
+ public UsesPermissionInfo(UsesPermissionInfo orig, int flags) {
+ super(orig);
+ mPermission = orig.mPermission;
+ mFlags = flags;
+ mDataSentOffDevice = orig.mDataSentOffDevice;
+ mDataSharedWithThirdParty = orig.mDataSharedWithThirdParty;
+ mDataUsedForMonetization = orig.mDataUsedForMonetization;
+ mDataRetention = orig.mDataRetention;
+ mDataRetentionWeeks = orig.mDataRetentionWeeks;
+ }
+
+ /**
+ * The name of the requested permission.
+ */
+ public String getPermission() {
+ return mPermission;
+ }
+
+ public @Flags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * If the application sends the data guarded by this permission off the device.
+ *
+ * See {@link android.R.attr#dataSentOffDevice}
+ */
+ public @Usage int getDataSentOffDevice() {
+ return mDataSentOffDevice;
+ }
+
+ /**
+ * If the application or its services shares the data guarded by this permission with third
+ * parties.
+ *
+ * See {@link android.R.attr#dataSharedWithThirdParty}
+ */
+ public @Usage int getDataSharedWithThirdParty() {
+ return mDataSharedWithThirdParty;
+ }
+
+ /**
+ * If the application or its services use the data guarded by this permission for monetization
+ * purposes.
+ *
+ * See {@link android.R.attr#dataUsedForMonetization}
+ */
+ public @Usage int getDataUsedForMonetization() {
+ return mDataUsedForMonetization;
+ }
+
+ /**
+ * How long the application or its services store the data guarded by this permission.
+ * If set to {@link #RETENTION_SPECIFIED} {@link #getDataRetentionWeeks()} will contain the
+ * number of weeks the data is stored.
+ *
+ * See {@link android.R.attr#dataRetentionTime}
+ */
+ public @Retention int getDataRetention() {
+ return mDataRetention;
+ }
+
+ /**
+ * If {@link #getDataRetention()} is {@link #RETENTION_SPECIFIED} the number of weeks the
+ * application or its services store data guarded by this permission.
+ *
+ * @throws IllegalStateException if {@link #getDataRetention} is not
+ * {@link #RETENTION_SPECIFIED}.
+ */
+ public int getDataRetentionWeeks() {
+ if (mDataRetention != RETENTION_SPECIFIED) {
+ throw new IllegalStateException("Data retention weeks not specified");
+ }
+ return mDataRetentionWeeks;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mPermission);
+ dest.writeInt(mFlags);
+ dest.writeInt(mDataSentOffDevice);
+ dest.writeInt(mDataSharedWithThirdParty);
+ dest.writeInt(mDataUsedForMonetization);
+ dest.writeInt(mDataRetention);
+ dest.writeInt(mDataRetentionWeeks);
+ }
+
+ private UsesPermissionInfo(Parcel source) {
+ super(source);
+ mPermission = source.readString();
+ mFlags = source.readInt();
+ mDataSentOffDevice = source.readInt();
+ mDataSharedWithThirdParty = source.readInt();
+ mDataUsedForMonetization = source.readInt();
+ mDataRetention = source.readInt();
+ mDataRetentionWeeks = source.readInt();
+ }
+
+ public static final Creator<UsesPermissionInfo> CREATOR =
+ new Creator<UsesPermissionInfo>() {
+ @Override
+ public UsesPermissionInfo createFromParcel(Parcel source) {
+ return new UsesPermissionInfo(source);
+ }
+ @Override
+ public UsesPermissionInfo[] newArray(int size) {
+ return new UsesPermissionInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 79bc9a3..73addb7 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -16,13 +16,15 @@
package android.content.pm.permission;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -32,7 +34,7 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.SomeArgs;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayList;
import java.util.Collections;
@@ -109,13 +111,11 @@
*/
public void getAppPermissions(@NonNull String packageName,
@NonNull OnResultCallback callback, @Nullable Handler handler) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
- args.arg2 = callback;
- args.arg3 = handler;
- Message message = mRemoteService.obtainMessage(
- RemoteService.MSG_GET_APP_PERMISSIONS, args);
- mRemoteService.processMessage(message);
+ checkNotNull(packageName);
+ checkNotNull(callback);
+
+ mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
+ mRemoteService, packageName, callback, handler));
}
/**
@@ -124,24 +124,20 @@
* @param packageName The package for which to revoke
* @param permissionName The permission to revoke
*/
- public void revokeRuntimePermission(String packageName, String permissionName) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
- args.arg2 = permissionName;
+ public void revokeRuntimePermission(@NonNull String packageName,
+ @NonNull String permissionName) {
+ checkNotNull(packageName);
+ checkNotNull(permissionName);
- Message message = mRemoteService.obtainMessage(
- RemoteService.MSG_REVOKE_APP_PERMISSIONS, args);
- mRemoteService.processMessage(message);
+ mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
+ mRemoteService, packageName, permissionName));
}
private static final class RemoteService
extends Handler implements ServiceConnection {
private static final long UNBIND_TIMEOUT_MILLIS = 10000;
- public static final int MSG_GET_APP_PERMISSIONS = 1;
- public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
- public static final int MSG_UNBIND = 3;
- public static final int MSG_REVOKE_APP_PERMISSIONS = 4;
+ public static final int MSG_UNBIND = 0;
private final Object mLock = new Object();
@@ -191,82 +187,57 @@
}
}
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_GET_APP_PERMISSIONS: {
- SomeArgs args = (SomeArgs) msg.obj;
- final String packageName = (String) args.arg1;
- final OnResultCallback callback = (OnResultCallback) args.arg2;
- final Handler handler = (Handler) args.arg3;
- args.recycle();
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
- try {
- remoteInstance.getAppPermissions(packageName,
- new RemoteCallback(new RemoteCallback.OnResultListener() {
- @Override
- public void onResult(Bundle result) {
- final List<RuntimePermissionPresentationInfo> reportedPermissions;
- List<RuntimePermissionPresentationInfo> permissions = null;
- if (result != null) {
- permissions = result.getParcelableArrayList(KEY_RESULT);
- }
- if (permissions == null) {
- permissions = Collections.emptyList();
- }
- reportedPermissions = permissions;
- if (handler != null) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- callback.onGetAppPermissions(reportedPermissions);
- }
- });
- } else {
- callback.onGetAppPermissions(reportedPermissions);
- }
+ private void getAppPermissions(@NonNull String packageName,
+ @NonNull OnResultCallback callback, @Nullable Handler handler) {
+ final IRuntimePermissionPresenter remoteInstance;
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+ try {
+ remoteInstance.getAppPermissions(packageName,
+ new RemoteCallback(result -> {
+ final List<RuntimePermissionPresentationInfo> reportedPermissions;
+ List<RuntimePermissionPresentationInfo> permissions = null;
+ if (result != null) {
+ permissions = result.getParcelableArrayList(KEY_RESULT);
+ }
+ if (permissions == null) {
+ permissions = Collections.emptyList();
+ }
+ reportedPermissions = permissions;
+ if (handler != null) {
+ handler.post(
+ () -> callback.onGetAppPermissions(reportedPermissions));
+ } else {
+ callback.onGetAppPermissions(reportedPermissions);
}
}, this));
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
- scheduleUnbind();
- } break;
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error getting app permissions", re);
+ }
+ scheduleUnbind();
- case MSG_UNBIND: {
- synchronized (mLock) {
- if (mBound) {
- mContext.unbindService(this);
- mBound = false;
- }
- mRemoteInstance = null;
- }
- } break;
+ synchronized (mLock) {
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
- case MSG_REVOKE_APP_PERMISSIONS: {
- SomeArgs args = (SomeArgs) msg.obj;
- final String packageName = (String) args.arg1;
- final String permissionName = (String) args.arg2;
- args.recycle();
- final IRuntimePermissionPresenter remoteInstance;
- synchronized (mLock) {
- remoteInstance = mRemoteInstance;
- }
- if (remoteInstance == null) {
- return;
- }
- try {
- remoteInstance.revokeRuntimePermission(packageName, permissionName);
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting app permissions", re);
- }
- } break;
+ private void revokeAppPermissions(@NonNull String packageName,
+ @NonNull String permissionName) {
+ final IRuntimePermissionPresenter remoteInstance;
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+ try {
+ remoteInstance.revokeRuntimePermission(packageName, permissionName);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error getting app permissions", re);
}
synchronized (mLock) {
@@ -274,6 +245,16 @@
}
}
+ private void unbind() {
+ synchronized (mLock) {
+ if (mBound) {
+ mContext.unbindService(this);
+ mBound = false;
+ }
+ mRemoteInstance = null;
+ }
+ }
+
@GuardedBy("mLock")
private void scheduleNextMessageIfNeededLocked() {
if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
@@ -284,7 +265,8 @@
private void scheduleUnbind() {
removeMessages(MSG_UNBIND);
- sendEmptyMessageDelayed(MSG_UNBIND, UNBIND_TIMEOUT_MILLIS);
+ sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
+ .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
}
}
}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 4371c77..740cdae 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -58,7 +58,7 @@
public final class AssetManager implements AutoCloseable {
private static final String TAG = "AssetManager";
private static final boolean DEBUG_REFS = false;
- private static final boolean FEATURE_FLAG_IDMAP2 = false;
+ private static final boolean FEATURE_FLAG_IDMAP2 = true;
private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index 79e15a7a..0ec812f 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -30,19 +30,29 @@
public interface BiometricAuthenticator {
/**
+ * No biometric methods or nothing has been enrolled.
+ * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
+ * modalities when calling authenticate().
* @hide
*/
- int TYPE_FINGERPRINT = 1;
+ int TYPE_NONE = 0;
+ /**
+ * Constant representing fingerprint.
+ * @hide
+ */
+ int TYPE_FINGERPRINT = 1 << 0;
/**
+ * Constant representing iris.
* @hide
*/
- int TYPE_IRIS = 2;
+ int TYPE_IRIS = 1 << 1;
/**
+ * Constant representing face.
* @hide
*/
- int TYPE_FACE = 3;
+ int TYPE_FACE = 1 << 2;
/**
* Container for biometric data
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index bd149fd..b238d77 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -251,9 +251,40 @@
private Executor mExecutor;
private AuthenticationCallback mAuthenticationCallback;
- IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
+ private final IBiometricServiceReceiver mBiometricServiceReceiver =
+ new IBiometricServiceReceiver.Stub() {
+
@Override
- public void onDialogDismissed(int reason) {
+ public void onAuthenticationSucceeded() throws RemoteException {
+ mExecutor.execute(() -> {
+ final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ });
+ }
+
+ @Override
+ public void onAuthenticationFailed() throws RemoteException {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationFailed();
+ });
+ }
+
+ @Override
+ public void onError(int error, String message) throws RemoteException {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationError(error, message);
+ });
+ }
+
+ @Override
+ public void onAcquired(int acquireInfo, String message) throws RemoteException {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message);
+ });
+ }
+
+ @Override
+ public void onDialogDismissed(int reason) throws RemoteException {
// Check the reason and invoke OnClickListener(s) if necessary
if (reason == DISMISSED_REASON_POSITIVE) {
mPositiveButtonInfo.executor.execute(() -> {
@@ -267,40 +298,6 @@
}
};
- IBiometricServiceReceiver mBiometricServiceReceiver =
- new IBiometricServiceReceiver.Stub() {
-
- @Override
- public void onAuthenticationSucceeded(long deviceId) throws RemoteException {
- mExecutor.execute(() -> {
- final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
- mAuthenticationCallback.onAuthenticationSucceeded(result);
- });
- }
-
- @Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
- mExecutor.execute(() -> {
- mAuthenticationCallback.onAuthenticationFailed();
- });
- }
-
- @Override
- public void onError(long deviceId, int error, String message)
- throws RemoteException {
- mExecutor.execute(() -> {
- mAuthenticationCallback.onAuthenticationError(error, message);
- });
- }
-
- @Override
- public void onAcquired(long deviceId, int acquireInfo, String message) {
- mExecutor.execute(() -> {
- mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message);
- });
- }
- };
-
private BiometricPrompt(Context context, Bundle bundle,
ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
mContext = context;
@@ -557,9 +554,8 @@
mExecutor = executor;
mAuthenticationCallback = callback;
final long sessionId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, sessionId, userId,
- mBiometricServiceReceiver, 0 /* flags */, mContext.getOpPackageName(),
- mBundle, mDialogReceiver);
+ mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver,
+ mContext.getOpPackageName(), mBundle);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception while authenticating", e);
mExecutor.execute(() -> {
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index e17feff..53a0761 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -18,7 +18,6 @@
import android.os.Bundle;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceReceiver;
/**
@@ -32,8 +31,7 @@
// Requests authentication. The service choose the appropriate biometric to use, and show
// the corresponding BiometricDialog.
void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+ IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
@@ -46,4 +44,8 @@
// Explicitly set the active user.
void setActiveUser(int userId);
+
+ // Notify BiometricService when <Biometric>Service is ready to start the prepared client.
+ // Client lifecycle is still managed in <Biometric>Service.
+ void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index a6e3696..22ef33e 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -16,12 +16,18 @@
package android.hardware.biometrics;
/**
- * Communication channel from the BiometricService back to BiometricPrompt.
+ * Communication channel from BiometricService back to BiometricPrompt
* @hide
*/
oneway interface IBiometricServiceReceiver {
- void onAuthenticationSucceeded(long deviceId);
- void onAuthenticationFailed(long deviceId);
- void onError(long deviceId, int error, String message);
- void onAcquired(long deviceId, int acquiredInfo, String message);
+ // Notify BiometricPrompt that authentication was successful
+ void onAuthenticationSucceeded();
+ // Noties that authentication failed.
+ void onAuthenticationFailed();
+ // Notify BiometricPrompt that an error has occurred.
+ void onError(int error, String message);
+ // Notifies that a biometric has been acquired.
+ void onAcquired(int acquiredInfo, String message);
+ // Notifies that the SystemUI dialog has been dismissed.
+ void onDialogDismissed(int reason);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
new file mode 100644
index 0000000..180daaf
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package android.hardware.biometrics;
+
+/**
+ * Communication channel from
+ * 1) BiometricDialogImpl (SysUI) back to BiometricService
+ * 2) <Biometric>Service back to BiometricService
+ * Receives messages from the above and does some handling before forwarding to BiometricPrompt
+ * via IBiometricServiceReceiver.
+ * @hide
+ */
+oneway interface IBiometricServiceReceiverInternal {
+ // Notify BiometricService that authentication was successful. If user confirmation is required,
+ // the auth token must be submitted into KeyStore.
+ void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
+ // Notify BiometricService that an error has occurred.
+ void onAuthenticationFailed(int cookie, boolean requireConfirmation);
+ // Notify BiometricService than an error has occured. Forward to the correct receiver depending
+ // on the cookie.
+ void onError(int cookie, int error, String message);
+ // Notifies that a biometric has been acquired.
+ void onAcquired(int acquiredInfo, String message);
+ // Notifies that the SystemUI dialog has been dismissed.
+ void onDialogDismissed(int reason);
+ // Notifies that the user has pressed the "try again" button on SystemUI
+ void onTryAgainPressed();
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7148b12..5e402c7 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -393,7 +393,7 @@
* doesn't have any recommendation for this use case or the recommended configurations
* are invalid.
*/
- public RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
+ public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
@RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) {
if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) &&
(usecase <= RecommendedStreamConfigurationMap.USECASE_RAW)) ||
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index 59e4a33..068c0ce 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -17,6 +17,7 @@
package android.hardware.camera2.params;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.ImageFormat;
@@ -173,7 +174,7 @@
*
* @return Use case id.
*/
- public int getRecommendedUseCase() {
+ public @RecommendedUsecase int getRecommendedUseCase() {
return mUsecase;
}
@@ -326,7 +327,7 @@
* @throws IllegalArgumentException if input size does not exist in the return value of
* getHighSpeedVideoSizes
*/
- public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(Size size) {
+ public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(@NonNull Size size) {
return getUnmodifiableRangeSet(mRecommendedMap.getHighSpeedVideoFpsRangesFor(size));
}
@@ -349,14 +350,14 @@
* For further information refer to {@link StreamConfigurationMap#getHighSpeedVideoSizesFor}.
* </p>
*
- * @param fpsRange one of the FPS range returned by {@link #getHighSpeedVideoFpsRanges()}
+ * @param fpsRange one of the FPS ranges returned by {@link #getHighSpeedVideoFpsRanges()}
* @return A non-modifiable set of video sizes to create high speed capture sessions for high
* speed streaming use cases.
*
* @throws IllegalArgumentException if input FPS range does not exist in the return value of
* getHighSpeedVideoFpsRanges
*/
- public @Nullable Set<Size> getHighSpeedVideoSizesFor(Range<Integer> fpsRange) {
+ public @Nullable Set<Size> getHighSpeedVideoSizesFor(@NonNull Range<Integer> fpsRange) {
return getUnmodifiableSizeSet(mRecommendedMap.getHighSpeedVideoSizesFor(fpsRange));
}
@@ -390,10 +391,8 @@
* 0 if the minimum frame duration is not available.
*
* @throws IllegalArgumentException if {@code format} or {@code size} was not supported
- * @throws NullPointerException if {@code size} was {@code null}
- *
*/
- public long getOutputMinFrameDuration(int format, Size size) {
+ public @IntRange(from = 0) long getOutputMinFrameDuration(int format, @NonNull Size size) {
return mRecommendedMap.getOutputMinFrameDuration(format, size);
}
@@ -409,9 +408,8 @@
* @return a stall duration {@code >=} 0 in nanoseconds
*
* @throws IllegalArgumentException if {@code format} or {@code size} was not supported
- * @throws NullPointerException if {@code size} was {@code null}
*/
- public long getOutputStallDuration(int format, Size size) {
+ public @IntRange(from = 0) long getOutputStallDuration(int format, @NonNull Size size) {
return mRecommendedMap.getOutputStallDuration(format, size);
}
@@ -422,16 +420,12 @@
* </p>
*
* @param klass
- * a non-{@code null} {@link Class} object reference
+ * a {@link Class} object reference
* @return
* a non-modifiable set of supported sizes for {@link ImageFormat#PRIVATE} format,
* or {@code null} if the {@code klass} is not a supported output.
- *
- *
- * @throws NullPointerException if {@code klass} was {@code null}
- *
*/
- public <T> @Nullable Set<Size> getOutputSizes(Class<T> klass) {
+ public <T> @Nullable Set<Size> getOutputSizes(@NonNull Class<T> klass) {
if (mSupportsPrivate) {
return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(klass));
}
@@ -447,16 +441,15 @@
* {@link StreamConfigurationMap#getOutputMinFrameDuration(Class, Size)}.</p>
*
* @param klass
- * a class which has a non-empty array returned by {@link #getOutputSizes(Class)}
+ * a class which has a non-empty array returned by {@link #getOutputSizes(Class)}
* @param size an output-compatible size
* @return a minimum frame duration {@code >} 0 in nanoseconds, or
* 0 if the minimum frame duration is not available.
*
* @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
- * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
- *
*/
- public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) {
+ public <T> @IntRange(from = 0) long getOutputMinFrameDuration(@NonNull final Class<T> klass,
+ @NonNull final Size size) {
if (mSupportsPrivate) {
return mRecommendedMap.getOutputMinFrameDuration(klass, size);
}
@@ -473,14 +466,13 @@
* @param klass
* a class which has a non-empty array returned by {@link #getOutputSizes(Class)}.
* @param size an output-compatible size
- * @return a minimum frame duration {@code >=} 0 in nanoseconds, or 0 if the stall duration is
+ * @return a minimum frame duration {@code >} 0 in nanoseconds, or 0 if the stall duration is
* not available.
*
* @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
- * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
- *
*/
- public <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
+ public <T> @IntRange(from = 0) long getOutputStallDuration(@NonNull final Class<T> klass,
+ @NonNull final Size size) {
if (mSupportsPrivate) {
return mRecommendedMap.getOutputStallDuration(klass, size);
}
@@ -495,14 +487,13 @@
* <p>For more information refer to {@link StreamConfigurationMap#isOutputSupportedFor}.
* </p>
*
- * @param surface a non-{@code null} {@link Surface} object reference
+ * @param surface a {@link Surface} object reference
* @return {@code true} if this is supported, {@code false} otherwise
*
- * @throws NullPointerException if {@code surface} was {@code null}
* @throws IllegalArgumentException if the Surface endpoint is no longer valid
*
*/
- public boolean isOutputSupportedFor(Surface surface) {
+ public boolean isOutputSupportedFor(@NonNull Surface surface) {
return mRecommendedMap.isOutputSupportedFor(surface);
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 9db1f92..9d61f02 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -24,7 +24,7 @@
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
-import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
/**
* Display manager local system service interface.
@@ -126,7 +126,7 @@
* Called by the window manager to perform traversals while holding a
* surface flinger transaction.
*/
- public abstract void performTraversal(SurfaceControl.Transaction t);
+ public abstract void performTraversal(Transaction t);
/**
* Tells the display manager about properties of the display that depend on the windows on it.
@@ -383,6 +383,6 @@
* update the position of its surfaces as part of the same transaction.
*/
public interface DisplayTransactionListener {
- void onDisplayTransaction();
+ void onDisplayTransaction(Transaction t);
}
}
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index df0d46b..f2c50b5 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -19,6 +19,7 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.text.TextUtils;
@@ -71,6 +72,9 @@
// The ID used to uniquely identify this display.
public String uniqueId;
+ // The physical port that the associated display device is connected to.
+ public @Nullable Byte physicalPort;
+
public @ViewportType int type;
public void copyFrom(DisplayViewport viewport) {
@@ -82,6 +86,7 @@
deviceWidth = viewport.deviceWidth;
deviceHeight = viewport.deviceHeight;
uniqueId = viewport.uniqueId;
+ physicalPort = viewport.physicalPort;
type = viewport.type;
}
@@ -113,6 +118,7 @@
&& deviceWidth == other.deviceWidth
&& deviceHeight == other.deviceHeight
&& TextUtils.equals(uniqueId, other.uniqueId)
+ && physicalPort == other.physicalPort
&& type == other.type;
}
@@ -128,6 +134,7 @@
result += prime * result + deviceWidth;
result += prime * result + deviceHeight;
result += prime * result + uniqueId.hashCode();
+ result += prime * result + physicalPort;
result += prime * result + type;
return result;
}
@@ -139,6 +146,7 @@
+ ", valid=" + valid
+ ", displayId=" + displayId
+ ", uniqueId='" + uniqueId + "'"
+ + ", physicalPort=" + physicalPort
+ ", orientation=" + orientation
+ ", logicalFrame=" + logicalFrame
+ ", physicalFrame=" + physicalFrame
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 47df8e8..a15dcec 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -15,9 +15,7 @@
*/
package android.hardware.face;
-import android.os.Bundle;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -32,19 +30,24 @@
void authenticate(IBinder token, long sessionId,
IFaceServiceReceiver receiver, int flags, String opPackageName);
- // This method invokes the BiometricDialog. The arguments are almost the same as above,
- // but should only be called from (BiometricPromptService).
- void authenticateFromService(boolean requireConfirmation, IBinder token, long sessionId,
- int userId, IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId);
+ // This method prepares the service to start authenticating, but doesn't start authentication.
+ // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
+ // called from BiometricService. The additional uid, pid, userId arguments should be determined
+ // by BiometricService. To start authentication after the clients are ready, use
+ // startPreparedClient().
+ void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId,
+ int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+ int cookie, int callingUid, int callingPid, int callingUserId);
+
+ // Starts authentication with the previously prepared client.
+ void startPreparedClient(int cookie);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Same as above, with extra arguments.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ int callingUid, int callingPid, int callingUserId, boolean fromClient);
// Start face enrollment
void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 2662a11..dd6b29d 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -15,9 +15,7 @@
*/
package android.hardware.fingerprint;
-import android.os.Bundle;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -35,22 +33,25 @@
void authenticate(IBinder token, long sessionId, int userId,
IFingerprintServiceReceiver receiver, int flags, String opPackageName);
- // This method invokes the BiometricDialog. The arguments are almost the same as above, except
- // this is protected by the MANAGE_BIOMETRIC signature permission. This method should only be
- // called from BiometricPromptService. The additional uid, pid, userId arguments should be
- // determined by BiometricPromptService.
- void authenticateFromService(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ // This method prepares the service to start authenticating, but doesn't start authentication.
+ // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
+ // called from BiometricService. The additional uid, pid, userId arguments should be determined
+ // by BiometricService. To start authentication after the clients are ready, use
+ // startPreparedClient().
+ void prepareForAuthentication(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie,
int callingUid, int callingPid, int callingUserId);
+ // Starts authentication with the previously prepared client.
+ void startPreparedClient(int cookie);
+
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
// Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
// an additional uid, pid, userid.
void cancelAuthenticationFromService(IBinder token, String opPackageName,
- int callingUid, int callingPid, int callingUserId);
+ int callingUid, int callingPid, int callingUserId, boolean fromClient);
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java
index 539c494..d1190ab 100644
--- a/core/java/android/hardware/location/ContextHubIntentEvent.java
+++ b/core/java/android/hardware/location/ContextHubIntentEvent.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Intent;
@@ -30,6 +31,7 @@
*
* @hide
*/
+@SystemApi
public class ContextHubIntentEvent {
@ContextHubManager.Event private final int mEventType;
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 88fb3de..7639302 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -56,38 +56,28 @@
/**
* An extra of type {@link ContextHubInfo} describing the source of the event.
- *
- * @hide
*/
public static final String EXTRA_CONTEXT_HUB_INFO =
"android.hardware.location.extra.CONTEXT_HUB_INFO";
/**
* An extra of type {@link ContextHubManager.Event} describing the event type.
- *
- * @hide
*/
public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
/**
* An extra of type long describing the ID of the nanoapp an event is for.
- *
- * @hide
*/
public static final String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
/**
* An extra of type int describing the nanoapp-specific abort code.
- *
- * @hide
*/
public static final String EXTRA_NANOAPP_ABORT_CODE =
"android.hardware.location.extra.NANOAPP_ABORT_CODE";
/**
* An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp.
- *
- * @hide
*/
public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
@@ -109,56 +99,41 @@
/**
* An event describing that a nanoapp has been loaded. Contains the EXTRA_NANOAPP_ID extra.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_LOADED = 0;
/**
* An event describing that a nanoapp has been unloaded. Contains the EXTRA_NANOAPP_ID extra.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_UNLOADED = 1;
/**
* An event describing that a nanoapp has been enabled. Contains the EXTRA_NANOAPP_ID extra.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_ENABLED = 2;
/**
* An event describing that a nanoapp has been disabled. Contains the EXTRA_NANOAPP_ID extra.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_DISABLED = 3;
/**
* An event describing that a nanoapp has aborted. Contains the EXTRA_NANOAPP_ID and
* EXTRA_NANOAPP_ABORT_CODE extras.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_ABORTED = 4;
/**
* An event containing a message sent from a nanoapp. Contains the EXTRA_NANOAPP_ID and
* EXTRA_NANOAPP_MESSAGE extras.
- *
- * @hide
*/
public static final int EVENT_NANOAPP_MESSAGE = 5;
/**
* An event describing that the Context Hub has reset.
- *
- * @hide
*/
public static final int EVENT_HUB_RESET = 6;
-
private final Looper mMainLooper;
private final IContextHubService mService;
private Callback mCallback;
@@ -797,14 +772,14 @@
* Creates a ContextHubClient that will receive notifications based on Intent events.
*
* This method should be used instead of {@link #createClient(ContextHubInfo,
- * ContextHubClientCallback)} and the equivalent API if the caller wants to preserve the
- * messaging endpoint of a ContextHubClient, even after a process exits. If the PendingIntent
- * with the provided nanoapp has already been registered at the service previously, then the
- * same ContextHubClient will be regenerated without creating a new client connection at the
- * service. Note that the PendingIntent, nanoapp, and Context Hub must all match in identifying
- * a previously registered ContextHubClient. If a client is regenerated, it can be treated as
- * the same endpoint entity from a nanoapp's perspective, and can be continued to be
- * used to send messages even if the original process has exited.
+ * ContextHubClientCallback)} or {@link #createClient(ContextHubInfo, ContextHubClientCallback,
+ * Executor)} if the caller wants to preserve the messaging endpoint of a ContextHubClient, even
+ * after a process exits. If the PendingIntent with the provided nanoapp has already been
+ * registered at the service, then the same ContextHubClient will be regenerated without
+ * creating a new client connection at the service. Note that the PendingIntent, nanoapp, and
+ * Context Hub must all match in identifying a previously registered ContextHubClient.
+ * If a client is regenerated, the host endpoint identifier attached to messages sent to the
+ * nanoapp remains consistent, even if the original process has exited.
*
* If registered successfully, intents will be delivered regarding events or messages from the
* specified nanoapp from the attached Context Hub. The intent will have an extra
@@ -815,10 +790,11 @@
* each event type, along with event-specific extra fields. The client can also use
* {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
*
- * Intent events will be delivered until it is unregistered through
- * {@link ContextHubClient.close()}. Note that the registration of this
- * ContextHubClient at the Context Hub Service will continued to be maintained until
- * {@link ContextHubClient.close()} is called.
+ * Intent events will be delivered until {@link ContextHubClient.close()} is called. Note that
+ * the registration of this ContextHubClient at the Context Hub Service will be maintained until
+ * {@link ContextHubClient.close()} is called. If {@link PendingIntent.cancel()} is called
+ * on the provided PendingIntent, then the client will be automatically unregistered by the
+ * service.
*
* @param hubInfo the hub to attach this client to
* @param pendingIntent the PendingIntent to register to the client
@@ -828,8 +804,6 @@
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
* @throws IllegalStateException if there were too many registered clients at the service
* @throws NullPointerException if pendingIntent or hubInfo is null
- *
- * @hide
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
@NonNull public ContextHubClient createClient(
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 1999e78..cb82fbe 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -44,6 +44,8 @@
public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10;
public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11;
+ public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12;
+
@IntDef(value = {
NETWORK_CONNECTED,
NETWORK_VALIDATED,
@@ -56,6 +58,7 @@
NETWORK_REVALIDATION_SUCCESS,
NETWORK_FIRST_VALIDATION_PORTAL_FOUND,
NETWORK_REVALIDATION_PORTAL_FOUND,
+ NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND,
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index c7184c0..64314a7 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -400,6 +400,9 @@
* }
* </pre>
*
+ * <p>The work source will be propagated for future outgoing binder transactions
+ * executed on this thread.
+ *
* @param workSource The original UID responsible for the binder call.
* @return token to restore original work source.
* @hide
@@ -423,6 +426,9 @@
/**
* Clears the work source on this thread.
*
+ * <p>The work source will be propagated for future outgoing binder transactions
+ * executed on this thread.
+ *
* @return token to restore original work source.
* @hide
**/
@@ -442,6 +448,9 @@
* Binder.restoreCallingWorkSource(token);
* }
* </pre>
+ *
+ * <p>The work source will be propagated for future outgoing binder transactions
+ * executed on this thread.
* @hide
**/
@CriticalNative
@@ -919,7 +928,9 @@
final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
try {
if (tracingEnabled) {
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
+ final String transactionName = getTransactionName(code);
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
+ + (transactionName != null ? transactionName : code));
}
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 0c1aae8..8904ee6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -657,6 +657,12 @@
public static String DIRECTORY_SCREENSHOTS = "Screenshots";
/**
+ * Standard directory in which to place any audio files which are
+ * audiobooks.
+ */
+ public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
+
+ /**
* List of standard storage directories.
* <p>
* Each of its values have its own constant:
@@ -671,6 +677,7 @@
* <li>{@link #DIRECTORY_DOWNLOADS}
* <li>{@link #DIRECTORY_DCIM}
* <li>{@link #DIRECTORY_DOCUMENTS}
+ * <li>{@link #DIRECTORY_AUDIOBOOKS}
* </ul>
* @hide
*/
@@ -684,7 +691,8 @@
DIRECTORY_MOVIES,
DIRECTORY_DOWNLOADS,
DIRECTORY_DCIM,
- DIRECTORY_DOCUMENTS
+ DIRECTORY_DOCUMENTS,
+ DIRECTORY_AUDIOBOOKS,
};
/**
@@ -709,6 +717,7 @@
/** {@hide} */ public static final int HAS_DOWNLOADS = 1 << 7;
/** {@hide} */ public static final int HAS_DCIM = 1 << 8;
/** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9;
+ /** {@hide} */ public static final int HAS_AUDIOBOOKS = 1 << 10;
/** {@hide} */ public static final int HAS_ANDROID = 1 << 16;
/** {@hide} */ public static final int HAS_OTHER = 1 << 17;
@@ -738,6 +747,7 @@
else if (DIRECTORY_DOWNLOADS.equals(name)) res |= HAS_DOWNLOADS;
else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM;
else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS;
+ else if (DIRECTORY_AUDIOBOOKS.equals(name)) res |= HAS_AUDIOBOOKS;
else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID;
else res |= HAS_OTHER;
}
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 8160338..9280cb9 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -67,7 +67,7 @@
/**
* Register a listener for thermal status change.
- * @param listener the IThermalStatusListener to be notified.
+ * @param listener the {@link android.os.IThermalStatusListener} to be notified.
* @return true if registered successfully.
* {@hide}
*/
@@ -75,7 +75,7 @@
/**
* Unregister a previously-registered listener for thermal status.
- * @param listener the IThermalStatusListener to no longer be notified.
+ * @param listener the {@link android.os.IThermalStatusListener} to no longer be notified.
* @return true if unregistered successfully.
* {@hide}
*/
@@ -86,5 +86,5 @@
* @return status defined in {@link android.os.Temperature}.
* {@hide}
*/
- int getCurrentStatus();
+ int getCurrentThermalStatus();
}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 44b9e311..6de1ff4 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -17,12 +17,6 @@
package android.os;
import static android.system.OsConstants.AF_UNIX;
-import static android.system.OsConstants.O_APPEND;
-import static android.system.OsConstants.O_CREAT;
-import static android.system.OsConstants.O_RDONLY;
-import static android.system.OsConstants.O_RDWR;
-import static android.system.OsConstants.O_TRUNC;
-import static android.system.OsConstants.O_WRONLY;
import static android.system.OsConstants.SEEK_SET;
import static android.system.OsConstants.SOCK_SEQPACKET;
import static android.system.OsConstants.SOCK_STREAM;
@@ -254,8 +248,16 @@
}
/** {@hide} */
- public static ParcelFileDescriptor fromFd(
- FileDescriptor fd, Handler handler, final OnCloseListener listener) throws IOException {
+ public static ParcelFileDescriptor fromPfd(ParcelFileDescriptor pfd, Handler handler,
+ final OnCloseListener listener) throws IOException {
+ final FileDescriptor original = new FileDescriptor();
+ original.setInt$(pfd.detachFd());
+ return fromFd(original, handler, listener);
+ }
+
+ /** {@hide} */
+ public static ParcelFileDescriptor fromFd(FileDescriptor fd, Handler handler,
+ final OnCloseListener listener) throws IOException {
if (handler == null) {
throw new IllegalArgumentException("Handler must not be null");
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 1c1db68..894015f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1008,7 +1008,8 @@
* progress, does nothing. Unlike {@link #nap(long)}, this does not put device to sleep when
* dream ends.
* </p><p>
- * Requires the {@link android.Manifest.permission#WRITE_DREAM_STATE} permission.
+ * Requires the {@link android.Manifest.permission#READ_DREAM_STATE} and
+ * {@link android.Manifest.permission#WRITE_DREAM_STATE} permissions.
* </p>
*
* @param time The time when the request to nap was issued, in the
@@ -1019,7 +1020,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.READ_DREAM_STATE,
+ android.Manifest.permission.WRITE_DREAM_STATE })
public void dream(long time) {
Sandman.startDreamByUserRequest(mContext);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 651caec..2abcb4c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1055,6 +1055,9 @@
*/
public static final native long getPss(int pid);
+ /** @hide */
+ public static final native long[] getRss(int pid);
+
/**
* Specifies the outcome of having started a process.
* @hide
diff --git a/core/java/android/os/RedactingFileDescriptor.java b/core/java/android/os/RedactingFileDescriptor.java
index 60eb5c3..4e5eaac 100644
--- a/core/java/android/os/RedactingFileDescriptor.java
+++ b/core/java/android/os/RedactingFileDescriptor.java
@@ -20,15 +20,18 @@
import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.Os;
-import android.system.OsConstants;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.util.Arrays;
/**
* Variant of {@link FileDescriptor} that allows its creator to specify regions
@@ -40,20 +43,21 @@
private static final String TAG = "RedactingFileDescriptor";
private static final boolean DEBUG = true;
- private final long[] mRedactRanges;
+ private volatile long[] mRedactRanges;
private FileDescriptor mInner = null;
private ParcelFileDescriptor mOuter = null;
- private RedactingFileDescriptor(Context context, File file, long[] redactRanges)
+ private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges)
throws IOException {
mRedactRanges = checkRangesArgument(redactRanges);
try {
try {
- mInner = Os.open(file.getAbsolutePath(), OsConstants.O_RDONLY, 0);
+ mInner = Os.open(file.getAbsolutePath(),
+ FileUtils.translateModePfdToPosix(mode), 0);
mOuter = context.getSystemService(StorageManager.class)
- .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, mCallback);
+ .openProxyFileDescriptor(mode, mCallback);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
@@ -78,16 +82,61 @@
/**
* Open the given {@link File} and returns a {@link ParcelFileDescriptor}
- * that offers a redacted, read-only view of the underlying data.
+ * that offers a redacted view of the underlying data. If a redacted region
+ * is written to, the newly written data can be read back correctly instead
+ * of continuing to be redacted.
*
* @param file The underlying file to open.
+ * @param mode The {@link ParcelFileDescriptor} mode to open with.
* @param redactRanges List of file offsets that should be redacted, stored
* as {@code [start1, end1, start2, end2, ...]}. Start values are
* inclusive and end values are exclusive.
*/
- public static ParcelFileDescriptor open(Context context, File file, long[] redactRanges)
- throws IOException {
- return new RedactingFileDescriptor(context, file, redactRanges).mOuter;
+ public static ParcelFileDescriptor open(Context context, File file, int mode,
+ long[] redactRanges) throws IOException {
+ return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter;
+ }
+
+ /**
+ * Update the given ranges argument to remove any references to the given
+ * offset and length. This is typically used when a caller has written over
+ * a previously redacted region.
+ */
+ @VisibleForTesting
+ public static long[] removeRange(long[] ranges, long start, long end) {
+ if (start == end) {
+ return ranges;
+ } else if (start > end) {
+ throw new IllegalArgumentException();
+ }
+
+ long[] res = EmptyArray.LONG;
+ for (int i = 0; i < ranges.length; i += 2) {
+ if (start <= ranges[i] && end >= ranges[i + 1]) {
+ // Range entirely covered; remove it
+ } else if (start >= ranges[i] && end <= ranges[i + 1]) {
+ // Range partially covered; punch a hole
+ res = Arrays.copyOf(res, res.length + 4);
+ res[res.length - 4] = ranges[i];
+ res[res.length - 3] = start;
+ res[res.length - 2] = end;
+ res[res.length - 1] = ranges[i + 1];
+ } else {
+ // Range might covered; adjust edges if needed
+ res = Arrays.copyOf(res, res.length + 2);
+ if (end >= ranges[i] && end <= ranges[i + 1]) {
+ res[res.length - 2] = Math.max(ranges[i], end);
+ } else {
+ res[res.length - 2] = ranges[i];
+ }
+ if (start >= ranges[i] && start <= ranges[i + 1]) {
+ res[res.length - 1] = Math.min(ranges[i + 1], start);
+ } else {
+ res[res.length - 1] = ranges[i + 1];
+ }
+ }
+ }
+ return res;
}
private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
@@ -126,7 +175,24 @@
@Override
public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
- throw new ErrnoException(TAG, OsConstants.EBADF);
+ int n = 0;
+ while (n < size) {
+ try {
+ final int res = Os.pwrite(mInner, data, n, size - n, offset + n);
+ if (res == 0) {
+ break;
+ } else {
+ n += res;
+ }
+ } catch (InterruptedIOException e) {
+ n += e.bytesTransferred;
+ }
+ }
+
+ // Clear any relevant redaction ranges before returning, since the
+ // writer should have access to see the data they just overwrote
+ mRedactRanges = removeRange(mRedactRanges, offset, offset + n);
+ return n;
}
@Override
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 866bd9a..acb9eac 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -43,6 +43,7 @@
int mTag;
long mElapsedTimeNs;
long mWallClockTimeNs;
+ WorkSource mWorkSource = null;
public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
this.mTag = tag;
@@ -71,6 +72,17 @@
};
/**
+ * Set work source if any.
+ */
+ public void setWorkSource(WorkSource ws) {
+ if (ws.getWorkChains() == null || ws.getWorkChains().size() == 0) {
+ Slog.w(TAG, "Empty worksource!");
+ return;
+ }
+ mWorkSource = ws;
+ }
+
+ /**
* Write a int value.
*/
public void writeInt(int val) {
@@ -119,11 +131,6 @@
mValues.add(val ? 1 : 0);
}
- /**
- * Writes the stored fields to a byte array. Will first write a new-line character to denote
- * END_LIST before writing contents to byte array.
- */
-
public void writeToParcel(Parcel out, int flags) {
if (DEBUG) {
Slog.d(TAG,
@@ -133,6 +140,34 @@
out.writeInt(mTag);
out.writeLong(mElapsedTimeNs);
out.writeLong(mWallClockTimeNs);
+ if (mWorkSource != null) {
+ ArrayList<android.os.WorkSource.WorkChain> workChains = mWorkSource.getWorkChains();
+ // number of chains
+ out.writeInt(workChains.size());
+ for (int i = 0; i < workChains.size(); i++) {
+ android.os.WorkSource.WorkChain wc = workChains.get(i);
+ if (wc.getSize() == 0) {
+ Slog.w(TAG, "Empty work chain.");
+ out.writeInt(0);
+ continue;
+ }
+ if (wc.getUids().length != wc.getTags().length
+ || wc.getUids().length != wc.getSize()) {
+ Slog.w(TAG, "Malformated work chain.");
+ out.writeInt(0);
+ continue;
+ }
+ // number of nodes
+ out.writeInt(wc.getSize());
+ for (int j = 0; j < wc.getSize(); j++) {
+ out.writeInt(wc.getUids()[j]);
+ out.writeString(wc.getTags()[j] == null ? "" : wc.getTags()[j]);
+ }
+ }
+ } else {
+ // no chains
+ out.writeInt(0);
+ }
out.writeInt(mTypes.size());
for (int i = 0; i < mTypes.size(); i++) {
out.writeInt(mTypes.get(i));
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index bf85fbd..5499181 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -28,7 +28,7 @@
*
* @hide
*/
-public class Temperature implements Parcelable {
+public final class Temperature implements Parcelable {
/** Temperature value */
private float mValue;
/** A temperature type from ThermalHAL */
@@ -44,7 +44,7 @@
THROTTLING_MODERATE,
THROTTLING_SEVERE,
THROTTLING_CRITICAL,
- THROTTLING_WARNING,
+ THROTTLING_EMERGENCY,
THROTTLING_SHUTDOWN,
})
@Retention(RetentionPolicy.SOURCE)
@@ -56,7 +56,7 @@
public static final int THROTTLING_MODERATE = ThrottlingSeverity.MODERATE;
public static final int THROTTLING_SEVERE = ThrottlingSeverity.SEVERE;
public static final int THROTTLING_CRITICAL = ThrottlingSeverity.CRITICAL;
- public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
+ public static final int THROTTLING_EMERGENCY = ThrottlingSeverity.EMERGENCY;
public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
@IntDef(prefix = { "TYPE_" }, value = {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 92b3169..abfcfaa 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2463,6 +2463,27 @@
}
/**
+ * Get the parent of a user profile.
+ *
+ * @param user the handle of the user profile
+ *
+ * @return the parent of the user or {@code null} if the user is not profile
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) {
+ UserInfo info = getProfileParent(user.getIdentifier());
+
+ if (info == null) {
+ return null;
+ }
+
+ return UserHandle.of(info.id);
+ }
+
+ /**
* Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
* managed profile don't run, generate notifications, or consume data or battery.
* <p>
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index b42f1c4..d315383 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -25,6 +25,7 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.app.Activity;
@@ -55,6 +56,7 @@
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.sysprop.VoldProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -1494,7 +1496,7 @@
* framework, so no service needs to check for changes during their lifespan
*/
public static boolean isBlockEncrypting() {
- final String state = SystemProperties.get("vold.encrypt_progress", "");
+ final String state = VoldProperties.encrypt_progress().orElse("");
return !"".equalsIgnoreCase(state);
}
@@ -1510,7 +1512,7 @@
* framework, so no service needs to check for changes during their lifespan
*/
public static boolean inCryptKeeperBounce() {
- final String status = SystemProperties.get("vold.decrypt");
+ final String status = VoldProperties.decrypt().orElse("");
return "trigger_restart_min_framework".equals(status);
}
@@ -1533,6 +1535,12 @@
return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
}
+ /** {@hide} */
+ @TestApi
+ public static boolean hasIsolatedStorage() {
+ return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false);
+ }
+
/**
* @deprecated disabled now that FUSE has been replaced by sdcardfs
* @hide
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index 18aea03..a41a644 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -16,6 +16,9 @@
package android.permissionpresenterservice;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
@@ -27,12 +30,8 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteCallback;
-import com.android.internal.os.SomeArgs;
-
import java.util.List;
/**
@@ -63,7 +62,7 @@
@Override
public final void attachBaseContext(Context base) {
super.attachBaseContext(base);
- mHandler = new MyHandler(base.getMainLooper());
+ mHandler = new Handler(base.getMainLooper());
}
/**
@@ -71,7 +70,8 @@
*
* @param packageName The package for which to query.
*/
- public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
+ public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(
+ @NonNull String packageName);
/**
* Revokes the permission {@code permissionName} for app {@code packageName}
@@ -87,61 +87,35 @@
return new IRuntimePermissionPresenter.Stub() {
@Override
public void getAppPermissions(String packageName, RemoteCallback callback) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
- args.arg2 = callback;
- mHandler.obtainMessage(MyHandler.MSG_GET_APP_PERMISSIONS,
- args).sendToTarget();
+ checkNotNull(packageName, "packageName");
+ checkNotNull(callback, "callback");
+
+ mHandler.sendMessage(
+ obtainMessage(RuntimePermissionPresenterService::getAppPermissions,
+ RuntimePermissionPresenterService.this, packageName, callback));
}
@Override
public void revokeRuntimePermission(String packageName, String permissionName) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
- args.arg2 = permissionName;
- mHandler.obtainMessage(MyHandler.MSG_REVOKE_APP_PERMISSION,
- args).sendToTarget();
+ checkNotNull(packageName, "packageName");
+ checkNotNull(permissionName, "permissionName");
+
+ mHandler.sendMessage(
+ obtainMessage(RuntimePermissionPresenterService::onRevokeRuntimePermission,
+ RuntimePermissionPresenterService.this, packageName,
+ permissionName));
}
};
}
- private final class MyHandler extends Handler {
- public static final int MSG_GET_APP_PERMISSIONS = 1;
- public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
- public static final int MSG_REVOKE_APP_PERMISSION = 3;
-
- public MyHandler(Looper looper) {
- super(looper, null, false);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_GET_APP_PERMISSIONS: {
- SomeArgs args = (SomeArgs) msg.obj;
- String packageName = (String) args.arg1;
- RemoteCallback callback = (RemoteCallback) args.arg2;
- args.recycle();
- List<RuntimePermissionPresentationInfo> permissions =
- onGetAppPermissions(packageName);
- if (permissions != null && !permissions.isEmpty()) {
- Bundle result = new Bundle();
- result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT,
- permissions);
- callback.sendResult(result);
- } else {
- callback.sendResult(null);
- }
- } break;
- case MSG_REVOKE_APP_PERMISSION: {
- SomeArgs args = (SomeArgs) msg.obj;
- String packageName = (String) args.arg1;
- String permissionName = (String) args.arg2;
- args.recycle();
-
- onRevokeRuntimePermission(packageName, permissionName);
- } break;
- }
+ private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
+ List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
+ if (permissions != null && !permissions.isEmpty()) {
+ Bundle result = new Bundle();
+ result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(null);
}
}
}
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index 76607e9..8e37559 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -660,7 +660,22 @@
if (familyBuilder == null) {
return null;
}
- return new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
+
+ final FontFamily family = familyBuilder.build();
+
+ final FontStyle normal = new FontStyle(FontStyle.FONT_WEIGHT_NORMAL,
+ FontStyle.FONT_SLANT_UPRIGHT);
+ Font bestFont = family.getFont(0);
+ int bestScore = normal.getMatchScore(bestFont.getStyle());
+ for (int i = 1; i < family.getSize(); ++i) {
+ final Font candidate = family.getFont(i);
+ final int score = normal.getMatchScore(candidate.getStyle());
+ if (score < bestScore) {
+ bestFont = candidate;
+ bestScore = score;
+ }
+ }
+ return new Typeface.CustomFallbackBuilder(family).setStyle(bestFont.getStyle()).build();
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 291891e..0299e41 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -37,6 +37,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
+import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -121,6 +122,8 @@
public static final String PARAM_INCLUDE_PENDING = "includePending";
/** {@hide} */
public static final String PARAM_PROGRESS = "progress";
+ /** {@hide} */
+ public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal";
/**
* Activity Action: Launch a music player.
@@ -478,6 +481,24 @@
}
/**
+ * Update the given {@link Uri} to indicate that the caller requires the
+ * original file contents when calling
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+ * <p>
+ * This can be useful when the caller wants to ensure they're backing up the
+ * exact bytes of the underlying media, without any Exif redaction being
+ * performed.
+ * <p>
+ * If the original file contents cannot be provided, a
+ * {@link UnsupportedOperationException} will be thrown when the returned
+ * {@link Uri} is used, such as when the caller doesn't hold
+ * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}.
+ */
+ public static @NonNull Uri setRequireOriginal(@NonNull Uri uri) {
+ return uri.buildUpon().appendQueryParameter(PARAM_REQUIRE_ORIGINAL, "1").build();
+ }
+
+ /**
* Create a new pending media item using the given parameters. Pending items
* are expected to have a short lifetime, and owners should either
* {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
@@ -572,6 +593,34 @@
public void setSecondaryDirectory(@Nullable String secondaryDirectory) {
this.secondaryDirectory = secondaryDirectory;
}
+
+ /**
+ * Optionally set the Uri from where the file has been downloaded. This is used
+ * for files being added to {@link Downloads} table.
+ *
+ * @see DownloadColumns#DOWNLOAD_URI
+ */
+ public void setDownloadUri(@Nullable Uri downloadUri) {
+ if (downloadUri == null) {
+ this.insertValues.remove(DownloadColumns.DOWNLOAD_URI);
+ } else {
+ this.insertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString());
+ }
+ }
+
+ /**
+ * Optionally set the Uri indicating HTTP referer of the file. This is used for
+ * files being added to {@link Downloads} table.
+ *
+ * @see DownloadColumns#REFERER_URI
+ */
+ public void setRefererUri(@Nullable Uri refererUri) {
+ if (refererUri == null) {
+ this.insertValues.remove(DownloadColumns.REFERER_URI);
+ } else {
+ this.insertValues.put(DownloadColumns.REFERER_URI, refererUri.toString());
+ }
+ }
}
/**
@@ -763,7 +812,7 @@
* Type: BOOLEAN
*
* @see MediaStore#createPending(Context, PendingParams)
- * @see MediaStore#QUERY_ARG_INCLUDE_PENDING
+ * @see MediaStore#PARAM_INCLUDE_PENDING
*/
public static final String IS_PENDING = "is_pending";
@@ -927,6 +976,12 @@
* Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file.
*/
public static final int MEDIA_TYPE_PLAYLIST = 4;
+
+ /**
+ * Column indicating if the file is part of Downloads collection.
+ * @hide
+ */
+ public static final String IS_DOWNLOAD = "is_download";
}
}
@@ -940,6 +995,80 @@
public static final Point MICRO_SIZE = new Point(96, 96);
}
+ /** Column fields for downloaded files used in {@link Downloads} table */
+ public interface DownloadColumns extends MediaColumns {
+ /**
+ * Uri indicating where the file has been downloaded from.
+ * <p>
+ * Type: TEXT
+ */
+ String DOWNLOAD_URI = "download_uri";
+
+ /**
+ * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
+ * <p>
+ * Type: TEXT
+ */
+ String REFERER_URI = "referer_uri";
+ }
+
+ /**
+ * Container for downloaded files.
+ *
+ * <p>
+ * Querying for downloads from this table will return files contributed via
+ * {@link PendingSession} and also ones which were downloaded using
+ * {@link android.app.DownloadManager} APIs.
+ */
+ public static final class Downloads implements DownloadColumns {
+ private Downloads() {}
+
+ /**
+ * The content:// style URI for the internal storage.
+ */
+ public static final Uri INTERNAL_CONTENT_URI =
+ getContentUri("internal");
+
+ /**
+ * The content:// style URI for the "primary" external storage
+ * volume.
+ */
+ public static final Uri EXTERNAL_CONTENT_URI =
+ getContentUri("external");
+
+ /**
+ * Get the content:// style URI for the downloads table on the
+ * given volume.
+ *
+ * @param volumeName the name of the volume to get the URI for
+ * @return the URI to the image media table on the given volume
+ */
+ public static Uri getContentUri(String volumeName) {
+ return AUTHORITY_URI.buildUpon().appendPath(volumeName)
+ .appendPath("downloads").build();
+ }
+
+ /** @hide */
+ public static Uri getContentUriForPath(@NonNull String path) {
+ return getContentUri(getVolumeNameForPath(path));
+ }
+ }
+
+ private static String getVolumeNameForPath(@NonNull String path) {
+ final StorageManager sm = AppGlobals.getInitialApplication()
+ .getSystemService(StorageManager.class);
+ final StorageVolume sv = sm.getStorageVolume(new File(path));
+ if (sv != null) {
+ if (sv.isPrimary()) {
+ return VOLUME_EXTERNAL;
+ } else {
+ return sv.getUuid();
+ }
+ } else {
+ return VOLUME_INTERNAL;
+ }
+ }
+
/**
* This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
* to be accessed elsewhere.
@@ -1038,13 +1167,25 @@
/**
* The latitude where the image was captured.
* <P>Type: DOUBLE</P>
+ *
+ * @deprecated location details are no longer indexed for privacy
+ * reasons, and this value is now always {@code null}.
+ * You can still manually obtain location metadata using
+ * {@link ExifInterface#getLatLong(float[])}.
*/
+ @Deprecated
public static final String LATITUDE = "latitude";
/**
* The longitude where the image was captured.
* <P>Type: DOUBLE</P>
+ *
+ * @deprecated location details are no longer indexed for privacy
+ * reasons, and this value is now always {@code null}.
+ * You can still manually obtain location metadata using
+ * {@link ExifInterface#getLatLong(float[])}.
*/
+ @Deprecated
public static final String LONGITUDE = "longitude";
/**
@@ -1565,6 +1706,12 @@
public static final String IS_NOTIFICATION = "is_notification";
/**
+ * Non-zero if the audio file is an audiobook
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String IS_AUDIOBOOK = "is_audiobook";
+
+ /**
* The genre of the audio file, if any
* <P>Type: TEXT</P>
* Does not exist in the database - only used by the media scanner for inserts.
@@ -1671,18 +1818,7 @@
* access this path.
*/
public static @Nullable Uri getContentUriForPath(@NonNull String path) {
- final StorageManager sm = AppGlobals.getInitialApplication()
- .getSystemService(StorageManager.class);
- final StorageVolume sv = sm.getStorageVolume(new File(path));
- if (sv != null) {
- if (sv.isPrimary()) {
- return EXTERNAL_CONTENT_URI;
- } else {
- return getContentUri(sv.getUuid());
- }
- } else {
- return INTERNAL_CONTENT_URI;
- }
+ return getContentUri(getVolumeNameForPath(path));
}
/**
@@ -2287,13 +2423,25 @@
/**
* The latitude where the video was captured.
* <P>Type: DOUBLE</P>
+ *
+ * @deprecated location details are no longer indexed for privacy
+ * reasons, and this value is now always {@code null}.
+ * You can still manually obtain location metadata using
+ * {@link ExifInterface#getLatLong(float[])}.
*/
+ @Deprecated
public static final String LATITUDE = "latitude";
/**
* The longitude where the video was captured.
* <P>Type: DOUBLE</P>
+ *
+ * @deprecated location details are no longer indexed for privacy
+ * reasons, and this value is now always {@code null}.
+ * You can still manually obtain location metadata using
+ * {@link ExifInterface#getLatLong(float[])}.
*/
+ @Deprecated
public static final String LONGITUDE = "longitude";
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dbdeb70..74ec0b9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5075,6 +5075,7 @@
* @hide
*/
@SystemApi
+ @TestApi
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static void resetToDefaults(@NonNull ContentResolver resolver,
@Nullable String tag) {
@@ -6278,6 +6279,7 @@
*
* @hide
*/
+ @SystemApi
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
"accessibility_display_magnification_navbar_enabled";
@@ -7967,6 +7969,14 @@
"managed_profile_contact_remote_search";
/**
+ * Whether parent profile can access remote calendar data in managed profile.
+ *
+ * @hide
+ */
+ public static final String CROSS_PROFILE_CALENDAR_ENABLED =
+ "cross_profile_calendar_enabled";
+
+ /**
* Whether or not the automatic storage manager is enabled and should run on the device.
*
* @hide
@@ -8224,6 +8234,24 @@
public static final String NOTIFICATION_NEW_INTERRUPTION_MODEL = "new_interruption_model";
/**
+ * How often to check for location access.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS =
+ "location_access_check_interval_millis";
+
+ /**
+ * Delay between granting location access and checking it.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
+ "location_access_check_delay_millis";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -10780,6 +10808,41 @@
public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
/**
+ * The threshold value for the number of consecutive dns timeout events received to be a
+ * signal of data stall. Set the value to 0 or less than 0 to disable. Note that the value
+ * should be larger than 0 if the DNS data stall detection is enabled.
+ *
+ * @hide
+ */
+ public static final String DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD =
+ "data_stall_consecutive_dns_timeout_threshold";
+
+ /**
+ * The minimal time interval in milliseconds for data stall reevaluation.
+ *
+ * @hide
+ */
+ public static final String DATA_STALL_MIN_EVALUATE_INTERVAL =
+ "data_stall_min_evaluate_interval";
+
+ /**
+ * DNS timeouts older than this timeout (in milliseconds) are not considered for detecting
+ * a data stall.
+ *
+ * @hide
+ */
+ public static final String DATA_STALL_VALID_DNS_TIME_THRESHOLD =
+ "data_stall_valid_dns_time_threshold";
+
+ /**
+ * Which data stall detection signal to use. Possible values are a union of the powers of 2
+ * of DATA_STALL_EVALUATION_TYPE_*.
+ *
+ * @hide
+ */
+ public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
+
+ /**
* Whether network service discovery is enabled.
*
* @hide
@@ -11000,6 +11063,16 @@
= "activity_starts_logging_enabled";
/**
+ * Feature flag to enable or disable the background activity starts.
+ * When disabled, apps aren't allowed to start activities unless they're in the foreground.
+ * Type: int (0 for false, 1 for true)
+ * Default: 1
+ * @hide
+ */
+ public static final String BACKGROUND_ACTIVITY_STARTS_ENABLED =
+ "background_activity_starts_enabled";
+
+ /**
* @hide
* @see com.android.server.appbinding.AppBindingConstants
*/
@@ -11565,7 +11638,7 @@
/**
* Whether or not show hidden launcher icon apps feature is enabled.
* Type: int (0 for false, 1 for true)
- * Default: 0
+ * Default: 1
* @hide
*/
public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED =
@@ -12765,6 +12838,23 @@
"max_sound_trigger_detection_service_ops_per_day";
/**
+ * Property used by {@code com.android.server.SystemServer} on start to decide whether
+ * the Smart Suggestions service should be created or not
+ *
+ * <p>By default it should *NOT* be set (in which case the decision is based on whether
+ * the OEM provides an implementation for the service), but it can be overridden to:
+ *
+ * <ul>
+ * <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency.
+ * <li>Enable the CTS tests to be run on AOSP builds
+ * </ul>
+ *
+ * @hide
+ */
+ public static final String SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED =
+ "smart_suggestions_service_explicitly_enabled";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
@@ -13437,6 +13527,13 @@
*/
public static final String WARNING_TEMPERATURE = "warning_temperature";
+
+ /**
+ * USB Temperature at which the high temperature alarm notification should be shown.
+ * @hide
+ */
+ public static final String USB_ALARM_TEMPERATURE = "usb_alarm_temperature";
+
/**
* Whether the diskstats logging task is enabled/disabled.
* @hide
@@ -13718,6 +13815,15 @@
*/
public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
+
+ /**
+ * Whether we've enabled native flags health check on this device. Takes effect on
+ * reboot. The value "1" enables native flags health check; otherwise it's disabled.
+ * @hide
+ */
+ public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED =
+ "native_flags_health_check_enabled";
+
}
/**
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
similarity index 69%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
index 5ca9d15..23d607d 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -15,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+
+package android.service.intelligence;
+
+parcelable ContentCaptureEventsRequest;
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.java b/core/java/android/service/intelligence/ContentCaptureEventsRequest.java
new file mode 100644
index 0000000..bc5b92b
--- /dev/null
+++ b/core/java/android/service/intelligence/ContentCaptureEventsRequest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+package android.service.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.intelligence.ContentCaptureEvent;
+
+import java.util.List;
+
+/**
+ * Batch of content capture events.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ContentCaptureEventsRequest implements Parcelable {
+
+ private final List<ContentCaptureEvent> mEvents;
+
+ /** @hide */
+ public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
+ mEvents = events;
+ }
+
+ /**
+ * Gets the events.
+ */
+ @NonNull
+ public List<ContentCaptureEvent> getEvents() {
+ return mEvents;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedList(mEvents, flags);
+ }
+
+ public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
+ new Parcelable.Creator<ContentCaptureEventsRequest>() {
+
+ @Override
+ public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
+ return new ContentCaptureEventsRequest(parcel
+ .createTypedArrayList(ContentCaptureEvent.CREATOR));
+ }
+
+ @Override
+ public ContentCaptureEventsRequest[] newArray(int size) {
+ return new ContentCaptureEventsRequest[size];
+ }
+ };
+}
diff --git a/core/java/android/service/intelligence/FillController.java b/core/java/android/service/intelligence/FillController.java
index c5e1242..4a9c85d 100644
--- a/core/java/android/service/intelligence/FillController.java
+++ b/core/java/android/service/intelligence/FillController.java
@@ -15,12 +15,12 @@
*/
package android.service.intelligence;
-import static android.service.intelligence.IntelligenceService.DEBUG;
+import static android.service.intelligence.SmartSuggestionsService.DEBUG;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
-import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
import android.util.Log;
import android.util.Pair;
import android.view.autofill.AutofillId;
diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java
index 95e9224..f68db9d 100644
--- a/core/java/android/service/intelligence/FillRequest.java
+++ b/core/java/android/service/intelligence/FillRequest.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
import android.view.autofill.AutofillId;
/**
diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java
index 4ea07bf..309f6a1 100644
--- a/core/java/android/service/intelligence/FillWindow.java
+++ b/core/java/android/service/intelligence/FillWindow.java
@@ -15,7 +15,7 @@
*/
package android.service.intelligence;
-import static android.service.intelligence.IntelligenceService.DEBUG;
+import static android.service.intelligence.SmartSuggestionsService.DEBUG;
import android.annotation.LongDef;
import android.annotation.NonNull;
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
index e2260d7..d6b3107 100644
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -17,6 +17,7 @@
package android.service.intelligence;
import android.os.IBinder;
+import android.service.intelligence.ContentCaptureEventsRequest;
import android.service.intelligence.InteractionSessionId;
import android.service.intelligence.InteractionContext;
import android.service.intelligence.SnapshotData;
@@ -26,19 +27,19 @@
import java.util.List;
-
/**
* Interface from the system to an intelligence service.
*
* @hide
*/
+ // TODO(b/111276913): rename / update javadoc (once the final name is defined)
oneway interface IIntelligenceService {
// Called when session is created (context not null) or destroyed (context null)
void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId);
- void onContentCaptureEvents(in InteractionSessionId sessionId,
- in List<ContentCaptureEvent> events);
+ void onContentCaptureEventsRequest(in InteractionSessionId sessionId,
+ in ContentCaptureEventsRequest request);
void onActivitySnapshot(in InteractionSessionId sessionId,
in SnapshotData snapshotData);
diff --git a/core/java/android/service/intelligence/InteractionContext.java b/core/java/android/service/intelligence/InteractionContext.java
index 0cc377b..7f4283d 100644
--- a/core/java/android/service/intelligence/InteractionContext.java
+++ b/core/java/android/service/intelligence/InteractionContext.java
@@ -37,7 +37,7 @@
/**
* Flag used to indicate that the app explicitly disabled content capture for the activity
* (using
- * {@link android.view.intelligence.IntelligenceManager#setContentCaptureEnabled()}),
+ * {@link android.view.intelligence.ContentCaptureManager#setContentCaptureEnabled()}),
* in which case the service will just receive activity-level events.
*/
public static final int FLAG_DISABLED_BY_APP = 0x1;
diff --git a/core/java/android/service/intelligence/PresentationParams.java b/core/java/android/service/intelligence/PresentationParams.java
index c59069b..9530309 100644
--- a/core/java/android/service/intelligence/PresentationParams.java
+++ b/core/java/android/service/intelligence/PresentationParams.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.Rect;
-import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
import android.util.DebugUtils;
import android.view.View;
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/SmartSuggestionsService.java
similarity index 71%
rename from core/java/android/service/intelligence/IntelligenceService.java
rename to core/java/android/service/intelligence/SmartSuggestionsService.java
index 040e25e..0e29e70 100644
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ b/core/java/android/service/intelligence/SmartSuggestionsService.java
@@ -19,8 +19,10 @@
import android.annotation.CallSuper;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
+import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Rect;
import android.os.CancellationSignal;
@@ -43,31 +45,31 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
- * A service used to capture the content of the screen.
- *
- * <p>The data collected by this service can be analyzed and combined with other sources to provide
- * contextual data in other areas of the system such as Autofill.
+ * A service used to capture the content of the screen to provide contextual data in other areas of
+ * the system such as Autofill.
*
* @hide
*/
@SystemApi
-public abstract class IntelligenceService extends Service {
+public abstract class SmartSuggestionsService extends Service {
- private static final String TAG = "IntelligenceService";
+ private static final String TAG = "SmartSuggestionsService";
// TODO(b/111330312): STOPSHIP use dynamic value, or change to false
static final boolean DEBUG = true;
+ static final boolean VERBOSE = false;
/**
* The {@link Intent} that must be declared as handled by the service.
* To be supported, the service must also require the
- * {@link android.Manifest.permission#BIND_INTELLIGENCE_SERVICE} permission so
+ * {@link android.Manifest.permission#BIND_SMART_SUGGESTIONS_SERVICE} permission so
* that other applications can not abuse it.
*/
public static final String SERVICE_INTERFACE =
- "android.service.intelligence.IntelligenceService";
+ "android.service.intelligence.SmartSuggestionsService";
private Handler mHandler;
@@ -80,21 +82,21 @@
throws RemoteException {
if (context != null) {
mHandler.sendMessage(
- obtainMessage(IntelligenceService::onCreateInteractionSession,
- IntelligenceService.this, context, sessionId));
+ obtainMessage(SmartSuggestionsService::onCreateInteractionSession,
+ SmartSuggestionsService.this, context, sessionId));
} else {
mHandler.sendMessage(
- obtainMessage(IntelligenceService::onDestroyInteractionSession,
- IntelligenceService.this, sessionId));
+ obtainMessage(SmartSuggestionsService::onDestroyInteractionSession,
+ SmartSuggestionsService.this, sessionId));
}
}
@Override
- public void onContentCaptureEvents(InteractionSessionId sessionId,
- List<ContentCaptureEvent> events) {
+ public void onContentCaptureEventsRequest(InteractionSessionId sessionId,
+ ContentCaptureEventsRequest request) {
mHandler.sendMessage(
- obtainMessage(IntelligenceService::onContentCaptureEvent,
- IntelligenceService.this, sessionId, events));
+ obtainMessage(SmartSuggestionsService::onContentCaptureEventsRequest,
+ SmartSuggestionsService.this, sessionId, request));
}
@@ -102,22 +104,22 @@
public void onActivitySnapshot(InteractionSessionId sessionId,
SnapshotData snapshotData) {
mHandler.sendMessage(
- obtainMessage(IntelligenceService::onActivitySnapshot,
- IntelligenceService.this, sessionId, snapshotData));
+ obtainMessage(SmartSuggestionsService::onActivitySnapshot,
+ SmartSuggestionsService.this, sessionId, snapshotData));
}
@Override
public void onAutofillRequest(InteractionSessionId sessionId, IBinder client,
int autofilSessionId, AutofillId focusedId) {
- mHandler.sendMessage(obtainMessage(IntelligenceService::handleOnAutofillRequest,
- IntelligenceService.this, sessionId, client, autofilSessionId, focusedId));
+ mHandler.sendMessage(obtainMessage(SmartSuggestionsService::handleOnAutofillRequest,
+ SmartSuggestionsService.this, sessionId, client, autofilSessionId, focusedId));
}
@Override
public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) {
mHandler.sendMessage(
- obtainMessage(IntelligenceService::handleOnDestroyAutofillWindowsRequest,
- IntelligenceService.this, sessionId));
+ obtainMessage(SmartSuggestionsService::handleOnDestroyAutofillWindowsRequest,
+ SmartSuggestionsService.this, sessionId));
}
};
@@ -139,25 +141,92 @@
}
/**
+ * Explicitly limits content capture to the given packages and activities.
+ *
+ * <p>When the whitelist is set, it overrides the values passed to
+ * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}
+ * and {@link #setPackageContentCaptureEnabled(String, boolean)}.
+ *
+ * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+ *
+ * <p>Useful when the service wants to restrict content capture to a category of apps, like
+ * chat apps. For example, if the service wants to support view captures on all activities of
+ * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
+ * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
+ * Arrays.asList(new ComponentName("ChatApp2", "act1"),
+ * new ComponentName("ChatApp2", "act2")));}
+ */
+ public final void setContentCaptureWhitelist(@Nullable List<String> packages,
+ @Nullable List<ComponentName> activities) {
+ //TODO(b/111276913): implement
+ }
+
+ /**
+ * Defines whether content capture should be enabled for activities with such
+ * {@link android.content.ComponentName}.
+ *
+ * <p>Useful to blacklist a particular activity.
+ */
+ public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
+ boolean enabled) {
+ //TODO(b/111276913): implement
+ }
+
+ /**
+ * Defines whether content capture should be enabled for activities of the app with such
+ * {@code packageName}.
+ *
+ * <p>Useful to blacklist any activity from a particular app.
+ */
+ public final void setPackageContentCaptureEnabled(@NonNull String packageName,
+ boolean enabled) {
+ //TODO(b/111276913): implement
+ }
+
+ /**
+ * Gets the activities where content capture was disabled by
+ * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
+ */
+ @NonNull
+ public final Set<ComponentName> getContentCaptureDisabledActivities() {
+ //TODO(b/111276913): implement
+ return null;
+ }
+
+ /**
+ * Gets the apps where content capture was disabled by
+ * {@link #setPackageContentCaptureEnabled(String, boolean)}.
+ */
+ @NonNull
+ public final Set<String> getContentCaptureDisabledPackages() {
+ //TODO(b/111276913): implement
+ return null;
+ }
+
+ /**
* Creates a new interaction session.
*
* @param context interaction context
* @param sessionId the session's Id
*/
public void onCreateInteractionSession(@NonNull InteractionContext context,
- @NonNull InteractionSessionId sessionId) {}
+ @NonNull InteractionSessionId sessionId) {
+ if (VERBOSE) {
+ Log.v(TAG, "onCreateInteractionSession(id=" + sessionId + ", ctx=" + context + ")");
+ }
+ }
/**
* Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
* session.
*
* @param sessionId the session's Id
- * @param events the events
+ * @param request the events
*/
// TODO(b/111276913): rename to onContentCaptureEvents or something like that; also, pass a
// Request object so it can be extended
- public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
- @NonNull List<ContentCaptureEvent> events);
+ public abstract void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
+ @NonNull ContentCaptureEventsRequest request);
private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
@NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) {
@@ -242,8 +311,7 @@
}
/**
- * Notifies the service of {@link IntelligenceSnapshotData snapshot data} associated with a
- * session.
+ * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
*
* @param sessionId the session's Id
* @param snapshotData the data
@@ -256,7 +324,11 @@
*
* @param sessionId the id of the session to destroy
*/
- public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {}
+ public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {
+ if (VERBOSE) {
+ Log.v(TAG, "onDestroyInteractionSession(id=" + sessionId + ")");
+ }
+ }
/** @hide */
static final class AutofillProxy {
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index ab94f43..1ddc099e 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -16,6 +16,7 @@
package android.service.notification;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.content.pm.ParceledListSlice;
@@ -50,4 +51,5 @@
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
void onNotificationDirectReply(String key);
void onSuggestedReplySent(String key, in CharSequence reply, int source);
+ void onActionClicked(String key, in Notification.Action action, int source);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 68da83f..c850a4e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -19,9 +19,11 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -189,11 +191,20 @@
* Implement this to know when a suggested reply is sent.
* @param key the notification key
* @param reply the reply that is just sent
- * @param source the source of the reply, e.g. SOURCE_FROM_APP
+ * @param source the source that provided the reply, e.g. SOURCE_FROM_APP
*/
public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {}
/**
+ * Implement this to know when an action is clicked.
+ * @param key the notification key
+ * @param action the action that is just clicked
+ * @param source the source that provided the action, e.g. SOURCE_FROM_APP
+ */
+ public void onActionClicked(String key, @Nullable Notification.Action action, int source) {
+ }
+
+ /**
* Updates a notification. N.B. this won’t cause
* an existing notification to alert, but might allow a future update to
* this notification to alert.
@@ -317,6 +328,15 @@
args.argi2 = source;
mHandler.obtainMessage(MyHandler.MSG_ON_SUGGESTED_REPLY_SENT, args).sendToTarget();
}
+
+ @Override
+ public void onActionClicked(String key, Notification.Action action, int source) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.arg2 = action;
+ args.argi2 = source;
+ mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_CLICKED, args).sendToTarget();
+ }
}
private final class MyHandler extends Handler {
@@ -326,6 +346,7 @@
public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
+ public static final int MSG_ON_ACTION_CLICKED = 7;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -395,6 +416,15 @@
onSuggestedReplySent(key, reply, source);
break;
}
+ case MSG_ON_ACTION_CLICKED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ Notification.Action action = (Notification.Action) args.arg2;
+ int source = args.argi2;
+ args.recycle();
+ onActionClicked(key, action, source);
+ break;
+ }
}
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 756a7c6..1fe97b7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1382,6 +1382,11 @@
}
@Override
+ public void onActionClicked(String key, Notification.Action action, int source) {
+ // no-op in the listener
+ }
+
+ @Override
public void onNotificationChannelModification(String pkgName, UserHandle user,
NotificationChannel channel,
@ChannelOrGroupModificationTypes int modificationType) {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 8371c31b..0e2ae83 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -806,7 +806,7 @@
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
- messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders());
+ callSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders());
}
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS,
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 43ab8dc..194147c 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -859,6 +859,27 @@
/**
* @hide
*/
+ public boolean areValuesSet() {
+ return getPriorityCategoryReminders() != STATE_UNSET
+ || getPriorityCategoryEvents() != STATE_UNSET
+ || getPriorityCategoryMessages() != STATE_UNSET
+ || getPriorityCategoryCalls() != STATE_UNSET
+ || getPriorityCategoryRepeatCallers() != STATE_UNSET
+ || getPriorityCategoryAlarms() != STATE_UNSET
+ || getPriorityCategoryMedia() != STATE_UNSET
+ || getPriorityCategorySystem() != STATE_UNSET
+ || getVisualEffectFullScreenIntent() != STATE_UNSET
+ || getVisualEffectLights() != STATE_UNSET
+ || getVisualEffectPeek() != STATE_UNSET
+ || getVisualEffectStatusBar() != STATE_UNSET
+ || getVisualEffectBadge() != STATE_UNSET
+ || getVisualEffectAmbient() != STATE_UNSET
+ || getVisualEffectNotificationList() != STATE_UNSET;
+ }
+
+ /**
+ * @hide
+ */
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
diff --git a/core/java/android/service/oemlock/IOemLockService.aidl b/core/java/android/service/oemlock/IOemLockService.aidl
index d5e10d6..99cffc5 100644
--- a/core/java/android/service/oemlock/IOemLockService.aidl
+++ b/core/java/android/service/oemlock/IOemLockService.aidl
@@ -22,6 +22,8 @@
* @hide
*/
interface IOemLockService {
+ String getLockName();
+
void setOemUnlockAllowedByCarrier(boolean allowed, in byte[] signature);
boolean isOemUnlockAllowedByCarrier();
diff --git a/core/java/android/service/oemlock/OemLockManager.java b/core/java/android/service/oemlock/OemLockManager.java
index f0d6603..029d645 100644
--- a/core/java/android/service/oemlock/OemLockManager.java
+++ b/core/java/android/service/oemlock/OemLockManager.java
@@ -44,6 +44,23 @@
}
/**
+ * Returns a vendor specific name for the OEM lock.
+ *
+ * This value is used to identify the security protocol used by locks.
+ *
+ * @return The name of the OEM lock or {@code null} if failed to get the name.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE)
+ @Nullable
+ public String getLockName() {
+ try {
+ return mService.getLockName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets whether the carrier has allowed this device to be OEM unlocked.
*
* Depending on the implementation, the validity of the request might need to be proved. This
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index dccce40..ebce484 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -27,7 +27,7 @@
void setDesiredSize(int width, int height);
void setDisplayPadding(in Rect padding);
void setVisibility(boolean visible);
- void setInAmbientMode(boolean inAmbientDisplay, boolean animated);
+ void setInAmbientMode(boolean inAmbientDisplay, long animationDuration);
void dispatchPointer(in MotionEvent event);
void dispatchWallpaperCommand(String action, int x, int y,
int z, in Bundle extras);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f6bb762..a095b0d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.Service;
import android.app.WallpaperColors;
@@ -56,6 +57,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
+import android.view.InsetsState;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -184,6 +186,7 @@
final DisplayCutout.ParcelableWrapper mDisplayCutout =
new DisplayCutout.ParcelableWrapper();
DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
+ final InsetsState mInsetsState = new InsetsState();
final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
final WindowManager.LayoutParams mLayout
@@ -440,7 +443,9 @@
/**
* Returns true if this engine is running in ambient mode -- that is,
* it is being shown in low power mode, on always on display.
+ * @hide
*/
+ @SystemApi
public boolean isInAmbientMode() {
return mIsInAmbientMode;
}
@@ -566,14 +571,16 @@
* Called when the device enters or exits ambient mode.
*
* @param inAmbientMode {@code true} if in ambient mode.
- * @param animated {@code true} if you'll have the opportunity of animating your transition
- * {@code false} when the wallpaper should present its ambient version
- * immediately.
+ * @param animationDuration How long the transition animation to change the ambient state
+ * should run, in milliseconds. If 0 is passed as the argument
+ * here, the state should be switched immediately.
*
* @see #isInAmbientMode()
* @see WallpaperInfo#supportsAmbientMode()
+ * @hide
*/
- public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ @SystemApi
+ public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
}
/**
@@ -803,9 +810,11 @@
mLayout.windowAnimations =
com.android.internal.R.style.Animation_Wallpaper;
mInputChannel = new InputChannel();
+
if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
- mOutsets, mDisplayCutout, mInputChannel) < 0) {
+ mOutsets, mDisplayCutout, mInputChannel,
+ mInsetsState) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
}
@@ -831,7 +840,8 @@
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
- mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
+ mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+ mInsetsState);
if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
+ ", frame=" + mWinFrame);
@@ -1044,19 +1054,19 @@
* message sent from handler.
*
* @param inAmbientMode {@code true} if in ambient mode.
- * @param animated {@code true} if the transition will be animated.
+ * @param animationDuration For how long the transition will last, in ms.
* @hide
*/
@VisibleForTesting
- public void doAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
if (!mDestroyed) {
if (DEBUG) {
Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", "
- + animated + "): " + this);
+ + animationDuration + "): " + this);
}
mIsInAmbientMode = inAmbientMode;
if (mCreated) {
- onAmbientModeChanged(inAmbientMode, animated);
+ onAmbientModeChanged(inAmbientMode, animationDuration);
}
}
}
@@ -1315,10 +1325,10 @@
}
@Override
- public void setInAmbientMode(boolean inAmbientDisplay, boolean animated)
+ public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration)
throws RemoteException {
- Message msg = mCaller.obtainMessageII(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
- animated ? 1 : 0);
+ Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
+ animationDuration);
mCaller.sendMessage(msg);
}
@@ -1389,7 +1399,7 @@
return;
}
case DO_IN_AMBIENT_MODE: {
- mEngine.doAmbientModeChanged(message.arg1 != 0, message.arg2 != 0);
+ mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj);
return;
}
case MSG_UPDATE_SURFACE:
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 2cf0262..8cb18b2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -485,7 +485,7 @@
* @deprecated Use {@link Builder} instead.
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521430)
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align, TextDirectionHeuristic textDir,
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 562ae7a..bab4bc3 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -18,8 +18,8 @@
import android.util.TimeFormatException;
+import libcore.timezone.ZoneInfoDB;
import libcore.util.ZoneInfo;
-import libcore.util.ZoneInfoDB;
import java.io.IOException;
import java.util.Locale;
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index be47320..433483f7 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -369,10 +369,7 @@
/**
* @return The color of the underline for that span, or 0 if there is no underline
- *
- * @hide
*/
- @UnsupportedAppUsage
public int getUnderlineColor() {
// The order here should match what is used in updateDrawState
final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0;
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index c822832..de182da 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -32,6 +32,7 @@
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Property;
import android.view.View;
@@ -109,7 +110,7 @@
}
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static final Property<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY =
new Property<View, PointF>(PointF.class, "bottomRight") {
@Override
@@ -144,7 +145,7 @@
}
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static final Property<View, PointF> POSITION_PROPERTY =
new Property<View, PointF>(PointF.class, "position") {
@Override
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index 7e499f2..b1fc17a 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.os.Build;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -38,9 +39,9 @@
private int mLayoutId = -1;
private ViewGroup mSceneRoot;
private View mLayout; // alternative to layoutId
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Runnable mEnterAction;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Runnable mExitAction;
/**
@@ -200,7 +201,7 @@
*
* @param sceneRoot The view on which the current scene is being set
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) {
sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5f348c4..dee6d90 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -45,7 +45,6 @@
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_dynamic_homepage", "true");
DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
- DEFAULT_FLAGS.put("settings_data_usage_v2", "true");
DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 21c4252..57d55bf 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -24,7 +24,7 @@
import libcore.timezone.CountryTimeZones;
import libcore.timezone.CountryTimeZones.TimeZoneMapping;
import libcore.timezone.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 3ee5f1f..33b3ff4f 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -18,6 +18,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -225,7 +226,7 @@
private int mMinimumFlingVelocity;
private int mMaximumFlingVelocity;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
@@ -592,8 +593,8 @@
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
- mHandler.sendEmptyMessageAtTime(LONG_PRESS,
- mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
+ mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ + ViewConfiguration.getLongPressTimeout());
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b8b7f3..5e6d3d1 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -24,6 +24,8 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.DisplayCutout;
+import android.view.InsetsState;
+import android.view.InsetsSourceControl;
import com.android.internal.os.IResultReceiver;
import android.util.MergedConfiguration;
@@ -53,6 +55,17 @@
in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
in DisplayCutout.ParcelableWrapper displayCutout);
+
+ /**
+ * Called when the window insets configuration has changed.
+ */
+ void insetsChanged(in InsetsState insetsState);
+
+ /**
+ * Called when this window retrieved control over a specified set of inset sources.
+ */
+ void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls);
+
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 308a000..c4be0e5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -96,8 +96,9 @@
*/
void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
- boolean scaleUp);
- void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter);
+ boolean scaleUp, int displayId);
+ void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter,
+ int displayId);
void executeAppTransition();
/**
@@ -294,14 +295,16 @@
void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled);
/**
- * Device has a software navigation bar (separate from the status bar).
+ * Device has a software navigation bar (separate from the status bar) on specific display.
+ *
+ * @param displayId the id of display to check if there is a software navigation bar.
*/
- boolean hasNavigationBar();
+ boolean hasNavigationBar(int displayId);
/**
* Get the position of the nav bar
*/
- int getNavBarPosition();
+ int getNavBarPosition(int displayId);
/**
* Lock the device immediately with the specified options (can be null).
@@ -548,4 +551,16 @@
* @see KeyguardManager#isDeviceLocked()
*/
void setShouldShowIme(int displayId, boolean shouldShow);
+
+ /**
+ * Reparent the top layers for a display to the requested surfaceControl. The display that
+ * is going to be re-parented (the displayId passed in) needs to have been created by the same
+ * process that is requesting the re-parent. This is to ensure clients can't just re-parent
+ * display content info to any SurfaceControl, as this would be a security issue.
+ *
+ * @param displayId The id of the display.
+ * @param surfaceControlHandle The SurfaceControl handle that the top level layers for the
+ * display should be re-parented to.
+ */
+ void reparentDisplayContent(int displayId, in IBinder surfaceControlHandle);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index bedfa9f..9762586 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -28,6 +28,7 @@
import android.view.IWindowId;
import android.view.MotionEvent;
import android.view.WindowManager;
+import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -40,10 +41,11 @@
int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outFrame,
out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,
- out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
+ out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
+ out InsetsState insetsState);
int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
- out Rect outStableInsets);
+ out Rect outStableInsets, out InsetsState insetsState);
void remove(IWindow window);
/**
@@ -86,6 +88,7 @@
* config for window, if it is now becoming visible and the merged configuration has changed
* since it was last displayed.
* @param outSurface Object in which is placed the new display surface.
+ * @param insetsState The current insets state in the system.
*
* @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
* {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
@@ -96,7 +99,8 @@
out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
out Rect outOutsets, out Rect outBackdropFrame,
out DisplayCutout.ParcelableWrapper displayCutout,
- out MergedConfiguration outMergedConfiguration, out Surface outSurface);
+ out MergedConfiguration outMergedConfiguration, out Surface outSurface,
+ out InsetsState insetsState);
/*
* Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index dc1e505..5f6bc23 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -16,6 +16,8 @@
package android.view;
+import android.os.IBinder;
+
/**
* Functions as a handle for an application that can receive input.
* Enables the native input dispatcher to refer indirectly to the window manager's
@@ -28,19 +30,18 @@
@SuppressWarnings("unused")
private long ptr;
- // The window manager's application window token.
- public final Object appWindowToken;
-
// Application name.
public String name;
// Dispatching timeout.
public long dispatchingTimeoutNanos;
+ public IBinder token;
+
private native void nativeDispose();
- public InputApplicationHandle(Object appWindowToken) {
- this.appWindowToken = appWindowToken;
+ public InputApplicationHandle(IBinder token) {
+ this.token = token;
}
@Override
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 621ee89..92e0009 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -17,6 +17,7 @@
package android.view;
import android.graphics.Region;
+import android.os.IBinder;
import android.view.IWindow;
import android.view.InputChannel;
@@ -37,8 +38,8 @@
// The client window.
public final IWindow clientWindow;
- // The input channel associated with the window.
- public InputChannel inputChannel;
+ // The token assosciated with the window.
+ public IBinder token;
// The window name.
public String name;
@@ -56,6 +57,8 @@
public int frameRight;
public int frameBottom;
+ public int surfaceInset;
+
// Global scaling factor applied to touch events when they are dispatched
// to the window
public float scaleFactor;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
new file mode 100644
index 0000000..ba5340c
--- /dev/null
+++ b/core/java/android/view/InsetsController.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowInsets.Type.InsetType;
+import android.view.InsetsState.InternalInsetType;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements {@link WindowInsetsController} on the client.
+ * @hide
+ */
+public class InsetsController implements WindowInsetsController {
+
+ private final InsetsState mState = new InsetsState();
+ private final Rect mFrame = new Rect();
+ private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
+
+ private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
+
+ void onFrameChanged(Rect frame) {
+ mFrame.set(frame);
+ }
+
+ public InsetsState getState() {
+ return mState;
+ }
+
+ public void setState(InsetsState state) {
+ mState.set(state);
+ }
+
+ /**
+ * @see InsetsState#calculateInsets
+ */
+ WindowInsets calculateInsets(boolean isScreenRound,
+ boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+ return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
+ }
+
+ /**
+ * Called when the server has dispatched us a new set of inset controls.
+ */
+ public void onControlsChanged(InsetsSourceControl[] activeControls) {
+ if (activeControls != null) {
+ for (InsetsSourceControl activeControl : activeControls) {
+ mTmpControlArray.put(activeControl.getType(), activeControl);
+ }
+ }
+
+ // Ensure to update all existing source consumers
+ for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+ final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ final InsetsSourceControl control = mTmpControlArray.get(consumer.getType());
+
+ // control may be null, but we still need to update the control to null if it got
+ // revoked.
+ consumer.setControl(control);
+ }
+
+ // Ensure to create source consumers if not available yet.
+ for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
+ final InsetsSourceControl control = mTmpControlArray.valueAt(i);
+ getSourceConsumer(control.getType()).setControl(control);
+ }
+ mTmpControlArray.clear();
+ }
+
+ @Override
+ public void show(@InsetType int types) {
+ final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ getSourceConsumer(internalTypes.valueAt(i)).show();
+ }
+ }
+
+ @Override
+ public void hide(@InsetType int types) {
+ final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ getSourceConsumer(internalTypes.valueAt(i)).hide();
+ }
+ }
+
+ @VisibleForTesting
+ public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetType int type) {
+ InsetsSourceConsumer controller = mSourceConsumers.get(type);
+ if (controller != null) {
+ return controller;
+ }
+ controller = new InsetsSourceConsumer(type, mState, Transaction::new);
+ mSourceConsumers.put(type, controller);
+ return controller;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix); pw.println("InsetsController:");
+ mState.dump(prefix + " ", pw);
+ }
+}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
new file mode 100644
index 0000000..0cb8ad7
--- /dev/null
+++ b/core/java/android/view/InsetsSource.java
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsState.InternalInsetType;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents the state of a single window generating insets for clients.
+ * @hide
+ */
+public class InsetsSource implements Parcelable {
+
+ private final @InternalInsetType int mType;
+
+ /** Frame of the source in screen coordinate space */
+ private final Rect mFrame;
+ private boolean mVisible;
+
+ private final Rect mTmpFrame = new Rect();
+
+ public InsetsSource(@InternalInsetType int type) {
+ mType = type;
+ mFrame = new Rect();
+ }
+
+ public InsetsSource(InsetsSource other) {
+ mType = other.mType;
+ mFrame = new Rect(other.mFrame);
+ mVisible = other.mVisible;
+ }
+
+ public void setFrame(Rect frame) {
+ mFrame.set(frame);
+ }
+
+ public void setVisible(boolean visible) {
+ mVisible = visible;
+ }
+
+ public @InternalInsetType int getType() {
+ return mType;
+ }
+
+ public Rect getFrame() {
+ return mFrame;
+ }
+
+ /**
+ * Calculates the insets this source will cause to a client window.
+ *
+ * @param relativeFrame The frame to calculate the insets relative to.
+ * @param ignoreVisibility If true, always reports back insets even if source isn't visible.
+ * @return The resulting insets.
+ */
+ public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+ if (!ignoreVisibility && !mVisible) {
+ return Insets.NONE;
+ }
+ if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) {
+ return Insets.NONE;
+ }
+
+ // Intersecting at top/bottom
+ if (mTmpFrame.width() == relativeFrame.width()) {
+ if (mTmpFrame.top == relativeFrame.top) {
+ return Insets.of(0, mTmpFrame.height(), 0, 0);
+ } else {
+ return Insets.of(0, 0, 0, mTmpFrame.height());
+ }
+ }
+ // Intersecting at left/right
+ else if (mTmpFrame.height() == relativeFrame.height()) {
+ if (mTmpFrame.left == relativeFrame.left) {
+ return Insets.of(mTmpFrame.width(), 0, 0, 0);
+ } else {
+ return Insets.of(0, 0, mTmpFrame.width(), 0);
+ }
+ } else {
+ return Insets.NONE;
+ }
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
+ pw.print(" frame="); pw.print(mFrame.toShortString());
+ pw.print(" visible="); pw.print(mVisible);
+ pw.println();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ InsetsSource that = (InsetsSource) o;
+
+ if (mType != that.mType) return false;
+ if (mVisible != that.mVisible) return false;
+ return mFrame.equals(that.mFrame);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mType;
+ result = 31 * result + mFrame.hashCode();
+ result = 31 * result + (mVisible ? 1 : 0);
+ return result;
+ }
+
+ public InsetsSource(Parcel in) {
+ mType = in.readInt();
+ mFrame = in.readParcelable(null /* loader */);
+ mVisible = in.readBoolean();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeParcelable(mFrame, 0 /* flags*/);
+ dest.writeBoolean(mVisible);
+ }
+
+ public static final Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
+
+ public InsetsSource createFromParcel(Parcel in) {
+ return new InsetsSource(in);
+ }
+
+ public InsetsSource[] newArray(int size) {
+ return new InsetsSource[size];
+ }
+ };
+}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
new file mode 100644
index 0000000..e74aa8d
--- /dev/null
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -0,0 +1,95 @@
+/*
+ * 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
+ */
+
+package android.view;
+
+import android.annotation.Nullable;
+import android.view.SurfaceControl.Transaction;
+import android.view.InsetsState.InternalInsetType;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.function.Supplier;
+
+/**
+ * Controls the visibility and animations of a single window insets source.
+ * @hide
+ */
+public class InsetsSourceConsumer {
+
+ private final Supplier<Transaction> mTransactionSupplier;
+ private final @InternalInsetType int mType;
+ private final InsetsState mState;
+ private @Nullable InsetsSourceControl mControl;
+ private boolean mHidden;
+
+ public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
+ Supplier<Transaction> transactionSupplier) {
+ mType = type;
+ mState = state;
+ mTransactionSupplier = transactionSupplier;
+ }
+
+ public void setControl(@Nullable InsetsSourceControl control) {
+ if (mControl == control) {
+ return;
+ }
+ mControl = control;
+ applyHiddenToControl();
+ }
+
+ @VisibleForTesting
+ public InsetsSourceControl getControl() {
+ return mControl;
+ }
+
+ int getType() {
+ return mType;
+ }
+
+ @VisibleForTesting
+ public void show() {
+ setHidden(false);
+ }
+
+ @VisibleForTesting
+ public void hide() {
+ setHidden(true);
+ }
+
+ private void setHidden(boolean hidden) {
+ if (mHidden == hidden) {
+ return;
+ }
+ mHidden = hidden;
+ applyHiddenToControl();
+ }
+
+ private void applyHiddenToControl() {
+ if (mControl == null) {
+ return;
+ }
+
+ // TODO: Animation
+ final Transaction t = mTransactionSupplier.get();
+ if (mHidden) {
+ t.hide(mControl.getLeash());
+ } else {
+ t.show(mControl.getLeash());
+ }
+ t.apply();
+ }
+}
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/core/java/android/view/InsetsSourceControl.aidl
similarity index 69%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to core/java/android/view/InsetsSourceControl.aidl
index 5ca9d15..755bf45 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/core/java/android/view/InsetsSourceControl.aidl
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
/**
* Copyright (c) 2018, The Android Open Source Project
*
@@ -15,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+
+package android.view;
+
+parcelable InsetsSourceControl;
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
new file mode 100644
index 0000000..9383e6c
--- /dev/null
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsState.InternalInsetType;
+
+/**
+ * Represents a parcelable object to allow controlling a single {@link InsetsSource}.
+ * @hide
+ */
+public class InsetsSourceControl implements Parcelable {
+
+ private final @InternalInsetType int mType;
+ private final SurfaceControl mLeash;
+
+ public InsetsSourceControl(@InternalInsetType int type, SurfaceControl leash) {
+ mType = type;
+ mLeash = leash;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public SurfaceControl getLeash() {
+ return mLeash;
+ }
+
+ public InsetsSourceControl(Parcel in) {
+ mType = in.readInt();
+ mLeash = in.readParcelable(null /* loader */);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeParcelable(mLeash, 0 /* flags*/);
+ }
+
+ public static final Creator<InsetsSourceControl> CREATOR
+ = new Creator<InsetsSourceControl>() {
+ public InsetsSourceControl createFromParcel(Parcel in) {
+ return new InsetsSourceControl(in);
+ }
+
+ public InsetsSourceControl[] newArray(int size) {
+ return new InsetsSourceControl[size];
+ }
+ };
+}
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/core/java/android/view/InsetsState.aidl
similarity index 63%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to core/java/android/view/InsetsState.aidl
index 5ca9d15..d02ddd1 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/core/java/android/view/InsetsState.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
/**
- * Copyright (c) 2018, The Android Open Source Project
+ * Copyright (c) 2017, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+
+package android.view;
+
+parcelable InsetsState;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
new file mode 100644
index 0000000..689b14f
--- /dev/null
+++ b/core/java/android/view/InsetsState.java
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsets.Type.InsetType;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Holder for state of system windows that cause window insets for all other windows in the system.
+ * @hide
+ */
+public class InsetsState implements Parcelable {
+
+ /**
+ * Internal representation of inset source types. This is different from the public API in
+ * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
+ * at the same time.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "TYPE", value = {
+ TYPE_TOP_BAR,
+ TYPE_SIDE_BAR_1,
+ TYPE_SIDE_BAR_2,
+ TYPE_SIDE_BAR_3,
+ TYPE_IME
+ })
+ public @interface InternalInsetType {}
+
+ static final int FIRST_TYPE = 0;
+
+ /** Top bar. Can be status bar or caption in freeform windowing mode. */
+ public static final int TYPE_TOP_BAR = FIRST_TYPE;
+
+ /**
+ * Up to 3 side bars that appear on left/right/bottom. On phones there is only one side bar
+ * (the navigation bar, see {@link #TYPE_NAVIGATION_BAR}), but other form factors might have
+ * multiple, like Android Auto.
+ */
+ public static final int TYPE_SIDE_BAR_1 = 1;
+ public static final int TYPE_SIDE_BAR_2 = 2;
+ public static final int TYPE_SIDE_BAR_3 = 3;
+
+ /** Input method window. */
+ public static final int TYPE_IME = 4;
+ static final int LAST_TYPE = TYPE_IME;
+
+ // Derived types
+
+ /** First side bar is navigation bar. */
+ public static final int TYPE_NAVIGATION_BAR = TYPE_SIDE_BAR_1;
+
+ /** A shelf is the same as the navigation bar. */
+ public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
+
+ private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
+
+ public InsetsState() {
+ }
+
+ /**
+ * Calculates {@link WindowInsets} based on the current source configuration.
+ *
+ * @param frame The frame to calculate the insets relative to.
+ * @return The calculated insets.
+ */
+ public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
+ boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+ Insets systemInsets = Insets.NONE;
+ Insets maxInsets = Insets.NONE;
+ final Rect relativeFrame = new Rect(frame);
+ final Rect relativeFrameMax = new Rect(frame);
+ for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ InsetsSource source = mSources.get(type);
+ if (source == null) {
+ continue;
+ }
+ systemInsets = processSource(source, systemInsets, relativeFrame,
+ false /* ignoreVisibility */);
+
+ // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
+ // target.
+ if (source.getType() != TYPE_IME) {
+ maxInsets = processSource(source, maxInsets, relativeFrameMax,
+ true /* ignoreVisibility */);
+ }
+ }
+ return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
+ alwaysConsumeNavBar, cutout);
+ }
+
+ private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
+ boolean ignoreVisibility) {
+ Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
+ insets = Insets.add(currentInsets, insets);
+ relativeFrame.inset(insets);
+ return insets;
+ }
+
+ public InsetsSource getSource(@InternalInsetType int type) {
+ return mSources.computeIfAbsent(type, InsetsSource::new);
+ }
+
+ /**
+ * Modifies the state of this class to exclude a certain type to make it ready for dispatching
+ * to the client.
+ *
+ * @param type The {@link InternalInsetType} of the source to remove
+ */
+ public void removeSource(int type) {
+ mSources.remove(type);
+ }
+
+ public void set(InsetsState other) {
+ set(other, false /* copySources */);
+ }
+
+ public void set(InsetsState other, boolean copySources) {
+ mSources.clear();
+ if (copySources) {
+ for (int i = 0; i < other.mSources.size(); i++) {
+ InsetsSource source = other.mSources.valueAt(i);
+ mSources.put(source.getType(), new InsetsSource(source));
+ }
+ } else {
+ mSources.putAll(other.mSources);
+ }
+ }
+
+ public static @InternalInsetType ArraySet<Integer> toInternalType(@InsetType int insetTypes) {
+ final ArraySet<Integer> result = new ArraySet<>();
+ if ((insetTypes & Type.TOP_BAR) != 0) {
+ result.add(TYPE_TOP_BAR);
+ }
+ if ((insetTypes & Type.SIDE_BARS) != 0) {
+ result.add(TYPE_SIDE_BAR_1);
+ result.add(TYPE_SIDE_BAR_2);
+ result.add(TYPE_SIDE_BAR_3);
+ }
+ if ((insetTypes & Type.IME) != 0) {
+ result.add(TYPE_IME);
+ }
+ return result;
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "InsetsState");
+ for (int i = mSources.size() - 1; i >= 0; i--) {
+ mSources.valueAt(i).dump(prefix + " ", pw);
+ }
+ }
+
+ static String typeToString(int type) {
+ switch (type) {
+ case TYPE_TOP_BAR:
+ return "TYPE_TOP_BAR";
+ case TYPE_SIDE_BAR_1:
+ return "TYPE_SIDE_BAR_1";
+ case TYPE_SIDE_BAR_2:
+ return "TYPE_SIDE_BAR_2";
+ case TYPE_SIDE_BAR_3:
+ return "TYPE_SIDE_BAR_3";
+ case TYPE_IME:
+ return "TYPE_IME";
+ default:
+ return "TYPE_UNKNOWN";
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) { return true; }
+ if (o == null || getClass() != o.getClass()) { return false; }
+
+ InsetsState state = (InsetsState) o;
+
+ if (mSources.size() != state.mSources.size()) {
+ return false;
+ }
+ for (int i = mSources.size() - 1; i >= 0; i--) {
+ InsetsSource source = mSources.valueAt(i);
+ InsetsSource otherSource = state.mSources.get(source.getType());
+ if (otherSource == null) {
+ return false;
+ }
+ if (!otherSource.equals(source)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mSources.hashCode();
+ }
+
+ public InsetsState(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSources.size());
+ for (int i = 0; i < mSources.size(); i++) {
+ dest.writeParcelable(mSources.valueAt(i), 0 /* flags */);
+ }
+ }
+
+ public static final Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
+
+ public InsetsState createFromParcel(Parcel in) {
+ return new InsetsState(in);
+ }
+
+ public InsetsState[] newArray(int size) {
+ return new InsetsState[size];
+ }
+ };
+
+ public void readFromParcel(Parcel in) {
+ mSources.clear();
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final InsetsSource source = in.readParcelable(null /* loader */);
+ mSources.put(source.getType(), source);
+ }
+ }
+}
+
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 0739516..9cced4e 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1803,6 +1803,28 @@
}
/**
+ * Returns whether this key will be sent to the
+ * {@link android.media.session.MediaSession.Callback} if not handled.
+ */
+ public static final boolean isMediaSessionKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Returns true if the specified keycode is a gamepad button.
* @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}.
*/
@@ -1861,31 +1883,6 @@
}
}
- /**
- * Whether this key is a media key, which can be send to apps that are
- * interested in media key events.
- *
- * @hide
- */
- public static final boolean isMediaKey(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- case KeyEvent.KEYCODE_MUTE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- case KeyEvent.KEYCODE_MEDIA_STOP:
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- case KeyEvent.KEYCODE_MEDIA_RECORD:
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
- return true;
- }
- return false;
- }
-
-
/** Is this a system key? System keys can not be used for menu shortcuts.
* @hide
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a7a5024..ab01085 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -104,6 +104,8 @@
int flags, int mask);
private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
int l, int t, int r, int b);
+ private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
+ float cornerRadius);
private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
int layerStack);
@@ -375,9 +377,13 @@
* Construct a new {@link SurfaceControl} with the set parameters.
*/
public SurfaceControl build() {
- if (mWidth <= 0 || mHeight <= 0) {
+ if (mWidth < 0 || mHeight < 0) {
throw new IllegalArgumentException(
- "width and height must be set");
+ "width and height must be positive or unset");
+ }
+ if ((mWidth > 0 || mHeight > 0) && (isColorLayerSet() || isContainerLayerSet())) {
+ throw new IllegalArgumentException(
+ "Only buffer layers can set a valid buffer size.");
}
return new SurfaceControl(mSession, mName, mWidth, mHeight, mFormat,
mFlags, mParent, mWindowType, mOwnerUid);
@@ -399,8 +405,8 @@
* @param width The buffer width in pixels.
* @param height The buffer height in pixels.
*/
- public Builder setSize(int width, int height) {
- if (width <= 0 || height <= 0) {
+ public Builder setBufferSize(int width, int height) {
+ if (width < 0 || height < 0) {
throw new IllegalArgumentException(
"width and height must be positive");
}
@@ -533,6 +539,10 @@
return this;
}
+ private boolean isColorLayerSet() {
+ return (mFlags & FX_SURFACE_DIM) == FX_SURFACE_DIM;
+ }
+
/**
* Indicates whether a 'ContainerLayer' is to be constructed.
*
@@ -550,6 +560,10 @@
return this;
}
+ private boolean isContainerLayerSet() {
+ return (mFlags & FX_SURFACE_CONTAINER) == FX_SURFACE_CONTAINER;
+ }
+
/**
* Set 'Surface creation flags' such as {@link HIDDEN}, {@link SECURE}.
*
@@ -869,10 +883,10 @@
}
}
- public void setSize(int w, int h) {
+ public void setBufferSize(int w, int h) {
checkNotReleased();
synchronized(SurfaceControl.class) {
- sGlobalTransaction.setSize(this, w, h);
+ sGlobalTransaction.setBufferSize(this, w, h);
}
}
@@ -994,6 +1008,18 @@
}
}
+ /**
+ * Sets the corner radius of a {@link SurfaceControl}.
+ *
+ * @param cornerRadius Corner radius in pixels.
+ */
+ public void setCornerRadius(float cornerRadius) {
+ checkNotReleased();
+ synchronized (SurfaceControl.class) {
+ sGlobalTransaction.setCornerRadius(this, cornerRadius);
+ }
+ }
+
public void setLayerStack(int layerStack) {
checkNotReleased();
synchronized(SurfaceControl.class) {
@@ -1427,7 +1453,7 @@
}
@UnsupportedAppUsage
- public Transaction setSize(SurfaceControl sc, int w, int h) {
+ public Transaction setBufferSize(SurfaceControl sc, int w, int h) {
sc.checkNotReleased();
mResizedSurfaces.put(sc, new Point(w, h));
nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
@@ -1517,6 +1543,20 @@
return this;
}
+ /**
+ * Sets the corner radius of a {@link SurfaceControl}.
+ * @param sc SurfaceControl
+ * @param cornerRadius Corner radius in pixels.
+ * @return Itself.
+ */
+ @UnsupportedAppUsage
+ public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) {
+ sc.checkNotReleased();
+ nativeSetCornerRadius(mNativeObject, sc.mNativeObject, cornerRadius);
+
+ return this;
+ }
+
@UnsupportedAppUsage
public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
sc.checkNotReleased();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2b68ec0..797d1c5 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -557,7 +557,7 @@
name,
(mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
new SurfaceControl.Builder(mSurfaceSession)
- .setSize(mSurfaceWidth, mSurfaceHeight)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setFormat(mFormat)
.setFlags(mSurfaceFlags));
} else if (mSurfaceControl == null) {
@@ -595,10 +595,14 @@
mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
0.0f, 0.0f,
mScreenRect.height() / (float) mSurfaceHeight);
+ // Set a window crop when creating the surface or changing its size to
+ // crop the buffer to the surface size since the buffer producer may
+ // use SCALING_MODE_SCALE and submit a larger size than the surface
+ // size.
+ mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
}
if (sizeChanged && !creating) {
- mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
- mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
+ mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
}
} finally {
SurfaceControl.closeTransaction();
@@ -1122,6 +1126,13 @@
}
};
+ /**
+ * @hide
+ */
+ public SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
class SurfaceControlWithBackground extends SurfaceControl {
SurfaceControl mBackgroundControl;
private boolean mOpaque = true;
@@ -1133,6 +1144,8 @@
mBackgroundControl = b.setName("Background for -" + name)
.setFormat(OPAQUE)
+ // Unset the buffer size of the background color layer.
+ .setBufferSize(0, 0)
.setColorLayer(true)
.build();
mOpaque = opaque;
@@ -1158,9 +1171,9 @@
}
@Override
- public void setSize(int w, int h) {
- super.setSize(w, h);
- mBackgroundControl.setSize(w, h);
+ public void setBufferSize(int w, int h) {
+ super.setBufferSize(w, h);
+ // The background surface is a color layer so we do not set a size.
}
@Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0eaef5a..4b9cbff 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -112,7 +112,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.intelligence.IntelligenceManager;
+import android.view.intelligence.ContentCaptureManager;
import android.widget.Checkable;
import android.widget.FrameLayout;
import android.widget.ScrollBarDrawable;
@@ -4159,7 +4159,7 @@
float mAlpha = 1f;
/**
- * The opacity of the view as manipulated by the Fade transition. This is a hidden
+ * The opacity of the view as manipulated by the Fade transition. This is a
* property only used by transitions, which is composited with the other alpha
* values to calculate the final visual alpha value.
*/
@@ -8138,7 +8138,7 @@
* is visible.
*
* <p>The populated structure is then passed to the service through
- * {@link IntelligenceManager#notifyViewAppeared(ViewStructure)}.
+ * {@link ContentCaptureManager#notifyViewAppeared(ViewStructure)}.
*
* <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
* <ul>
@@ -8915,7 +8915,7 @@
}
/**
- * Helper used to notify the {@link IntelligenceManager} when the view is removed or
+ * Helper used to notify the {@link ContentCaptureManager} when the view is removed or
* added, based on whether it's laid out and visible, and without knowing if the parent removed
* it from the view hierarchy.
*
@@ -8931,11 +8931,15 @@
* </ol>
*/
private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
+ // First check if context has client, so it saves a service lookup when it doesn't
+ if (!mContext.isContentCaptureSupported()) return;
- final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
- if (im == null || !im.isContentCaptureEnabled()) return;
+ // Then check if it's enabled in the context...
+ final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
+ if (cm == null || !cm.isContentCaptureEnabled()) return;
- // NOTE: isImportantForContentCapture() is more expensive than im.isContentCaptureEnabled()
+ // ... and finally at the view level
+ // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
if (!isImportantForContentCapture()) return;
if (appeared) {
@@ -8950,9 +8954,9 @@
return;
}
// All good: notify the manager...
- final ViewStructure structure = im.newViewStructure(this);
+ final ViewStructure structure = cm.newViewStructure(this);
onProvideContentCaptureStructure(structure, /* flags= */ 0);
- im.notifyViewAppeared(structure);
+ cm.notifyViewAppeared(structure);
// ...and set the flags
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
@@ -8969,7 +8973,7 @@
return;
}
// All good: notify the manager...
- im.notifyViewDisappeared(getAutofillId());
+ cm.notifyViewDisappeared(getAutofillId());
// ...and set the flags
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
@@ -10337,6 +10341,20 @@
}
/**
+ * Retrieves the single {@link WindowInsetsController} of the window this view is attached to.
+ *
+ * @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
+ * a window.
+ * @hide pending unhide
+ */
+ public @Nullable WindowInsetsController getWindowInsetsController() {
+ if (mAttachInfo != null) {
+ return mAttachInfo.mViewRootImpl.getInsetsController();
+ }
+ return null;
+ }
+
+ /**
* @hide Compute the insets that should be consumed by this view and the ones
* that should propagate to those under it.
*
@@ -15729,15 +15747,12 @@
}
/**
- * This property is hidden and intended only for use by the Fade transition, which
- * animates it to produce a visual translucency that does not side-effect (or get
- * affected by) the real alpha property. This value is composited with the other
- * alpha value (and the AlphaAnimation value, when that is present) to produce
- * a final visual translucency result, which is what is passed into the DisplayList.
- *
- * @hide
+ * This property is intended only for use by the Fade transition, which animates it
+ * to produce a visual translucency that does not side-effect (or get affected by)
+ * the real alpha property. This value is composited with the other alpha value
+ * (and the AlphaAnimation value, when that is present) to produce a final visual
+ * translucency result, which is what is passed into the DisplayList.
*/
- @UnsupportedAppUsage
public void setTransitionAlpha(float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mTransitionAlpha != alpha) {
@@ -15760,16 +15775,13 @@
}
/**
- * This property is hidden and intended only for use by the Fade transition, which
- * animates it to produce a visual translucency that does not side-effect (or get
- * affected by) the real alpha property. This value is composited with the other
- * alpha value (and the AlphaAnimation value, when that is present) to produce
- * a final visual translucency result, which is what is passed into the DisplayList.
- *
- * @hide
+ * This property is intended only for use by the Fade transition, which animates
+ * it to produce a visual translucency that does not side-effect (or get affected
+ * by) the real alpha property. This value is composited with the other alpha
+ * value (and the AlphaAnimation value, when that is present) to produce a final
+ * visual translucency result, which is what is passed into the DisplayList.
*/
@ViewDebug.ExportedProperty(category = "drawing")
- @UnsupportedAppUsage
public float getTransitionAlpha() {
return mTransformationInfo != null ? mTransformationInfo.mTransitionAlpha : 1;
}
@@ -16288,9 +16300,17 @@
}
}
- /** @hide */
- @UnsupportedAppUsage
- public void setAnimationMatrix(Matrix matrix) {
+ /**
+ * Changes the transformation matrix on the view. This is used in animation frameworks,
+ * such as {@link android.transition.Transition}. When the animation finishes, the matrix
+ * should be cleared by calling this method with <code>null</code> as the matrix parameter.
+ * Application developers should use transformation methods like {@link #setRotation(float)},
+ * {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}}
+ * and {@link #setTranslationY(float)} (float)}} instead.
+ *
+ * @param matrix The matrix, null indicates that the matrix should be cleared.
+ */
+ public void setAnimationMatrix(@Nullable Matrix matrix) {
invalidateViewProperty(true, false);
mRenderNode.setAnimationMatrix(matrix);
invalidateViewProperty(false, true);
@@ -21460,7 +21480,7 @@
* previous ones
* {@hide}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
@@ -21525,11 +21545,19 @@
}
/**
- * Same as setFrame, but public and hidden. For use in {@link android.transition.ChangeBounds}.
- * @hide
+ * Assign a size and position to this view.
+ *
+ * This method is meant to be used in animations only as it applies this position and size
+ * for the view only temporary and it can be changed back at any time by the layout.
+ *
+ * @param left Left position, relative to parent
+ * @param top Top position, relative to parent
+ * @param right Right position, relative to parent
+ * @param bottom Bottom position, relative to parent
+ *
+ * @see #setLeft(int), #setRight(int), #setTop(int), #setBottom(int)
*/
- @UnsupportedAppUsage
- public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
+ public final void setLeftTopRightBottom(int left, int top, int right, int bottom) {
setFrame(left, top, right, bottom);
}
@@ -23537,6 +23565,16 @@
}
/**
+ * Get the identifier used for this view by the drawing system.
+ *
+ * @see RenderNode#getUniqueId()
+ * @return A long that uniquely identifies this view's drawing component
+ */
+ public long getUniqueDrawingId() {
+ return mRenderNode.getUniqueId();
+ }
+
+ /**
* Returns this view's tag.
*
* @return the Object stored in this view as a tag, or {@code null} if not
@@ -24741,7 +24779,7 @@
final SurfaceSession session = new SurfaceSession(root.mSurface);
final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
.setName("drag surface")
- .setSize(shadowSize.x, shadowSize.y)
+ .setBufferSize(shadowSize.x, shadowSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
final Surface surface = new Surface();
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 767cd33..d03d97e 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -393,7 +393,7 @@
case HAS_PERMANENT_MENU_KEY_AUTODETECT: {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
- sHasPermanentMenuKey = !wm.hasNavigationBar();
+ sHasPermanentMenuKey = !wm.hasNavigationBar(context.getDisplayId());
sHasPermanentMenuKeySet = true;
} catch (RemoteException ex) {
sHasPermanentMenuKey = false;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1e91aa8..741510e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7042,10 +7042,7 @@
* suppression is disabled with a later call to suppressLayout(false).
* When layout suppression is disabled, a requestLayout() call is sent
* if layout() was attempted while layout was being suppressed.
- *
- * @hide
*/
- @UnsupportedAppUsage
public void suppressLayout(boolean suppress) {
mSuppressLayout = suppress;
if (!suppress) {
@@ -7061,8 +7058,6 @@
* suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
*
* @return true if layout calls are currently suppressed, false otherwise.
- *
- * @hide
*/
public boolean isLayoutSuppressed() {
return mSuppressLayout;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 484c6f3..cb47886 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -161,6 +161,24 @@
private static final boolean MT_RENDERER_AVAILABLE = true;
/**
+ * If set to true, the view system will switch from using rectangles retrieved from window to
+ * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
+ * directly from the full configuration, enabling richer information about the insets state, as
+ * well as new APIs to control it frame-by-frame, and synchronize animations with it.
+ * <p>
+ * Only switch this to true once the new insets system is productionized and the old APIs are
+ * fully migrated over.
+ */
+ private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets";
+
+ /**
+ * @see #USE_NEW_INSETS_PROPERTY
+ * @hide
+ */
+ public static final boolean USE_NEW_INSETS =
+ SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false);
+
+ /**
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
*/
@@ -432,6 +450,8 @@
boolean mAdded;
boolean mAddedTouchMode;
+ final Rect mTmpFrame = new Rect();
+
// These are accessed by multiple threads.
final Rect mWinFrame; // frame given by window manager.
@@ -444,6 +464,7 @@
final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
boolean mPendingAlwaysConsumeNavBar;
+ private InsetsState mPendingInsets = new InsetsState();
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -531,6 +552,8 @@
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
+ private final InsetsController mInsetsController = new InsetsController();
+
static final class SystemUiVisibilityInfo {
int seq;
int globalVisibility;
@@ -797,9 +820,11 @@
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
- getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
+ getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
- mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
+ mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
+ mInsetsController.getState());
+ setFrame(mTmpFrame);
} catch (RemoteException e) {
mAdded = false;
mView = null;
@@ -826,6 +851,7 @@
mAttachInfo.mAlwaysConsumeNavBar =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
+ mPendingInsets = mInsetsController.getState();
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -1473,31 +1499,22 @@
mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName("Bounds for - " + getTitle().toString())
- .setSize(mWidth, mHeight)
.build();
- setBoundsSurfaceSizeAndCrop();
+ setBoundsSurfaceCrop();
mTransaction.setLayer(mBoundsSurfaceControl, zOrderLayer)
.show(mBoundsSurfaceControl)
.apply();
mBoundsSurface.copyFrom(mBoundsSurfaceControl);
}
- private void setBoundsSurfaceSizeAndCrop() {
+ private void setBoundsSurfaceCrop() {
// mWinFrame is already adjusted for surface insets. So offset it and use it as
// the cropping bounds.
mTempBoundsRect.set(mWinFrame);
mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
mWindowAttributes.surfaceInsets.top);
mTransaction.setWindowCrop(mBoundsSurfaceControl, mTempBoundsRect);
-
- // Expand the bounds by the surface insets to get the size of surface.
- mTempBoundsRect.inset(-mWindowAttributes.surfaceInsets.left,
- -mWindowAttributes.surfaceInsets.top,
- -mWindowAttributes.surfaceInsets.right,
- -mWindowAttributes.surfaceInsets.bottom);
- mTransaction.setSize(mBoundsSurfaceControl, mTempBoundsRect.width(),
- mTempBoundsRect.height());
}
/**
@@ -1506,7 +1523,7 @@
*/
private void updateBoundsSurface() {
if (mBoundsSurfaceControl != null && mSurface.isValid()) {
- setBoundsSurfaceSizeAndCrop();
+ setBoundsSurfaceCrop();
mTransaction.deferTransactionUntilSurface(mBoundsSurfaceControl,
mSurface, mSurface.getNextFrameNumber())
.apply();
@@ -1780,7 +1797,8 @@
Rect stableInsets = mDispatchStableInsets;
DisplayCutout displayCutout = mDispatchDisplayCutout;
// For dispatch we preserve old logic, but for direct requests from Views we allow to
- // immediately use pending insets.
+ // immediately use pending insets. This is such that getRootWindowInsets returns the
+ // result from the layout hint before we ran a traversal shortly after adding a window.
if (!forceConstruct
&& (!mPendingContentInsets.equals(contentInsets) ||
!mPendingStableInsets.equals(stableInsets) ||
@@ -1797,10 +1815,16 @@
}
contentInsets = ensureInsetsNonNegative(contentInsets, "content");
stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
- mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets,
- mContext.getResources().getConfiguration().isScreenRound(),
- mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+ if (USE_NEW_INSETS) {
+ mLastWindowInsets = mInsetsController.calculateInsets(
+ mContext.getResources().getConfiguration().isScreenRound(),
+ mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+ } else {
+ mLastWindowInsets = new WindowInsets(contentInsets,
+ null /* windowDecorInsets */, stableInsets,
+ mContext.getResources().getConfiguration().isScreenRound(),
+ mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+ }
}
return mLastWindowInsets;
}
@@ -1828,6 +1852,10 @@
host.dispatchApplyWindowInsets(insets);
}
+ InsetsController getInsetsController() {
+ return mInsetsController;
+ }
+
private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
return lp.type == TYPE_STATUS_BAR_PANEL
|| lp.type == TYPE_INPUT_METHOD
@@ -1916,7 +1944,6 @@
// PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
// However, windows are now always 32 bits by default, so choose 32 bits
mAttachInfo.mUse32BitDrawingCache = true;
- mAttachInfo.mHasWindowFocus = false;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
@@ -2000,6 +2027,9 @@
if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
insetsChanged = true;
}
+ if (!mPendingInsets.equals(mInsetsController.getState())) {
+ insetsChanged = true;
+ }
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;
@@ -2193,6 +2223,8 @@
mAttachInfo.mStableInsets);
final boolean cutoutChanged = !mPendingDisplayCutout.equals(
mAttachInfo.mDisplayCutout);
+ final boolean insetsStateChanged = !mPendingInsets.equals(
+ mInsetsController.getState());
final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
final boolean surfaceSizeChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -2230,6 +2262,10 @@
mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
contentInsetsChanged = true;
}
+ if (insetsStateChanged) {
+ mInsetsController.setState(mPendingInsets);
+ contentInsetsChanged = true;
+ }
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -2675,7 +2711,6 @@
}
private void maybeHandleWindowMove(Rect frame) {
-
// TODO: Well, we are checking whether the frame has changed similarly
// to how this is done for the insets. This is however incorrect since
// the insets and the frame are translated. For example, the old frame
@@ -4180,6 +4215,8 @@
private final static int MSG_UPDATE_POINTER_ICON = 27;
private final static int MSG_POINTER_CAPTURE_CHANGED = 28;
private final static int MSG_DRAW_FINISHED = 29;
+ private final static int MSG_INSETS_CHANGED = 30;
+ private final static int MSG_INSETS_CONTROL_CHANGED = 31;
final class ViewRootHandler extends Handler {
@Override
@@ -4235,6 +4272,8 @@
return "MSG_POINTER_CAPTURE_CHANGED";
case MSG_DRAW_FINISHED:
return "MSG_DRAW_FINISHED";
+ case MSG_INSETS_CHANGED:
+ return "MSG_INSETS_CHANGED";
}
return super.getMessageName(message);
}
@@ -4315,7 +4354,7 @@
|| !mPendingVisibleInsets.equals(args.arg3)
|| !mPendingOutsets.equals(args.arg7);
- mWinFrame.set((Rect) args.arg1);
+ setFrame((Rect) args.arg1);
mPendingOverscanInsets.set((Rect) args.arg5);
mPendingContentInsets.set((Rect) args.arg2);
mPendingStableInsets.set((Rect) args.arg6);
@@ -4338,16 +4377,36 @@
requestLayout();
}
break;
+ case MSG_INSETS_CHANGED:
+ mPendingInsets = (InsetsState) msg.obj;
+
+ // TODO: Full traversal not needed here.
+ if (USE_NEW_INSETS) {
+ requestLayout();
+ }
+ break;
+ case MSG_INSETS_CONTROL_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mPendingInsets = (InsetsState) args.arg1;
+ mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
+
+ // TODO: Full traversal not necessarily needed here.
+ if (USE_NEW_INSETS) {
+ requestLayout();
+ }
+ break;
+ }
case MSG_WINDOW_MOVED:
if (mAdded) {
final int w = mWinFrame.width();
final int h = mWinFrame.height();
final int l = msg.arg1;
final int t = msg.arg2;
- mWinFrame.left = l;
- mWinFrame.right = l + w;
- mWinFrame.top = t;
- mWinFrame.bottom = t + h;
+ mTmpFrame.left = l;
+ mTmpFrame.right = l + w;
+ mTmpFrame.top = t;
+ mTmpFrame.bottom = t + h;
+ setFrame(mTmpFrame);
mPendingBackDropFrame.set(mWinFrame);
maybeHandleWindowMove(mWinFrame);
@@ -6733,9 +6792,9 @@
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
- mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
+ mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
- mPendingMergedConfiguration, mSurface);
+ mPendingMergedConfiguration, mSurface, mPendingInsets);
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6745,15 +6804,22 @@
}
if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
+ mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
+ setFrame(mTmpFrame);
+
return relayoutResult;
}
+ private void setFrame(Rect frame) {
+ mWinFrame.set(frame);
+ mInsetsController.onFrameChanged(frame);
+ }
+
/**
* {@inheritDoc}
*/
@@ -6856,6 +6922,8 @@
mChoreographer.dump(prefix, writer);
+ mInsetsController.dump(prefix, writer);
+
writer.print(prefix); writer.println("View Hierarchy:");
dumpViewHierarchy(innerPrefix, writer, mView);
}
@@ -7064,6 +7132,18 @@
mHandler.sendMessage(msg);
}
+ private void dispatchInsetsChanged(InsetsState insetsState) {
+ mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
+ }
+
+ private void dispatchInsetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = insetsState;
+ args.arg2 = activeControls;
+ mHandler.obtainMessage(MSG_INSETS_CONTROL_CHANGED, args).sendToTarget();
+ }
+
public void dispatchMoved(int newX, int newY) {
if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
if (mTranslator != null) {
@@ -8127,6 +8207,23 @@
}
@Override
+ public void insetsChanged(InsetsState insetsState) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchInsetsChanged(insetsState);
+ }
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls);
+ }
+ }
+
+ @Override
public void moved(int newX, int newY) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index c1e94d8..58ab817 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2410,4 +2410,11 @@
public boolean isCloseOnSwipeEnabled() {
return mCloseOnSwipeEnabled;
}
+
+ /**
+ * @return The {@link WindowInsetsController} associated with this window
+ * @see View#getWindowInsetsController()
+ * @hide pending unhide
+ */
+ public abstract @NonNull WindowInsetsController getInsetsController();
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index a8debbd..572d331 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -18,13 +18,17 @@
package android.view;
import android.annotation.NonNull;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.view.inputmethod.InputMethod;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -807,4 +811,69 @@
mIsRound, mAlwaysConsumeNavBar, mDisplayCutout);
}
}
+
+ /**
+ * Class that defines different types of sources causing window insets.
+ * @hide pending unhide
+ */
+ public static final class Type {
+
+ static final int TOP_BAR = 0x1;
+ static final int IME = 0x2;
+ static final int SIDE_BARS = 0x4;
+ static final int WINDOW_DECOR = 0x8;
+
+ private Type() {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = { TOP_BAR, IME, SIDE_BARS, WINDOW_DECOR })
+ public @interface InsetType {
+ }
+
+ /**
+ * @return An inset type representing the top bar of a window, which can be the status
+ * bar on handheld-like devices as well as a caption bar.
+ */
+ public static @InsetType int topBar() {
+ return TOP_BAR;
+ }
+
+ /**
+ * @return An inset type representing the window of an {@link InputMethod}.
+ */
+ public static @InsetType int ime() {
+ return IME;
+ }
+
+ /**
+ * @return An inset type representing any system bars that are not {@link #topBar()}.
+ */
+ public static @InsetType int sideBars() {
+ return SIDE_BARS;
+ }
+
+ /**
+ * @return An inset type representing decor that is being app-controlled.
+ */
+ public static @InsetType int windowDecor() {
+ return WINDOW_DECOR;
+ }
+
+ /**
+ * @return All system bars. Includes {@link #topBar()} as well as {@link #sideBars()}, but
+ * not {@link #ime()}.
+ */
+ public static @InsetType int systemBars() {
+ return TOP_BAR | SIDE_BARS;
+ }
+
+ /**
+ * @return All inset types combined.
+ */
+ public static @InsetType int all() {
+ return 0xFFFFFFFF;
+ }
+ }
}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
new file mode 100644
index 0000000..7be5f2e
--- /dev/null
+++ b/core/java/android/view/WindowInsetsController.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import android.view.WindowInsets.Type.InsetType;
+
+/**
+ * Interface to control windows that generate insets.
+ *
+ * TODO Needs more information and examples once the API is more baked.
+ * @hide pending unhide
+ */
+public interface WindowInsetsController {
+
+ /**
+ * Makes a set of windows that cause insets appear on screen.
+ * <p>
+ * Note that if the window currently doesn't have control over a certain type, it will apply the
+ * change as soon as the window gains control. The app can listen to the event by observing
+ * {@link View#onApplyWindowInsets} and checking visibility with "TODO at method" in
+ * {@link WindowInsets}.
+ *
+ * @param types A bitmask of {@link WindowInsets.Type.InsetType} specifying what windows the app
+ * would like to make appear on screen.
+ */
+ void show(@InsetType int types);
+
+ /**
+ * Makes a set of windows causing insets disappear.
+ * <p>
+ * Note that if the window currently doesn't have control over a certain type, it will apply the
+ * change as soon as the window gains control. The app can listen to the event by observing
+ * {@link View#onApplyWindowInsets} and checking visibility with "TODO at method" in
+ * {@link WindowInsets}.
+ *
+ * @param types A bitmask of {@link WindowInsets.Type.InsetType} specifying what windows the app
+ * would like to make disappear.
+ */
+ void hide(@InsetType int types);
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8d8a370..45c3651 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1752,15 +1752,6 @@
public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
/**
- * If this flag is set on the window, window manager will acquire a sleep token that puts
- * all activities to sleep as long as this window is visible. When this flag is set, the
- * window needs to occlude all activity windows.
- * @hide
- */
- @RequiresPermission(permission.DEVICE_POWER)
- public static final int PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN = 0x00200000;
-
- /**
* Flag to indicate that this window should be considered a screen decoration similar to the
* nav bar and status bar. This will cause this window to affect the window insets reported
* to other windows when it is visible.
@@ -1872,10 +1863,6 @@
equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
name = "IS_ROUNDED_CORNERS_OVERLAY"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
- equals = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
- name = "ACQUIRES_SLEEP_TOKEN"),
- @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_IS_SCREEN_DECOR,
equals = PRIVATE_FLAG_IS_SCREEN_DECOR,
name = "IS_SCREEN_DECOR"),
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 260e938..16bafe2 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -44,6 +44,7 @@
int PRESENCE_EXTERNAL = 1 << 1;
// Navigation bar position values
+ int NAV_BAR_INVALID = -1;
int NAV_BAR_LEFT = 1 << 0;
int NAV_BAR_RIGHT = 1 << 1;
int NAV_BAR_BOTTOM = 1 << 2;
diff --git a/core/java/android/view/inspector/ChildTraverser.java b/core/java/android/view/inspector/ChildTraverser.java
deleted file mode 100644
index b775de5..0000000
--- a/core/java/android/view/inspector/ChildTraverser.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.
- */
-
-package android.view.inspector;
-
-import android.annotation.NonNull;
-
-/**
- * Interface for visiting all the child nodes of an inspectable object.
- *
- * Inspectable objects may return a collection of children as an array, an {@link Iterable} or an
- * {@link java.util.Iterator}. This provides a unified API for traversing across all the children
- * of an inspectable node.
- *
- * This interface is consumed by {@link InspectionHelper#traverseChildren(Object, ChildTraverser)}
- * and may be implemented as a lambda.
- *
- * @see InspectionHelper#traverseChildren(Object, ChildTraverser)
- * @hide
- */
-@FunctionalInterface
-public interface ChildTraverser {
- /**
- * Visit one child object of a parent inspectable object.
- *
- * The iteration interface will filter null values out before passing them to this method, but
- * some child objects may not be inspectable. It is up to the implementor to determine their
- * inspectablity and what to do with them.
- *
- * @param child A child object, guaranteed not to be null.
- */
- void traverseChild(@NonNull Object child);
-}
diff --git a/core/java/android/view/inspector/InspectableChildren.java b/core/java/android/view/inspector/InspectableChildren.java
deleted file mode 100644
index de8fa29..0000000
--- a/core/java/android/view/inspector/InspectableChildren.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.
- */
-
-package android.view.inspector;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Marks a getter for an inspectable node's inspectable children.
- *
- * This annotation can be applied to any getter that returns a collection of objects, either an
- * array, an {@link Iterable} or a {@link java.util.Iterator}. The getter may return null, which
- * will be treated as an empty collection. Additionally, the inspector will discard any null
- * entries in the collection.
- *
- * By default, this annotation is inherited. At runtime, the inspector introspects on the class
- * hierachy and uses the annotated getter from the bottommost class, if different from any
- * annoated getters of the parent class. If a class inherits from a parent class with an annotated
- * getter, but does not include this annotation, the child class will be traversed using the
- * getter annotated on the parent. This holds true even if the child class overrides the getter.
- *
- * @see InspectionHelper#traverseChildren(Object, ChildTraverser)
- * @see InspectionHelper#hasChildTraversal()
- * @hide
- */
-@Target({METHOD})
-@Retention(SOURCE)
-public @interface InspectableChildren {
-}
diff --git a/core/java/android/view/inspector/InspectableNodeName.java b/core/java/android/view/inspector/InspectableNodeName.java
index 716409c..ea94ad4 100644
--- a/core/java/android/view/inspector/InspectableNodeName.java
+++ b/core/java/android/view/inspector/InspectableNodeName.java
@@ -34,7 +34,7 @@
* This annotation does not inherit. If a class extends an annotated parent class, but does not
* annotate itself, its node name will be inferred from its Java name.
*
- * @see InspectionHelper#getNodeName()
+ * @see InspectionCompanion#getNodeName()
* @hide
*/
@Target({TYPE})
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index b0fd503..5b957156 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -17,8 +17,11 @@
package android.view.inspector;
import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.content.res.ResourceId;
+
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -31,8 +34,8 @@
* but on a different getter, the inspector will use the child's getter when inspecting instances
* of the child, and the parent's otherwise.
*
- * @see InspectionHelper#mapProperties(PropertyMapper)
- * @see InspectionHelper#readProperties(Object, PropertyReader)
+ * @see InspectionCompanion#mapProperties(PropertyMapper)
+ * @see InspectionCompanion#readProperties(Object, PropertyReader)
* @hide
*/
@Target({METHOD})
@@ -46,5 +49,171 @@
*
* @return The name of the property.
*/
- String value() default "";
+ String name() default "";
+
+ /**
+ * If the property is inflated from XML, the resource ID of its XML attribute.
+ *
+ * If left as {ID_NULL}, and {@link #hasAttributeId()} is true, the attribute ID will be
+ * inferred from {@link #name()}.
+ *
+ * @return The attribute ID of the property or {@link ResourceId#ID_NULL}
+ */
+ int attributeId() default ResourceId.ID_NULL;
+
+ /**
+ * If this property has an attribute ID.
+ *
+ * Set to false if the annotated property does not have an attribute ID, that is, it is not
+ * inflated from an XML attribute. This will prevent the automatic inference of the attribute
+ * ID if {@link #attributeId()} is set to {@link ResourceId#ID_NULL}.
+ *
+ * @return Whether to infer an attribute ID if not supplied
+ */
+ boolean hasAttributeId() default true;
+
+ /**
+ * Specify how to interpret a value type packed into a primitive integer.
+ *
+ * @return A {@link ValueType}
+ */
+ ValueType valueType() default ValueType.INFERRED;
+
+ /**
+ * For enumerations packed into primitive {int} properties, map the values to string names.
+ *
+ * Note that {@link #enumMapping()} cannot be used simultaneously with {@link #flagMapping()}.
+ *
+ * @return An array of {@link EnumMap}, empty if not applicable
+ * @see android.annotation.IntDef
+ * @see IntEnumMapping
+ */
+ EnumMap[] enumMapping() default {};
+
+ /**
+ * For flags packed into primitive {int} properties, model the string names of the flags.
+ *
+ * Note that {@link #flagMapping()} cannot be used simultaneously with {@link #enumMapping()}.
+ *
+ * @return An array of {@link FlagMap}, empty if not applicable
+ * @see android.annotation.IntDef
+ * @see IntFlagMapping
+ */
+ FlagMap[] flagMapping() default {};
+
+
+ /**
+ * One entry in an enumeration packed into a primitive {int}.
+ *
+ * @see IntEnumMapping
+ * @hide
+ */
+ @Target({TYPE})
+ @Retention(SOURCE)
+ @interface EnumMap {
+ /**
+ * The string name of this enumeration value.
+ *
+ * @return A string name
+ */
+ String name();
+
+ /**
+ * The integer value of this enumeration value.
+ *
+ * @return An integer value
+ */
+ int value();
+ }
+
+ /**
+ * One flag value of many that may be packed into a primitive {int}.
+ *
+ * @see IntFlagMapping
+ * @hide
+ */
+ @Target({TYPE})
+ @Retention(SOURCE)
+ @interface FlagMap {
+ /**
+ * The string name of this flag.
+ *
+ * @return A string name
+ */
+ String name();
+
+ /**
+ * A target value that the property's value must equal after masking.
+ *
+ * If a mask is not supplied (i.e., {@link #mask()} is 0), the target will be reused as the
+ * mask. This handles the common case where no flags mutually exclude each other.
+ *
+ * @return The target value to compare against
+ */
+ int target();
+
+ /**
+ * A mask that the property will be bitwise anded with before comparing to the target.
+ *
+ * If set to 0 (the default), the value of {@link #target()} will be used as a mask. Zero
+ * was chosen as the default since bitwise and with zero is always zero.
+ *
+ * @return A mask, or 0 to use the target as a mask
+ */
+ int mask() default 0;
+ }
+
+ /**
+ * The type of value packed into a primitive {int}.
+ *
+ * @hide
+ */
+ enum ValueType {
+ /**
+ * No special handling, property is considered to be a numeric value.
+ */
+ NONE,
+
+ /**
+ * The default the annotation processor infers the value type from context.
+ */
+ INFERRED,
+
+ /**
+ * Value packs an enumeration.
+ *
+ * This is inferred if {@link #enumMapping()} is specified.
+ *
+ * @see EnumMap
+ */
+ INT_ENUM,
+
+ /**
+ * Value packs flags, of which many may be enabled at once.
+ *
+ * This is inferred if {@link #flagMapping()} is specified.
+ *
+ * @see FlagMap
+ */
+ INT_FLAG,
+
+ /**
+ * Value packs color information.
+ *
+ * This is inferred from {@link android.annotation.ColorInt}, or
+ * {@link android.annotation.ColorLong} on the getter method.
+ *
+ * @see android.graphics.Color
+ */
+ COLOR,
+
+ /**
+ * Value packs gravity information.
+ *
+ * This type is not inferred, and is non-trivial to represent using {@link FlagMap}.
+ *
+ * @see android.view.Gravity
+ */
+ GRAVITY
+ }
}
diff --git a/core/java/android/view/inspector/InspectionCompanion.java b/core/java/android/view/inspector/InspectionCompanion.java
new file mode 100644
index 0000000..62d769b
--- /dev/null
+++ b/core/java/android/view/inspector/InspectionCompanion.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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.
+ */
+
+package android.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * An interface for companion objects used to inspect views.
+ *
+ * Inspection companions only need to handle the properties and node name of the specific class
+ * they are defined for, not anything from a parent class. At runtime, the inspector instantiates
+ * one instance of each inspection companion, and handles visiting them in the correct inheritance
+ * order for each type it inspects.
+ *
+ * Properties are read from the top of the type tree to the bottom, so that classes that override
+ * a property in their parent class can overwrite it in the reader. In general, properties will
+ * cleanly inherit through their getters, and the inspector runtime will read the properties of a
+ * parent class via the parent's inspection companion, and the child companion will only read
+ * properties added or changed since the parent was defined.
+ *
+ * Only one child traversal is considered for each class. If a descendant class defines a
+ * different child traversal than its parent, only the bottom traversal is used. If a class does
+ * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's
+ * traversal will be used.
+ *
+ * @param <T> The type of inspectable this is the companion to
+ */
+public interface InspectionCompanion<T> {
+ /**
+ * Map the string names of the properties this companion knows about to integer IDs.
+ *
+ * Each companion is responsible for storing the integer IDs of all its properties. This is the
+ * only method that is allowed to modify the stored IDs.
+ *
+ * Calling {@link #readProperties(T, PropertyReader)} before calling this results in
+ * undefined behavior.
+ *
+ * @param propertyMapper A {@link PropertyMapper} maps string names to IDs.
+ */
+ void mapProperties(@NonNull PropertyMapper propertyMapper);
+
+ /**
+ * Read the values of an instance of this companion's type into a {@link PropertyReader}.
+ *
+ * This method needs to return the property IDs stored by
+ * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties
+ * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is
+ * called before {mapProperties}.
+ *
+ * @param inspectable A object of type {T} to read the properties of.
+ * @param propertyReader An object which receives the property IDs and values.
+ */
+ void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader);
+
+ /**
+ * Get an optional name to display to developers for inspection nodes of this companion's type.
+ *
+ * The default implementation returns null, which will cause the runtime to use the class's
+ * simple name as defined by {@link Class#getSimpleName()} as the node name.
+ *
+ * If the type of this companion is inflated from XML, this method should be overridden to
+ * return the string used as the tag name for this type in XML.
+ *
+ * @return A string to use as the node name, or null to use the simple class name fallback.
+ */
+ @Nullable
+ default String getNodeName() {
+ return null;
+ }
+
+ /**
+ * Thrown by {@link #readProperties(Object, PropertyReader)} if called before
+ * {@link #mapProperties(PropertyMapper)}.
+ */
+ class UninitializedPropertyMapException extends RuntimeException {
+ public UninitializedPropertyMapException() {
+ super("Unable to read properties of an inspectable before mapping their IDs.");
+ }
+ }
+}
diff --git a/core/java/android/view/inspector/InspectionHelper.java b/core/java/android/view/inspector/InspectionHelper.java
deleted file mode 100644
index 27a9704..0000000
--- a/core/java/android/view/inspector/InspectionHelper.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright 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.
- */
-
-package android.view.inspector;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * An interface for companion objects used to inspect views.
- *
- * Inspection helpers only need to handle the properties, name and traversal of the specific class
- * they are defined for, not anything from a parent class. At runtime, the inspector instantiates
- * one instance of each inspection helper, and handles visiting them in the correct inheritance
- * order for each type it inspects.
- *
- * Properties are read from the top of the type tree to the bottom, so that classes that override
- * a property in their parent class can overwrite it in the reader. In general, properties will
- * cleanly inherit through their getters, and the inspector runtime will read the properties of a
- * parent class via the parent's inspection helper, and the child helper will only read properties
- * added or changed since the parent was defined.
- *
- * Only one child traversal is considered for each class. If a descendant class defines a
- * different child traversal than its parent, only the bottom traversal is used. If a class does
- * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's
- * traversal will be used.
- *
- * @param <T> The type of inspectable this helper operates on
- * @hide
- */
-public interface InspectionHelper<T> {
- /**
- * Map the string names of the properties this helper knows about to integer IDs.
- *
- * Each helper is responsible for storing the integer IDs of all its properties. This is the
- * only method that is allowed to modify the stored IDs.
- *
- * Calling {@link #readProperties(T, PropertyReader)} before calling this results in
- * undefined behavior.
- *
- * @param propertyMapper A {@link PropertyMapper} or lambda which maps string names to IDs.
- */
- void mapProperties(@NonNull PropertyMapper propertyMapper);
-
- /**
- * Read the values of an instance of this helper's type into a {@link PropertyReader}.
- *
- * This method needs to return the property IDs stored by
- * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties
- * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is
- * called before {mapProperties}.
- *
- * @param inspectable A object of type {@link T} to read the properties of.
- * @param propertyReader An object which receives the property IDs and values.
- */
- void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader);
-
- /**
- * Query if this inspectable type can potentially have child nodes.
- *
- * E.g.: any descendant of {@link android.view.ViewGroup} can have child nodes, but a leaf
- * view like {@link android.widget.ImageView} may not.
- *
- * The default implementation always returns false. If an implementing class overrides this, it
- * should also define {@link #traverseChildren(T, ChildTraverser)}.
- *
- * @return True if this inspectable type can potentially have child nodes, false otherwise.
- */
- default boolean hasChildTraversal() {
- return false;
- }
-
- /**
- * Traverse the child nodes of an instance of this helper's type into a {@link ChildTraverser}.
- *
- * This provides the ability to traverse over a variety of collection APIs (e.g.: arrays,
- * {@link Iterable}, or {@link java.util.Iterator}) in a uniform fashion. The traversal must be
- * in the order defined by this helper's type. If the getter returns null, the helper must
- * treat it as an empty collection.
- *
- * The default implementation throws a {@link NoChildTraversalException}. If
- * {@link #hasChildTraversal()} returns is overriden to return true, it is expected that the
- * implementing class will also override this method and provide a traversal.
- *
- * @param inspectable An object of type {@link T} to traverse the child nodes of.
- * @param childTraverser A {@link ChildTraverser} or lamba to receive the children in order.
- * @throws NoChildTraversalException If there is no defined child traversal
- */
- default void traverseChildren(
- @NonNull T inspectable,
- @SuppressWarnings("unused") @NonNull ChildTraverser childTraverser) {
- throw new NoChildTraversalException(inspectable.getClass());
- }
-
- /**
- * Get an optional name to display to developers for inspection nodes of this helper's type.
- *
- * The default implementation returns null, which will cause the runtime to use the class's
- * simple name as defined by {@link Class#getSimpleName()} as the node name.
- *
- * If the type of this helper is inflated from XML, this method should be overridden to return
- * the string used as the tag name for this type in XML.
- *
- * @return A string to use as the node name, or null to use the simple class name fallback.
- */
- @Nullable
- default String getNodeName() {
- return null;
- }
-
- /**
- * Thrown by {@link #readProperties(Object, PropertyReader)} if called before
- * {@link #mapProperties(PropertyMapper)}.
- */
- class UninitializedPropertyMapException extends RuntimeException {
- public UninitializedPropertyMapException() {
- super("Unable to read properties of an inspectable before mapping their IDs.");
- }
- }
-
- /**
- * Thrown by {@link #traverseChildren(Object, ChildTraverser)} if no child traversal exists.
- */
- class NoChildTraversalException extends RuntimeException {
- public NoChildTraversalException(Class cls) {
- super(String.format(
- "Class %s does not have a defined child traversal. Cannot traverse children.",
- cls.getCanonicalName()
- ));
- }
- }
-}
diff --git a/core/java/android/view/inspector/IntEnumMapping.java b/core/java/android/view/inspector/IntEnumMapping.java
new file mode 100644
index 0000000..69f6dce
--- /dev/null
+++ b/core/java/android/view/inspector/IntEnumMapping.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 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.
+ */
+
+package android.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Maps the values of an {int} property to string names for properties that encode enumerations.
+ *
+ * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
+ * for enumerations packed into primitive {int} properties.
+ *
+ * This class is immutable, and must be constructed by a {@link Builder}.
+ *
+ * @see PropertyMapper#mapIntEnum(String, int, IntEnumMapping)
+ */
+public final class IntEnumMapping {
+ private final Value[] mValues;
+
+ /**
+ * Map from a property value to a string name.
+ *
+ * @param value The value of a property
+ * @return The name of the enumeration value, null if the value is not mapped
+ */
+ @Nullable
+ public String nameOf(int value) {
+ for (Value valueTuple : mValues) {
+ if (valueTuple.mValue == value) {
+ return valueTuple.mName;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Create a new instance from a builder.
+ *
+ * This constructor is private, use {@link Builder#build()} instead.
+ *
+ * @param builder A builder to create from
+ */
+ private IntEnumMapping(Builder builder) {
+ mValues = builder.mValues.toArray(new Value[builder.mValues.size()]);
+ }
+
+ /**
+ * A builder for {@link IntEnumMapping}
+ */
+ public static final class Builder {
+ private final ArrayList<Value> mValues;
+
+ public Builder() {
+ mValues = new ArrayList<>();
+ }
+
+ /**
+ * Add a new entry to this mapping.
+ *
+ * @param name Name of the enumeration value
+ * @param value Int value of the enumeration value
+ * @return This builder
+ */
+ @NonNull
+ public Builder addValue(@NonNull String name, int value) {
+ mValues.add(new Value(name, value));
+ return this;
+ }
+
+ /**
+ * Clear the builder, allowing for recycling.
+ */
+ public void clear() {
+ mValues.clear();
+ }
+
+ /**
+ * Build a new {@link IntEnumMapping} from this builder
+ *
+ * @return A new mapping
+ */
+ @NonNull
+ public IntEnumMapping build() {
+ return new IntEnumMapping(this);
+ }
+ }
+
+ /**
+ * Inner class that holds the name and value of an enumeration value.
+ */
+ private static final class Value {
+ @NonNull private final String mName;
+ private final int mValue;
+
+ private Value(@NonNull String name, int value) {
+ mName = name;
+ mValue = value;
+ }
+ }
+}
diff --git a/core/java/android/view/inspector/IntFlagMapping.java b/core/java/android/view/inspector/IntFlagMapping.java
new file mode 100644
index 0000000..dcb87e1
--- /dev/null
+++ b/core/java/android/view/inspector/IntFlagMapping.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 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.
+ */
+
+package android.view.inspector;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+
+/**
+ * Maps the values of an {int} property to arrays of string for properties that encode flags.
+ *
+ * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
+ * for flag values packed into primitive {int} properties.
+ *
+ * Each flag has a
+ *
+ * This class is immutable, and must be constructed by a {@link Builder}.
+ *
+ * @see PropertyMapper#mapIntFlag(String, int, IntFlagMapping)
+ */
+public final class IntFlagMapping {
+ private final Flag[] mFlags;
+
+ /**
+ * Get an array of the names of enabled flags for a given property value.
+ *
+ * @param value The value of the property
+ * @return The names of the enabled flags
+ */
+ @NonNull
+ public String[] namesOf(int value) {
+ ArrayList<String> enabledFlagNames = new ArrayList<>(mFlags.length);
+
+ for (Flag flag : mFlags) {
+ if (flag.isEnabledFor(value)) {
+ enabledFlagNames.add(flag.mName);
+ }
+ }
+
+ return enabledFlagNames.toArray(new String[enabledFlagNames.size()]);
+ }
+
+ /**
+ * Create a new instance from a builder.
+ *
+ * This constructor is private, use {@link Builder#build()} instead.
+ *
+ * @param builder A builder to create from
+ */
+ private IntFlagMapping(Builder builder) {
+ mFlags = builder.mFlags.toArray(new Flag[builder.mFlags.size()]);
+ }
+
+ /**
+ * A builder for {@link IntFlagMapping}.
+ */
+ public static final class Builder {
+ private ArrayList<Flag> mFlags;
+
+ public Builder() {
+ mFlags = new ArrayList<>();
+ }
+
+ /**
+ * Add a new flag without a mask.
+ *
+ * The target value will be used as a mask, to handle the common case where flag values
+ * are not mutually exclusive. The flag will be considered enabled for a property value if
+ * the result of bitwise anding the target and the value equals the target, that is:
+ * {(value & target) == target}.
+ *
+ * @param name The name of the flag
+ * @param target The value to compare against
+ * @return This builder
+ */
+ @NonNull
+ public Builder addFlag(@NonNull String name, int target) {
+ mFlags.add(new Flag(name, target, target));
+ return this;
+ }
+
+ /**
+ * Add a new flag with a mask.
+ *
+ * The flag will be considered enabled for a property value if the result of bitwise anding
+ * the value and the mask equals the target, that is: {(value & mask) == target}.
+ *
+ * @param name The name of the flag
+ * @param target The value to compare against
+ * @param mask A bit mask
+ * @return This builder
+ */
+ @NonNull
+ public Builder addFlag(@NonNull String name, int target, int mask) {
+ mFlags.add(new Flag(name, target, mask));
+ return this;
+ }
+
+ /**
+ * Clear the builder, allowing for recycling.
+ */
+ public void clear() {
+ mFlags.clear();
+ }
+
+ /**
+ * Build a new {@link IntFlagMapping} from this builder.
+ *
+ * @return A new mapping
+ */
+ @NonNull
+ public IntFlagMapping build() {
+ return new IntFlagMapping(this);
+ }
+ }
+
+ /**
+ * Inner class that holds the name, mask, and target value of a flag
+ */
+ private static final class Flag {
+ @NonNull private final String mName;
+ private final int mTarget;
+ private final int mMask;
+
+ private Flag(@NonNull String name, int target, int mask) {
+ mName = name;
+ mTarget = target;
+ mMask = mask;
+ }
+
+ /**
+ * Compare the supplied property value against the mask and taget.
+ *
+ * @param value The value to check
+ * @return True if this flag is enabled
+ */
+ private boolean isEnabledFor(int value) {
+ return (value & mMask) == mTarget;
+ }
+ }
+}
diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java
index 35550bd..5fb291b 100644
--- a/core/java/android/view/inspector/PropertyMapper.java
+++ b/core/java/android/view/inspector/PropertyMapper.java
@@ -16,102 +16,160 @@
package android.view.inspector;
+import android.annotation.AttrRes;
import android.annotation.NonNull;
/**
* An interface for mapping the string names of inspectable properties to integer identifiers.
*
- * This interface is consumed by {@link InspectionHelper#mapProperties(PropertyMapper)}.
+ * This interface is consumed by {@link InspectionCompanion#mapProperties(PropertyMapper)}.
*
* Mapping properties to IDs enables quick comparisons against shadow copies of inspectable
* objects without performing a large number of string comparisons.
*
- * @see InspectionHelper#mapProperties(PropertyMapper)
- * @hide
+ * @see InspectionCompanion#mapProperties(PropertyMapper)
*/
public interface PropertyMapper {
/**
* Map a string name to an integer ID for a primitive boolean property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapBoolean(@NonNull String name);
+ int mapBoolean(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive byte property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapByte(@NonNull String name);
+ int mapByte(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive char property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapChar(@NonNull String name);
+ int mapChar(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive double property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapDouble(@NonNull String name);
+ int mapDouble(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive float property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapFloat(@NonNull String name);
+ int mapFloat(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive int property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapInt(@NonNull String name);
+ int mapInt(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive long property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapLong(@NonNull String name);
+ int mapLong(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for a primitive short property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapShort(@NonNull String name);
+ int mapShort(@NonNull String name, @AttrRes int attributeId);
/**
* Map a string name to an integer ID for an object property.
*
* @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
* @return An integer ID for the property
* @throws PropertyConflictException If the property name is already mapped as another type.
*/
- int mapObject(@NonNull String name);
+ int mapObject(@NonNull String name, @AttrRes int attributeId);
/**
+ * Map a string name to an integer ID for a color property.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ * @see android.graphics.Color
+ */
+ int mapColor(@NonNull String name, @AttrRes int attributeId);
+
+ /**
+ * Map a string name to an integer ID for a gravity property.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ * @see android.view.Gravity
+ */
+ int mapGravity(@NonNull String name, @AttrRes int attributeId);
+
+ /**
+ * Map a string name to an integer ID for an enumeration packed into an int property.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @param mapping A mapping from int to String
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ */
+ int mapIntEnum(
+ @NonNull String name,
+ @AttrRes int attributeId,
+ @NonNull IntEnumMapping mapping);
+
+ /**
+ * Map a string name to an integer ID for a flag set packed into an int property.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @param mapping A mapping from int to an array of strings
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ */
+ int mapIntFlag(
+ @NonNull String name,
+ @AttrRes int attributeId,
+ @NonNull IntFlagMapping mapping);
+ /**
* Thrown from a map method if a property name is already mapped as different type.
*/
class PropertyConflictException extends RuntimeException {
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index df81c10..fd83e8d 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -16,19 +16,21 @@
package android.view.inspector;
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Color;
/**
* An interface for reading the properties of an inspectable object.
*
- * Used as the parameter for {@link InspectionHelper#readProperties(Object, PropertyReader)}.
+ * Used as the parameter for {@link InspectionCompanion#readProperties(Object, PropertyReader)}.
* It has separate methods for all primitive types to avoid autoboxing overhead if a concrete
* implementation is able to work with primitives. Implementations should be prepared to accept
* {null} as the value of {@link PropertyReader#readObject(int, Object)}.
*
- * @see InspectionHelper#readProperties(Object, PropertyReader)
- * @hide
+ * @see InspectionCompanion#readProperties(Object, PropertyReader)
*/
public interface PropertyReader {
/**
@@ -115,6 +117,60 @@
void readObject(int id, @Nullable Object value);
/**
+ * Read a color packed into a {@link ColorInt} as a property.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+ */
+ void readColor(int id, @ColorInt int value);
+
+ /**
+ * Read a color packed into a {@link ColorLong} as a property.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+ */
+ void readColor(int id, @ColorLong long value);
+
+ /**
+ * Read a {@link Color} object as a property.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+ */
+ void readColor(int id, @Nullable Color value);
+
+ /**
+ * Read {@link android.view.Gravity} packed into an primitive {int}.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a gravity property
+ */
+ void readGravity(int id, int value);
+
+ /**
+ * Read an enumeration packed into a primitive {int}.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as an object
+ */
+ void readIntEnum(int id, int value);
+
+ /**
+ * Read a flag packed into a primitive {int}.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as an object
+ */
+ void readIntFlag(int id, int value);
+
+ /**
* Thrown if a client calls a typed read method for a property of a different type.
*/
class PropertyTypeMismatchException extends RuntimeException {
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/intelligence/ContentCaptureEvent.java
index befcb55..f636281 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.java
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.java
@@ -163,7 +163,7 @@
* Gets optional flags associated with the event.
*
* @return either {@code 0} or
- * {@link android.view.intelligence.IntelligenceManager#FLAG_USER_INPUT}.
+ * {@link android.view.intelligence.ContentCaptureManager#FLAG_USER_INPUT}.
*/
public int getFlags() {
return mFlags;
diff --git a/core/java/android/view/intelligence/ContentCaptureManager.java b/core/java/android/view/intelligence/ContentCaptureManager.java
new file mode 100644
index 0000000..45518d5
--- /dev/null
+++ b/core/java/android/view/intelligence/ContentCaptureManager.java
@@ -0,0 +1,483 @@
+/*
+ * 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.
+ */
+package android.view.intelligence;
+
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.intelligence.InteractionSessionId;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
+import android.view.intelligence.ContentCaptureEvent.EventType;
+
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/*
+ * NOTE: all methods in this class should return right away, or do the real work in a handler
+ * thread.
+ *
+ * Hence, the only field that must be thread-safe is mEnabled, which is called at the beginning
+ * of every method.
+ */
+/**
+ * TODO(b/111276913): add javadocs / implement
+ */
+@SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE)
+public final class ContentCaptureManager {
+
+ private static final String TAG = "ContentCaptureManager";
+
+ // TODO(b/111276913): define a way to dynamically set them(for example, using settings?)
+ private static final boolean VERBOSE = false;
+ private static final boolean DEBUG = true; // STOPSHIP if not set to false
+
+ /**
+ * Used to indicate that a text change was caused by user input (for example, through IME).
+ */
+ //TODO(b/111276913): link to notifyTextChanged() method once available
+ public static final int FLAG_USER_INPUT = 0x1;
+
+ /**
+ * Initial state, when there is no session.
+ *
+ * @hide
+ */
+ public static final int STATE_UNKNOWN = 0;
+
+ /**
+ * Service's startSession() was called, but server didn't confirm it was created yet.
+ *
+ * @hide
+ */
+ public static final int STATE_WAITING_FOR_SERVER = 1;
+
+ /**
+ * Session is active.
+ *
+ * @hide
+ */
+ public static final int STATE_ACTIVE = 2;
+
+ /**
+ * Session is disabled.
+ *
+ * @hide
+ */
+ public static final int STATE_DISABLED = 3;
+
+ private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
+
+ /**
+ * Maximum number of events that are buffered before sent to the app.
+ */
+ // TODO(b/111276913): use settings
+ private static final int MAX_BUFFER_SIZE = 100;
+
+ @NonNull
+ private final AtomicBoolean mDisabled = new AtomicBoolean();
+
+ @NonNull
+ private final Context mContext;
+
+ @Nullable
+ private final IIntelligenceManager mService;
+
+ @Nullable
+ private InteractionSessionId mId;
+
+ private int mState = STATE_UNKNOWN;
+
+ @Nullable
+ private IBinder mApplicationToken;
+
+ @Nullable
+ private ComponentName mComponentName;
+
+ /**
+ * List of events held to be sent as a batch.
+ */
+ @Nullable
+ private ArrayList<ContentCaptureEvent> mEvents;
+
+ // TODO(b/111276913): use UI Thread directly (as calls are one-way) or a shared thread / handler
+ // held at the Application level
+ private final Handler mHandler;
+
+ /** @hide */
+ public ContentCaptureManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
+ mContext = Preconditions.checkNotNull(context, "context cannot be null");
+ if (VERBOSE) {
+ Log.v(TAG, "Constructor for " + context.getPackageName());
+ }
+ mService = service;
+ // TODO(b/111276913): use an existing bg thread instead...
+ final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
+ bgThread.start();
+ mHandler = Handler.createAsync(bgThread.getLooper());
+ }
+
+ /** @hide */
+ public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
+ if (!isContentCaptureEnabled()) return;
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleStartSession, this,
+ token, componentName));
+ }
+
+ private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
+ if (mState != STATE_UNKNOWN) {
+ // TODO(b/111276913): revisit this scenario
+ Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
+ + getStateAsString(mState));
+ return;
+ }
+ mState = STATE_WAITING_FOR_SERVER;
+ mId = new InteractionSessionId();
+ mApplicationToken = token;
+ mComponentName = componentName;
+
+ if (VERBOSE) {
+ Log.v(TAG, "handleStartSession(): token=" + token + ", act="
+ + getActivityDebugName() + ", id=" + mId);
+ }
+ final int flags = 0; // TODO(b/111276913): get proper flags
+
+ try {
+ mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
+ mId, flags, new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ handleSessionStarted(resultCode);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
+ + e);
+ }
+ }
+
+ private void handleSessionStarted(int resultCode) {
+ mState = resultCode;
+ mDisabled.set(mState == STATE_DISABLED);
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityStarted() result: code=" + resultCode + ", id=" + mId
+ + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
+ }
+ }
+
+ private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ if (mEvents == null) {
+ if (VERBOSE) {
+ Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+ }
+ mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+ }
+ mEvents.add(event);
+ final int numberEvents = mEvents.size();
+ if (numberEvents < MAX_BUFFER_SIZE && !forceFlush) {
+ // Buffering events, return right away...
+ return;
+ }
+
+ if (mState != STATE_ACTIVE) {
+ // Callback from startSession hasn't been called yet - typically happens on system
+ // apps that are started before the system service
+ // TODO(b/111276913): try to ignore session while system is not ready / boot
+ // not complete instead. Similarly, the manager service should return right away
+ // when the user does not have a service set
+ if (VERBOSE) {
+ Log.v(TAG, "Closing session for " + getActivityDebugName()
+ + " after " + numberEvents + " delayed events and state "
+ + getStateAsString(mState));
+ }
+ handleResetState();
+ // TODO(b/111276913): blacklist activity / use special flag to indicate that
+ // when it's launched again
+ return;
+ }
+
+ if (mId == null) {
+ // Sanity check - should not happen
+ Log.wtf(TAG, "null session id for " + getActivityDebugName());
+ return;
+ }
+
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+ }
+ mService.sendEvents(mContext.getUserId(), mId, mEvents);
+ // TODO(b/111276913): decide whether we should clear or set it to null, as each has
+ // its own advantages: clearing will save extra allocations while the session is
+ // active, while setting to null would save memory if there's no more event coming.
+ mEvents.clear();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
+ + ": " + e);
+ }
+ }
+
+ /**
+ * Used for intermediate events (i.e, other than created and destroyed).
+ *
+ * @hide
+ */
+ public void onActivityLifecycleEvent(@EventType int type) {
+ if (!isContentCaptureEnabled()) return;
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugName()
+ + ": " + ContentCaptureEvent.getTypeAsString(type));
+ }
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+ new ContentCaptureEvent(type), /* forceFlush= */ true));
+ }
+
+ /** @hide */
+ public void onActivityDestroyed() {
+ if (!isContentCaptureEnabled()) return;
+
+ //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+ // id) and send it to the cache of batched commands
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
+ + ", mId=" + mId);
+ }
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleFinishSession, this));
+ }
+
+ private void handleFinishSession() {
+ //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
+ // to system_server, so it's ok to call both in sequence here. But once we split
+ // them so the events are sent directly to the service, we need to make sure they're
+ // sent in order.
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "Finishing session " + mId + " with "
+ + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+ + getActivityDebugName());
+ }
+
+ mService.finishSession(mContext.getUserId(), mId, mEvents);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error finishing session " + mId + " for " + getActivityDebugName()
+ + ": " + e);
+ } finally {
+ handleResetState();
+ }
+ }
+
+ private void handleResetState() {
+ mState = STATE_UNKNOWN;
+ mId = null;
+ mApplicationToken = null;
+ mComponentName = null;
+ mEvents = null;
+ }
+
+ /**
+ * Notifies the Intelligence Service that a node has been added to the view structure.
+ *
+ * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+ * automatically by the Android System for views that return {@code true} on
+ * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
+ *
+ * @param node node that has been added.
+ */
+ public void notifyViewAppeared(@NonNull ViewStructure node) {
+ Preconditions.checkNotNull(node);
+ if (!isContentCaptureEnabled()) return;
+
+ if (!(node instanceof ViewNode.ViewStructureImpl)) {
+ throw new IllegalArgumentException("Invalid node class: " + node.getClass());
+ }
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_APPEARED)
+ .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
+ /* forceFlush= */ false));
+ }
+
+ /**
+ * Notifies the Intelligence Service that a node has been removed from the view structure.
+ *
+ * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+ * automatically by the Android System for standard views.
+ *
+ * @param id id of the node that has been removed.
+ */
+ public void notifyViewDisappeared(@NonNull AutofillId id) {
+ Preconditions.checkNotNull(id);
+ if (!isContentCaptureEnabled()) return;
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
+ /* forceFlush= */ false));
+ }
+
+ /**
+ * Notifies the Intelligence Service that the value of a text node has been changed.
+ *
+ * @param id of the node.
+ * @param text new text.
+ * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
+ * changed by the user (for example, through the keyboard).
+ */
+ public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags) {
+ Preconditions.checkNotNull(id);
+
+ if (!isContentCaptureEnabled()) return;
+
+ mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+ new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+ .setText(text), /* forceFlush= */ false));
+ }
+
+ /**
+ * Creates a {@link ViewStructure} for a "standard" view.
+ *
+ * @hide
+ */
+ @NonNull
+ public ViewStructure newViewStructure(@NonNull View view) {
+ return new ViewNode.ViewStructureImpl(view);
+ }
+
+ /**
+ * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
+ * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
+ *
+ * @param parentId id of the virtual view parent (it can be obtained by calling
+ * {@link ViewStructure#getAutofillId()} on the parent).
+ * @param virtualId id of the virtual child, relative to the parent.
+ *
+ * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
+ */
+ @NonNull
+ public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
+ return new ViewNode.ViewStructureImpl(parentId, virtualId);
+ }
+
+ /**
+ * Returns the component name of the system service that is consuming the captured events for
+ * the current user.
+ */
+ @Nullable
+ public ComponentName getServiceComponentName() {
+ //TODO(b/111276913): implement
+ return null;
+ }
+
+ /**
+ * Checks whether content capture is enabled for this activity.
+ */
+ public boolean isContentCaptureEnabled() {
+ return mService != null && !mDisabled.get();
+ }
+
+ /**
+ * Called by apps to explicitly enable or disable content capture.
+ *
+ * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
+ * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
+ */
+ public void setContentCaptureEnabled(boolean enabled) {
+ //TODO(b/111276913): implement
+ }
+
+ /** @hide */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.println("IntelligenceManager");
+ final String prefix2 = prefix + " ";
+ pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
+ pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
+ if (mService != null) {
+ pw.print(prefix2); pw.print("mService: "); pw.println(mService);
+ }
+ pw.print(prefix2); pw.print("mDisabled: "); pw.println(mDisabled.get());
+ pw.print(prefix2); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+ if (mId != null) {
+ pw.print(prefix2); pw.print("id: "); pw.println(mId);
+ }
+ pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
+ pw.print(getStateAsString(mState)); pw.println(")");
+ if (mApplicationToken != null) {
+ pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
+ }
+ if (mComponentName != null) {
+ pw.print(prefix2); pw.print("component name: ");
+ pw.println(mComponentName.flattenToShortString());
+ }
+ if (mEvents != null) {
+ final int numberEvents = mEvents.size();
+ pw.print(prefix2); pw.print("buffered events: "); pw.print(numberEvents);
+ pw.print('/'); pw.println(MAX_BUFFER_SIZE);
+ if (VERBOSE && numberEvents > 0) {
+ final String prefix3 = prefix2 + " ";
+ for (int i = 0; i < numberEvents; i++) {
+ final ContentCaptureEvent event = mEvents.get(i);
+ pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+ pw.println();
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets a string that can be used to identify the activity on logging statements.
+ */
+ private String getActivityDebugName() {
+ return mComponentName == null ? mContext.getPackageName()
+ : mComponentName.flattenToShortString();
+ }
+
+ @NonNull
+ private static String getStateAsString(int state) {
+ switch (state) {
+ case STATE_UNKNOWN:
+ return "UNKNOWN";
+ case STATE_WAITING_FOR_SERVER:
+ return "WAITING_FOR_SERVER";
+ case STATE_ACTIVE:
+ return "ACTIVE";
+ case STATE_DISABLED:
+ return "DISABLED";
+ default:
+ return "INVALID:" + state;
+ }
+ }
+}
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
index 2f128de..882fb26 100644
--- a/core/java/android/view/intelligence/IIntelligenceManager.aidl
+++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl
@@ -28,6 +28,7 @@
/**
* {@hide}
*/
+// TODO(b/111276913): rename once the final name is defined
oneway interface IIntelligenceManager {
/**
* Starts a session, sending the "remote" sessionId to the receiver.
@@ -38,7 +39,8 @@
/**
* Finishes a session.
*/
- void finishSession(int userId, in InteractionSessionId sessionId);
+ void finishSession(int userId, in InteractionSessionId sessionId,
+ in List<ContentCaptureEvent> events);
/**
* Sends a batch of events
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
deleted file mode 100644
index 755c54c..0000000
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ /dev/null
@@ -1,554 +0,0 @@
-/*
- * 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.
- */
-package android.view.intelligence;
-
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_APPEARED;
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.service.intelligence.InteractionSessionId;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewStructure;
-import android.view.autofill.AutofillId;
-import android.view.intelligence.ContentCaptureEvent.EventType;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.IResultReceiver;
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * TODO(b/111276913): add javadocs / implement
- */
-@SystemService(Context.INTELLIGENCE_MANAGER_SERVICE)
-public final class IntelligenceManager {
-
- private static final String TAG = "IntelligenceManager";
-
- // TODO(b/111276913): define a way to dynamically set them(for example, using settings?)
- private static final boolean VERBOSE = false;
- private static final boolean DEBUG = true; // STOPSHIP if not set to false
-
- /**
- * Used to indicate that a text change was caused by user input (for example, through IME).
- */
- //TODO(b/111276913): link to notifyTextChanged() method once available
- public static final int FLAG_USER_INPUT = 0x1;
-
- /**
- * Initial state, when there is no session.
- *
- * @hide
- */
- public static final int STATE_UNKNOWN = 0;
-
- /**
- * Service's startSession() was called, but server didn't confirm it was created yet.
- *
- * @hide
- */
- public static final int STATE_WAITING_FOR_SERVER = 1;
-
- /**
- * Session is active.
- *
- * @hide
- */
- public static final int STATE_ACTIVE = 2;
-
- /**
- * Session is disabled.
- *
- * @hide
- */
- public static final int STATE_DISABLED = 3;
-
- private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
-
- /**
- * Maximum number of events that are delayed for an app.
- *
- * <p>If the session is not started after the limit is reached, it's discarded.
- */
- private static final int MAX_DELAYED_SIZE = 20;
-
- private final Context mContext;
-
- @Nullable
- private final IIntelligenceManager mService;
-
- private final Object mLock = new Object();
-
- @Nullable
- @GuardedBy("mLock")
- private InteractionSessionId mId;
-
- @GuardedBy("mLock")
- private int mState = STATE_UNKNOWN;
-
- @GuardedBy("mLock")
- private IBinder mApplicationToken;
-
- // TODO(b/111276913): replace by an interface name implemented by Activity, similar to
- // AutofillClient
- @GuardedBy("mLock")
- private ComponentName mComponentName;
-
- // TODO(b/111276913): create using maximum batch size as capacity
- /**
- * List of events held to be sent as a batch.
- */
- @GuardedBy("mLock")
- private final ArrayList<ContentCaptureEvent> mEvents = new ArrayList<>();
-
- private final Handler mHandler;
-
- /** @hide */
- public IntelligenceManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
- mContext = Preconditions.checkNotNull(context, "context cannot be null");
- mService = service;
-
- // TODO(b/111276913): use an existing bg thread instead...
- final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
- bgThread.start();
- mHandler = Handler.createAsync(bgThread.getLooper());
- }
-
- /** @hide */
- public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
- if (!isContentCaptureEnabled()) return;
-
- synchronized (mLock) {
- if (mState != STATE_UNKNOWN) {
- // TODO(b/111276913): revisit this scenario
- Log.w(TAG, "ignoring onActivityStarted(" + token + ") while on state "
- + getStateAsString(mState));
- return;
- }
- mState = STATE_WAITING_FOR_SERVER;
- mId = new InteractionSessionId();
- mApplicationToken = token;
- mComponentName = componentName;
-
- if (VERBOSE) {
- Log.v(TAG, "onActivityCreated(): token=" + token + ", act="
- + getActivityDebugNameLocked() + ", id=" + mId);
- }
- final int flags = 0; // TODO(b/111276913): get proper flags
-
- try {
- mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
- mId, flags, new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData)
- throws RemoteException {
- synchronized (mLock) {
- mState = resultCode;
- if (VERBOSE) {
- Log.v(TAG, "onActivityStarted() result: code=" + resultCode
- + ", id=" + mId
- + ", state=" + getStateAsString(mState));
- }
- }
- }
- });
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
- // calling right away
- private void sendEvent(@NonNull ContentCaptureEvent event) {
- mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this, event));
- }
-
- private void handleSendEvent(@NonNull ContentCaptureEvent event) {
-
- //TODO(b/111276913): make a copy and don't use lock
- synchronized (mLock) {
- mEvents.add(event);
- final int numberEvents = mEvents.size();
- if (mState != STATE_ACTIVE) {
- if (numberEvents >= MAX_DELAYED_SIZE) {
- // Typically happens on system apps that are started before the system service
- // is ready (like com.android.settings/.FallbackHome)
- //TODO(b/111276913): try to ignore session while system is not ready / boot
- // not complete instead. Similarly, the manager service should return right away
- // when the user does not have a service set
- if (VERBOSE) {
- Log.v(TAG, "Closing session for " + getActivityDebugNameLocked()
- + " after " + numberEvents + " delayed events and state "
- + getStateAsString(mState));
- }
- // TODO(b/111276913): blacklist activity / use special flag to indicate that
- // when it's launched again
- resetStateLocked();
- return;
- }
-
- if (VERBOSE) {
- Log.v(TAG, "Delaying " + numberEvents + " events for "
- + getActivityDebugNameLocked() + " while on state "
- + getStateAsString(mState));
- }
- return;
- }
-
- if (mId == null) {
- // Sanity check - should not happen
- Log.wtf(TAG, "null session id for " + mComponentName);
- return;
- }
-
- //TODO(b/111276913): right now we're sending sending right away (unless not ready), but
- // we should hold the events and flush later.
- try {
- if (DEBUG) {
- Log.d(TAG, "Sending " + numberEvents + " event(s) for "
- + getActivityDebugNameLocked());
- }
- mService.sendEvents(mContext.getUserId(), mId, mEvents);
- mEvents.clear();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * Used for intermediate events (i.e, other than created and destroyed).
- *
- * @hide
- */
- public void onActivityLifecycleEvent(@EventType int type) {
- if (!isContentCaptureEnabled()) return;
- if (VERBOSE) {
- Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugNameLocked()
- + ": " + ContentCaptureEvent.getTypeAsString(type));
- }
- sendEvent(new ContentCaptureEvent(type));
- }
-
- /** @hide */
- public void onActivityDestroyed() {
- if (!isContentCaptureEnabled()) return;
-
- synchronized (mLock) {
- //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
- // id) and send it to the cache of batched commands
-
- if (VERBOSE) {
- Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
- + ", mId=" + mId);
- }
-
- try {
- mService.finishSession(mContext.getUserId(), mId);
- resetStateLocked();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- @GuardedBy("mLock")
- private void resetStateLocked() {
- mState = STATE_UNKNOWN;
- mId = null;
- mApplicationToken = null;
- mComponentName = null;
- mEvents.clear();
- }
-
- /**
- * Notifies the Intelligence Service that a node has been added to the view structure.
- *
- * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
- * automatically by the Android System for views that return {@code true} on
- * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
- *
- * @param node node that has been added.
- */
- public void notifyViewAppeared(@NonNull ViewStructure node) {
- Preconditions.checkNotNull(node);
- if (!isContentCaptureEnabled()) return;
-
- if (!(node instanceof ViewNode.ViewStructureImpl)) {
- throw new IllegalArgumentException("Invalid node class: " + node.getClass());
- }
- sendEvent(new ContentCaptureEvent(TYPE_VIEW_APPEARED)
- .setViewNode(((ViewNode.ViewStructureImpl) node).mNode));
- }
-
- /**
- * Notifies the Intelligence Service that a node has been removed from the view structure.
- *
- * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
- * automatically by the Android System for standard views.
- *
- * @param id id of the node that has been removed.
- */
- public void notifyViewDisappeared(@NonNull AutofillId id) {
- Preconditions.checkNotNull(id);
- if (!isContentCaptureEnabled()) return;
-
- sendEvent(new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id));
- }
-
- /**
- * Notifies the Intelligence Service that the value of a text node has been changed.
- *
- * @param id of the node.
- * @param text new text.
- * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
- * changed by the user (for example, through the keyboard).
- */
- public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
- int flags) {
- Preconditions.checkNotNull(id);
- if (!isContentCaptureEnabled()) return;
-
- sendEvent(new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
- .setText(text));
- }
-
- /**
- * Creates a {@link ViewStructure} for a "standard" view.
- *
- * @hide
- */
- @NonNull
- public ViewStructure newViewStructure(@NonNull View view) {
- return new ViewNode.ViewStructureImpl(view);
- }
-
- /**
- * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
- * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
- *
- * @param parentId id of the virtual view parent (it can be obtained by calling
- * {@link ViewStructure#getAutofillId()} on the parent).
- * @param virtualId id of the virtual child, relative to the parent.
- *
- * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
- */
- @NonNull
- public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
- return new ViewNode.ViewStructureImpl(parentId, virtualId);
- }
-
- /**
- * Returns the component name of the {@code android.service.intelligence.IntelligenceService}
- * that is enabled for the current user.
- */
- @Nullable
- public ComponentName getIntelligenceServiceComponentName() {
- //TODO(b/111276913): implement
- return null;
- }
-
- /**
- * Checks whether content capture is enabled for this activity.
- */
- public boolean isContentCaptureEnabled() {
- //TODO(b/111276913): properly implement by checking if it was explicitly disabled by
- // service, or if service is not set
- // (and probably renamign to isEnabledLocked()
- return mService != null && mState != STATE_DISABLED;
- }
-
- /**
- * Called by apps to explicitly enable or disable content capture.
- *
- * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
- * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
- */
- public void setContentCaptureEnabled(boolean enabled) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Called by the the service {@link android.service.intelligence.IntelligenceService}
- * to define whether content capture should be enabled for activities with such
- * {@link android.content.ComponentName}.
- *
- * <p>Useful to blacklist a particular activity.
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- public void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
- boolean enabled) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Called by the the service {@link android.service.intelligence.IntelligenceService}
- * to explicitly limit content capture to the given packages and activities.
- *
- * <p>When the whitelist is set, it overrides the values passed to
- * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}
- * and {@link #setPackageContentCaptureEnabled(String, boolean)}.
- *
- * <p>To reset the whitelist, call it passing {@code null} to both arguments.
- *
- * <p>Useful when the service wants to restrict content capture to a category of apps, like
- * chat apps. For example, if the service wants to support view captures on all activities of
- * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
- * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
- * Arrays.asList(new ComponentName("ChatApp2", "act1"),
- * new ComponentName("ChatApp2", "act2")));}
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- public void setContentCaptureWhitelist(@Nullable List<String> packages,
- @Nullable List<ComponentName> activities) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Called by the the service {@link android.service.intelligence.IntelligenceService}
- * to define whether content capture should be enabled for activities of the app with such
- * {@code packageName}.
- *
- * <p>Useful to blacklist any activity from a particular app.
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- public void setPackageContentCaptureEnabled(@NonNull String packageName, boolean enabled) {
- //TODO(b/111276913): implement
- }
-
- /**
- * Gets the activities where content capture was disabled by
- * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- @NonNull
- public Set<ComponentName> getContentCaptureDisabledActivities() {
- //TODO(b/111276913): implement
- return null;
- }
-
- /**
- * Gets the apps where content capture was disabled by
- * {@link #setPackageContentCaptureEnabled(String, boolean)}.
- *
- * @throws UnsupportedOperationException if not called by the UID that owns the
- * {@link android.service.intelligence.IntelligenceService} associated with the
- * current user.
- *
- * @hide
- */
- @SystemApi
- @NonNull
- public Set<String> getContentCaptureDisabledPackages() {
- //TODO(b/111276913): implement
- return null;
- }
-
- /** @hide */
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.println("IntelligenceManager");
- final String prefix2 = prefix + " ";
- synchronized (mLock) {
- pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
- pw.print(prefix2); pw.print("mService: "); pw.println(mService);
- pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
- pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
- pw.print(prefix2); pw.print("id: "); pw.println(mId);
- pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
- pw.print(getStateAsString(mState)); pw.println(")");
- pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
- pw.print(prefix2); pw.print("component name: ");
- pw.println(mComponentName == null ? "null" : mComponentName.flattenToShortString());
- final int numberEvents = mEvents.size();
- pw.print(prefix2); pw.print("batched events: "); pw.println(numberEvents);
- if (numberEvents > 0) {
- for (int i = 0; i < numberEvents; i++) {
- final ContentCaptureEvent event = mEvents.get(i);
- pw.println(i); pw.print(": "); event.dump(pw); pw.println();
- }
-
- }
- }
- }
-
- /**
- * Gets a string that can be used to identify the activity on logging statements.
- */
- @GuardedBy("mLock")
- private String getActivityDebugNameLocked() {
- return mComponentName == null ? mContext.getPackageName()
- : mComponentName.flattenToShortString();
- }
-
- @NonNull
- private static String getStateAsString(int state) {
- switch (state) {
- case STATE_UNKNOWN:
- return "UNKNOWN";
- case STATE_WAITING_FOR_SERVER:
- return "WAITING_FOR_SERVER";
- case STATE_ACTIVE:
- return "ACTIVE";
- case STATE_DISABLED:
- return "DISABLED";
- default:
- return "INVALID:" + state;
- }
- }
-}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 9ccd321..f781802 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
import android.os.Trace;
import android.util.AttributeSet;
@@ -107,7 +108,7 @@
*/
public static final int AUTO_FIT = -1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521080)
private int mNumColumns = AUTO_FIT;
@UnsupportedAppUsage
@@ -117,7 +118,7 @@
@UnsupportedAppUsage
private int mVerticalSpacing = 0;
private int mStretchMode = STRETCH_COLUMN_WIDTH;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521079)
private int mColumnWidth;
@UnsupportedAppUsage
private int mRequestedColumnWidth;
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index f4c25c3..7d02757 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -837,7 +837,7 @@
mSurfaceSession = new SurfaceSession(parentSurface);
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setFormat(PixelFormat.TRANSLUCENT)
- .setSize(mSurfaceWidth, mSurfaceHeight)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setName("magnifier surface")
.setFlags(SurfaceControl.HIDDEN)
.build();
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index a28cc40..157992a 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -18,6 +18,8 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Px;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.Widget;
@@ -30,6 +32,7 @@
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.text.InputFilter;
import android.text.InputType;
@@ -88,6 +91,16 @@
* of the current value. Tapping on the current value allows to type in a
* desired value.
* </li>
+ * <li>
+ * If the current theme is derived from {@link android.R.style#Theme_Material}
+ * the widget presents the current value as a scrolling vertical selector with
+ * the selected value in the center and the previous and following numbers above
+ * and below, separated by a divider. The value is changed by flinging vertically.
+ * The thickness of the divider can be changed by using the
+ * {@link android.R.attr#selectionDividerHeight} attribute and the color of the
+ * divider can be changed by using the
+ * {@link android.R.attr#colorControlNormal} attribute.
+ * </li>
* </ul>
* <p>
* For an example of using this widget, see {@link android.widget.TimePicker}.
@@ -436,14 +449,14 @@
/**
* Divider for showing item to be selected while scrolling
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private final Drawable mSelectionDivider;
/**
* The height of the selection divider.
*/
- @UnsupportedAppUsage
- private final int mSelectionDividerHeight;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ private int mSelectionDividerHeight;
/**
* The current scroll state of the number picker.
@@ -1556,6 +1569,24 @@
return mSelectorIndexToStringCache.get(getValue());
}
+ /**
+ * Set the height for the divider that separates the currently selected value from the others.
+ * @param height The height to be set
+ */
+ public void setSelectionDividerHeight(@IntRange(from = 0) @Px int height) {
+ mSelectionDividerHeight = height;
+ invalidate();
+ }
+
+ /**
+ * Retrieve the height for the divider that separates the currently selected value from the
+ * others.
+ * @return The height of the divider
+ */
+ public int getSelectionDividerHeight() {
+ return mSelectionDividerHeight;
+ }
+
@Override
protected float getTopFadingEdgeStrength() {
return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c0979fe..3b916d1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -18,6 +18,8 @@
import android.annotation.ColorInt;
import android.annotation.DimenRes;
+import android.annotation.IntDef;
+import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.StyleRes;
import android.annotation.UnsupportedAppUsage;
@@ -131,6 +133,12 @@
static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
/**
+ * The intent extra that contains {@code true} if inflating as dak text theme.
+ * @hide
+ */
+ static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground";
+
+ /**
* The intent extra that contains the bounds for all shared elements.
*/
public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
@@ -161,6 +169,37 @@
private static final int LAYOUT_PARAM_ACTION_TAG = 19;
private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
+ private static final int SET_INT_TAG_TAG = 22;
+
+ /** @hide **/
+ @IntDef(flag = true, value = {
+ FLAG_REAPPLY_DISALLOWED,
+ FLAG_WIDGET_IS_COLLECTION_CHILD,
+ FLAG_USE_LIGHT_BACKGROUND_LAYOUT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApplyFlags {}
+ /**
+ * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
+ * the layout in a way that isn't recoverable, since views are being removed.
+ * @hide
+ */
+ public static final int FLAG_REAPPLY_DISALLOWED = 1;
+ /**
+ * This flag indicates whether this RemoteViews object is being created from a
+ * RemoteViewsService for use as a child of a widget collection. This flag is used
+ * to determine whether or not certain features are available, in particular,
+ * setting on click extras and setting on click pending intents. The former is enabled,
+ * and the latter disabled when this flag is true.
+ * @hide
+ */
+ public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2;
+ /**
+ * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead
+ * of {link #mLayoutId}
+ * @hide
+ */
+ public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
/**
* Application that hosts the remote views.
@@ -177,6 +216,11 @@
private final int mLayoutId;
/**
+ * The resource ID of the layout file in dark text mode. (Added to the parcel)
+ */
+ private int mLightBackgroundLayoutId = 0;
+
+ /**
* An array of actions to perform on the view tree once it has been
* inflated
*/
@@ -196,12 +240,6 @@
private boolean mIsRoot = true;
/**
- * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
- * the layout in a way that isn't recoverable, since views are being removed.
- */
- private boolean mReapplyDisallowed;
-
- /**
* Constants to whether or not this RemoteViews is composed of a landscape and portrait
* RemoteViews.
*/
@@ -217,14 +255,8 @@
@UnsupportedAppUsage
private RemoteViews mPortrait = null;
- /**
- * This flag indicates whether this RemoteViews object is being created from a
- * RemoteViewsService for use as a child of a widget collection. This flag is used
- * to determine whether or not certain features are available, in particular,
- * setting on click extras and setting on click pending intents. The former is enabled,
- * and the latter disabled when this flag is true.
- */
- private boolean mIsWidgetCollectionChild = false;
+ @ApplyFlags
+ private int mApplyFlags = 0;
/** Class cookies of the Parcel this instance was read from. */
private final Map<Class, Object> mClassCookies;
@@ -274,23 +306,29 @@
}
/**
+ * Sets an integer tag to the view.
+ *
+ * @hide
+ */
+ public void setIntTag(int viewId, int key, int tag) {
+ addAction(new SetIntTagAction(viewId, key, tag));
+ }
+
+ /**
* Set that it is disallowed to reapply another remoteview with the same layout as this view.
* This should be done if an action is destroying the view tree of the base layout.
*
* @hide
*/
- public void setReapplyDisallowed() {
- mReapplyDisallowed = true;
+ public void addFlags(@ApplyFlags int flags) {
+ mApplyFlags = mApplyFlags | flags;
}
/**
- * @return Whether it is disallowed to reapply another remoteview with the same layout as this
- * view. True if this remoteview has actions that destroyed view tree of the base layout.
- *
* @hide
*/
- public boolean isReapplyDisallowed() {
- return mReapplyDisallowed;
+ public boolean hasFlags(@ApplyFlags int flag) {
+ return (mApplyFlags & flag) == flag;
}
/**
@@ -758,7 +796,10 @@
// Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
// RemoteViewsService
AppWidgetHostView host = (AppWidgetHostView) rootParent;
- intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
+ intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
+ .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
+ hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
+
if (target instanceof AbsListView) {
AbsListView v = (AbsListView) target;
v.setRemoteViewsAdapter(intent, isAsync);
@@ -819,7 +860,7 @@
// If the view is an AdapterView, setting a PendingIntent on click doesn't make
// much sense, do they mean to set a PendingIntent template for the
// AdapterView's children?
- if (mIsWidgetCollectionChild) {
+ if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
+ "(id: " + viewId + ")");
ApplicationInfo appInfo = root.getContext().getApplicationInfo();
@@ -833,7 +874,7 @@
}
target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
} else if (mResponse.mFillIntent != null) {
- if (!mIsWidgetCollectionChild) {
+ if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
+ "only from RemoteViewsFactory (ie. on collection items).");
return;
@@ -1535,6 +1576,7 @@
viewId = parcel.readInt();
mIndex = parcel.readInt();
mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
+ mNestedViews.addFlags(mApplyFlags);
}
public void writeToParcel(Parcel dest, int flags) {
@@ -2122,6 +2164,43 @@
}
}
+ private class SetIntTagAction extends Action {
+ private final int mViewId;
+ private final int mKey;
+ private final int mTag;
+
+ SetIntTagAction(int viewId, int key, int tag) {
+ mViewId = viewId;
+ mKey = key;
+ mTag = tag;
+ }
+
+ SetIntTagAction(Parcel parcel) {
+ mViewId = parcel.readInt();
+ mKey = parcel.readInt();
+ mTag = parcel.readInt();
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mViewId);
+ dest.writeInt(mKey);
+ dest.writeInt(mTag);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ final View target = root.findViewById(mViewId);
+ if (target == null) return;
+
+ target.setTagInternal(mKey, mTag);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_INT_TAG_TAG;
+ }
+ }
+
/**
* Create a new RemoteViews object that will display the views contained
* in the specified layout file.
@@ -2143,7 +2222,7 @@
*
* @hide
*/
- public RemoteViews(String packageName, int userId, int layoutId) {
+ public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
this(getApplicationInfo(packageName, userId), layoutId);
}
@@ -2156,7 +2235,7 @@
*
* @hide
*/
- protected RemoteViews(ApplicationInfo application, int layoutId) {
+ protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
mApplication = application;
mLayoutId = layoutId;
mBitmapCache = new BitmapCache();
@@ -2182,7 +2261,8 @@
throw new RuntimeException("Both RemoteViews must share the same package and user");
}
mApplication = portrait.mApplication;
- mLayoutId = portrait.getLayoutId();
+ mLayoutId = portrait.mLayoutId;
+ mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
mLandscape = landscape;
mPortrait = portrait;
@@ -2203,8 +2283,8 @@
mApplication = src.mApplication;
mIsRoot = src.mIsRoot;
mLayoutId = src.mLayoutId;
- mIsWidgetCollectionChild = src.mIsWidgetCollectionChild;
- mReapplyDisallowed = src.mReapplyDisallowed;
+ mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
+ mApplyFlags = src.mApplyFlags;
mClassCookies = src.mClassCookies;
if (src.hasLandscapeAndPortraitLayouts()) {
@@ -2262,7 +2342,7 @@
mApplication = parcel.readInt() == 0 ? info :
ApplicationInfo.CREATOR.createFromParcel(parcel);
mLayoutId = parcel.readInt();
- mIsWidgetCollectionChild = parcel.readInt() == 1;
+ mLightBackgroundLayoutId = parcel.readInt();
readActionsFromParcel(parcel, depth);
} else {
@@ -2271,9 +2351,10 @@
mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
mClassCookies);
mApplication = mPortrait.mApplication;
- mLayoutId = mPortrait.getLayoutId();
+ mLayoutId = mPortrait.mLayoutId;
+ mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
}
- mReapplyDisallowed = parcel.readInt() == 0;
+ mApplyFlags = parcel.readInt();
}
private void readActionsFromParcel(Parcel parcel, int depth) {
@@ -2326,6 +2407,8 @@
return new OverrideTextColorsAction(parcel);
case SET_RIPPLE_DRAWABLE_COLOR_TAG:
return new SetRippleDrawableColor(parcel);
+ case SET_INT_TAG_TAG:
+ return new SetIntTagAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -2360,19 +2443,8 @@
* @return the layout id.
*/
public int getLayoutId() {
- return mLayoutId;
- }
-
- /*
- * This flag indicates whether this RemoteViews object is being created from a
- * RemoteViewsService for use as a child of a widget collection. This flag is used
- * to determine whether or not certain features are available, in particular,
- * setting on click extras and setting on click pending intents. The former is enabled,
- * and the latter disabled when this flag is true.
- */
- @UnsupportedAppUsage
- void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
- mIsWidgetCollectionChild = isWidgetCollectionChild;
+ return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
+ ? mLightBackgroundLayoutId : mLayoutId;
}
/**
@@ -3243,6 +3315,33 @@
setInt(viewId, "setLabelFor", labeledId);
}
+ /**
+ * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
+ * used by the host when the widgets displayed on a light-background where foreground elements
+ * and text can safely draw using a dark color without any additional background protection.
+ */
+ public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
+ mLightBackgroundLayoutId = layoutId;
+ }
+
+ /**
+ * If this view supports dark text versions, creates a copy representing that version,
+ * otherwise returns itself.
+ * @hide
+ */
+ public RemoteViews getDarkTextViews() {
+ if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
+ return this;
+ }
+
+ try {
+ addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+ return new RemoteViews(this);
+ } finally {
+ mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
+ }
+ }
+
private RemoteViews getRemoteViewsToApply(Context context) {
if (hasLandscapeAndPortraitLayouts()) {
int orientation = context.getResources().getConfiguration().orientation;
@@ -3603,7 +3702,7 @@
mApplication.writeToParcel(dest, flags);
}
dest.writeInt(mLayoutId);
- dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
+ dest.writeInt(mLightBackgroundLayoutId);
writeActionsToParcel(dest);
} else {
dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
@@ -3616,7 +3715,7 @@
// Both RemoteViews already share the same package and user
mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
}
- dest.writeInt(mReapplyDisallowed ? 1 : 0);
+ dest.writeInt(mApplyFlags);
}
private void writeActionsToParcel(Parcel parcel) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index d17c7c5..c5cd1a1 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -16,6 +16,9 @@
package android.widget;
+import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID;
+import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND;
+
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.app.IServiceConnection;
@@ -97,6 +100,7 @@
private final Context mContext;
private final Intent mIntent;
private final int mAppWidgetId;
+ private final boolean mOnLightBackground;
private final Executor mAsyncViewLoadExecutor;
private OnClickHandler mRemoteViewsOnClickHandler;
@@ -817,13 +821,13 @@
throw new IllegalArgumentException("Non-null Intent must be specified.");
}
- mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
+ mAppWidgetId = intent.getIntExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
mRequestedViews = new RemoteViewsFrameLayoutRefSet();
+ mOnLightBackground = intent.getBooleanExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, false);
// Strip the previously injected app widget id from service intent
- if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
- intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
- }
+ intent.removeExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID);
+ intent.removeExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND);
// Initialize the worker thread
mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
@@ -1107,6 +1111,7 @@
} else {
layout = new RemoteViewsFrameLayout(parent.getContext(), mCache);
layout.setExecutor(mAsyncViewLoadExecutor);
+ layout.setOnLightBackground(mOnLightBackground);
}
if (isInCache) {
diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java
index e490458..b80fe48 100644
--- a/core/java/android/widget/RemoteViewsListAdapter.java
+++ b/core/java/android/widget/RemoteViewsListAdapter.java
@@ -85,7 +85,7 @@
public View getView(int position, View convertView, ViewGroup parent) {
if (position < getCount()) {
RemoteViews rv = mRemoteViewsList.get(position);
- rv.setIsWidgetCollectionChild(true);
+ rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
View v;
if (convertView != null && rv != null &&
convertView.getId() == rv.getLayoutId()) {
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 2827f63..214e5cc 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -163,7 +163,7 @@
try {
rv = mFactory.getViewAt(position);
if (rv != null) {
- rv.setIsWidgetCollectionChild(true);
+ rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
}
} catch (Exception ex) {
Thread t = Thread.currentThread();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4ed9924f0d..085f8f1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -166,7 +166,7 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.intelligence.IntelligenceManager;
+import android.view.intelligence.ContentCaptureManager;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
@@ -6254,8 +6254,13 @@
return mHint;
}
- @UnsupportedAppUsage
- boolean isSingleLine() {
+ /**
+ * Returns if the text is constrained to a single horizontally scrolling line ignoring new
+ * line characters instead of letting it wrap onto multiple lines.
+ *
+ * @attr ref android.R.styleable#TextView_singleLine
+ */
+ public boolean isSingleLine() {
return mSingleLine;
}
@@ -10130,7 +10135,7 @@
}
/**
- * Notify managers (such as {@link AutofillManager} and {@link IntelligenceManager}) that are
+ * Notify managers (such as {@link AutofillManager} and {@link ContentCaptureManager}) that are
* interested on text changes.
*/
private void notifyListeningManagersAfterTextChanged() {
@@ -10150,10 +10155,10 @@
// ContentCapture
if (isImportantForContentCapture() && isTextEditable()) {
- final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
- if (im != null && im.isContentCaptureEnabled()) {
+ final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
+ if (cm != null && cm.isContentCaptureEnabled()) {
// TODO(b/111276913): pass flags when edited by user / add CTS test
- im.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
+ cm.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
}
}
}
@@ -12268,13 +12273,13 @@
}
/**
- * Returns the current {@link TextDirectionHeuristic}.
- *
- * @return the current {@link TextDirectionHeuristic}.
- * @hide
+ * Returns resolved {@link TextDirectionHeuristic} that will be used for text layout.
+ * The {@link TextDirectionHeuristic} that is used by TextView is only available after
+ * {@link #getTextDirection()} and {@link #getLayoutDirection()} is resolved. Therefore the
+ * return value may not be the same as the one TextView uses if the View's layout direction is
+ * not resolved or detached from parent root view.
*/
- @UnsupportedAppUsage
- protected TextDirectionHeuristic getTextDirectionHeuristic() {
+ public TextDirectionHeuristic getTextDirectionHeuristic() {
if (hasPasswordTransformationMethod()) {
// passwords fields should be LTR
return TextDirectionHeuristics.LTR;
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 34e8ed4..a87bbf3 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
+import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateFormat;
@@ -52,17 +53,23 @@
public static final boolean ENABLED_DEFAULT = false;
public static final boolean DETAILED_TRACKING_DEFAULT = true;
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100;
+ public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000;
+
+ private static class OverflowBinder extends Binder {}
private static final String TAG = "BinderCallsStats";
private static final int CALL_SESSIONS_POOL_SIZE = 100;
private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
+ private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class;
+ private static final int OVERFLOW_TRANSACTION_CODE = -1;
// Whether to collect all the data: cpu + exceptions + reply/request sizes.
private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
// Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
// of 100 requests.
private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
+ private int mMaxBinderCallStatsCount = MAX_BINDER_CALL_STATS_COUNT_DEFAULT;
@GuardedBy("mLock")
private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
@GuardedBy("mLock")
@@ -71,8 +78,11 @@
private final Object mLock = new Object();
private final Random mRandom;
private long mStartTime = System.currentTimeMillis();
+ private long mCallStatsCount = 0;
+ private boolean mAddDebugEntries = false;
private CachedDeviceState.Readonly mDeviceState;
+ private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
/** Injector for {@link BinderCallsStats}. */
public static class Injector {
@@ -86,7 +96,11 @@
}
public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.close();
+ }
mDeviceState = deviceState;
+ mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
}
@Override
@@ -158,7 +172,13 @@
final CallStat callStat = uidEntry.getOrCreate(
callingUid, s.binderClass, s.transactionCode,
- mDeviceState.isScreenInteractive());
+ mDeviceState.isScreenInteractive(),
+ mCallStatsCount >= mMaxBinderCallStatsCount);
+ final boolean isNewCallStat = callStat.callCount == 0;
+ if (isNewCallStat) {
+ mCallStatsCount++;
+ }
+
callStat.callCount++;
callStat.recordedCallCount++;
callStat.cpuTimeMicros += duration;
@@ -304,9 +324,30 @@
exported.methodName = methodName;
}
+ // Debug entries added to help validate the data.
+ if (mAddDebugEntries && mBatteryStopwatch != null) {
+ resultCallStats.add(createDebugEntry("start_time_millis", mStartTime));
+ resultCallStats.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+ resultCallStats.add(
+ createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
+ }
+
return resultCallStats;
}
+ private ExportedCallStat createDebugEntry(String variableName, long value) {
+ final int uid = Process.myUid();
+ final ExportedCallStat callStat = new ExportedCallStat();
+ callStat.className = "";
+ callStat.workSourceUid = uid;
+ callStat.callingUid = uid;
+ callStat.recordedCallCount = 1;
+ callStat.callCount = 1;
+ callStat.methodName = "__DEBUG_" + variableName;
+ callStat.maxReplySizeBytes = value;
+ return callStat;
+ }
+
/** @hide */
public ArrayMap<String, Integer> getExportedExceptionStats() {
synchronized (mLock) {
@@ -328,6 +369,8 @@
long totalCpuTime = 0;
pw.print("Start time: ");
pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
+ pw.print("On battery time (ms): ");
+ pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
final List<UidEntry> entries = new ArrayList<>();
@@ -444,6 +487,28 @@
}
}
+ public void setAddDebugEntries(boolean addDebugEntries) {
+ mAddDebugEntries = addDebugEntries;
+ }
+
+ /**
+ * Sets the maximum number of items to track.
+ */
+ public void setMaxBinderCallStats(int maxKeys) {
+ if (maxKeys <= 0) {
+ Slog.w(TAG, "Ignored invalid max value (value must be positive): "
+ + maxKeys);
+ return;
+ }
+
+ synchronized (mLock) {
+ if (maxKeys != mMaxBinderCallStatsCount) {
+ mMaxBinderCallStatsCount = maxKeys;
+ reset();
+ }
+ }
+ }
+
public void setSamplingInterval(int samplingInterval) {
if (samplingInterval <= 0) {
Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
@@ -461,9 +526,13 @@
public void reset() {
synchronized (mLock) {
+ mCallStatsCount = 0;
mUidEntries.clear();
mExceptionCounts.clear();
mStartTime = System.currentTimeMillis();
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.reset();
+ }
}
}
@@ -595,10 +664,21 @@
}
CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass,
- int transactionCode, boolean screenInteractive) {
+ int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) {
CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive);
- // Only create CallStat if it's a new entry, otherwise update existing instance
+ // Only create CallStat if it's a new entry, otherwise update existing instance.
if (mapCallStat == null) {
+ if (maxCallStatsReached) {
+ mapCallStat = get(callingUid, OVERFLOW_BINDER, OVERFLOW_TRANSACTION_CODE,
+ screenInteractive);
+ if (mapCallStat != null) {
+ return mapCallStat;
+ }
+
+ binderClass = OVERFLOW_BINDER;
+ transactionCode = OVERFLOW_TRANSACTION_CODE;
+ }
+
mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
screenInteractive);
CallStatKey key = new CallStatKey();
diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java
index 8c90682..334cca3 100644
--- a/core/java/com/android/internal/os/CachedDeviceState.java
+++ b/core/java/com/android/internal/os/CachedDeviceState.java
@@ -16,8 +16,14 @@
package com.android.internal.os;
+
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.ArrayList;
+
/**
* Stores the device state (e.g. charging/on battery, screen on/off) to be shared with
* the System Server telemetry services.
@@ -27,6 +33,9 @@
public class CachedDeviceState {
private volatile boolean mScreenInteractive;
private volatile boolean mCharging;
+ private final Object mStopwatchesLock = new Object();
+ @GuardedBy("mStopwatchLock")
+ private final ArrayList<TimeInStateStopwatch> mOnBatteryStopwatches = new ArrayList<>();
public CachedDeviceState() {
mCharging = true;
@@ -44,7 +53,23 @@
}
public void setCharging(boolean charging) {
- mCharging = charging;
+ if (mCharging != charging) {
+ mCharging = charging;
+ updateStopwatches(/* shouldStart= */ !charging);
+ }
+ }
+
+ private void updateStopwatches(boolean shouldStart) {
+ synchronized (mStopwatchesLock) {
+ final int size = mOnBatteryStopwatches.size();
+ for (int i = 0; i < size; i++) {
+ if (shouldStart) {
+ mOnBatteryStopwatches.get(i).start();
+ } else {
+ mOnBatteryStopwatches.get(i).stop();
+ }
+ }
+ }
}
public Readonly getReadonlyClient() {
@@ -62,5 +87,74 @@
public boolean isScreenInteractive() {
return mScreenInteractive;
}
+
+ /** Creates a {@link TimeInStateStopwatch stopwatch} that tracks the time on battery. */
+ public TimeInStateStopwatch createTimeOnBatteryStopwatch() {
+ synchronized (mStopwatchesLock) {
+ final TimeInStateStopwatch stopwatch = new TimeInStateStopwatch();
+ mOnBatteryStopwatches.add(stopwatch);
+ if (!mCharging) {
+ stopwatch.start();
+ }
+ return stopwatch;
+ }
+ }
+ }
+
+ /** Tracks the time the device spent in a given state. */
+ public class TimeInStateStopwatch implements AutoCloseable {
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private long mStartTimeMillis;
+ @GuardedBy("mLock")
+ private long mTotalTimeMillis;
+
+ /** Returns the time in state since the last call to {@link TimeInStateStopwatch#reset}. */
+ public long getMillis() {
+ synchronized (mLock) {
+ return mTotalTimeMillis + elapsedTime();
+ }
+ }
+
+ /** Resets the time in state to 0 without stopping the timer if it's started. */
+ public void reset() {
+ synchronized (mLock) {
+ mTotalTimeMillis = 0;
+ mStartTimeMillis = isRunning() ? SystemClock.elapsedRealtime() : 0;
+ }
+ }
+
+ private void start() {
+ synchronized (mLock) {
+ if (!isRunning()) {
+ mStartTimeMillis = SystemClock.elapsedRealtime();
+ }
+ }
+ }
+
+ private void stop() {
+ synchronized (mLock) {
+ if (isRunning()) {
+ mTotalTimeMillis += elapsedTime();
+ mStartTimeMillis = 0;
+ }
+ }
+ }
+
+ private long elapsedTime() {
+ return isRunning() ? SystemClock.elapsedRealtime() - mStartTimeMillis : 0;
+ }
+
+ @VisibleForTesting
+ public boolean isRunning() {
+ return mStartTimeMillis > 0;
+ }
+
+ @Override
+ public void close() {
+ synchronized (mStopwatchesLock) {
+ mOnBatteryStopwatches.remove(this);
+ }
+ }
}
}
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 826cd89..2742b7c 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -103,17 +103,9 @@
/**
* Default predicate for what UIDs to check for when getting processes. This filters to only
- * select system UIDs (1000-1999)
+ * select UID 1000 (the {@code system} user)
*/
- private static final Predicate<Integer> DEFAULT_UID_PREDICATE =
- uid -> 1000 <= uid && uid < 2000;
-
- /**
- * Do not report any threads that have a total CPU usage (across all frequencies) less than or
- * equal to this number. This significantly reduces the amount of reported threads without
- * losing any important information
- */
- private static final int TOTAL_CPU_USAGE_THRESHOLD_MILLIS = 20;
+ private static final Predicate<Integer> DEFAULT_UID_PREDICATE = uid -> uid == 1000;
/**
* Value returned when there was an error getting an integer ID value (e.g. PID, UID)
@@ -346,15 +338,6 @@
}
int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong);
- // Filter threads that have low total CPU usage
- int cpuUsageSum = 0;
- for (int i = 0; i < cpuUsages.length; i++) {
- cpuUsageSum += cpuUsages[i];
- }
- if (cpuUsageSum <= TOTAL_CPU_USAGE_THRESHOLD_MILLIS) {
- return null;
- }
-
return new ThreadCpuUsage(threadId, threadName, cpuUsages);
}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index cf2a297..de85c1f 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -49,6 +49,7 @@
private final int mEntriesSizeCap;
private int mSamplingInterval;
private CachedDeviceState.Readonly mDeviceState;
+ private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
private long mStartTime = System.currentTimeMillis();
private boolean mAddDebugEntries = false;
@@ -58,7 +59,12 @@
}
public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.close();
+ }
+
mDeviceState = deviceState;
+ mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
}
public void setAddDebugEntries(boolean addDebugEntries) {
@@ -148,9 +154,11 @@
maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
// Debug entries added to help validate the data.
- if (mAddDebugEntries) {
+ if (mAddDebugEntries && mBatteryStopwatch != null) {
exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+ exportedEntries.add(
+ createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
}
return exportedEntries;
}
@@ -168,6 +176,10 @@
return mStartTime;
}
+ public long getBatteryTimeMillis() {
+ return mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0;
+ }
+
private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) {
synchronized (specialEntry) {
if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) {
@@ -188,6 +200,9 @@
mOverflowEntry.reset();
}
mStartTime = System.currentTimeMillis();
+ if (mBatteryStopwatch != null) {
+ mBatteryStopwatch.reset();
+ }
}
public void setSamplingInterval(int samplingInterval) {
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index b9d53c1..d78bfac 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -120,6 +120,8 @@
arg5 = null;
arg6 = null;
arg7 = null;
+ arg8 = null;
+ arg9 = null;
argi1 = 0;
argi2 = 0;
argi3 = 0;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 3b7ce0a..d8ee643 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -41,6 +41,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Color;
@@ -90,6 +91,7 @@
import android.view.ViewRootImpl;
import android.view.ViewRootImpl.ActivityConfigCallback;
import android.view.Window;
+import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -1891,7 +1893,7 @@
return true;
}
// These are all the recognized media key codes in
- // KeyEvent.isMediaKey()
+ // KeyEvent.isMediaSessionKey()
case KeyEvent.KEYCODE_MEDIA_PLAY:
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -1992,7 +1994,7 @@
return true;
}
// These are all the recognized media key codes in
- // KeyEvent.isMediaKey()
+ // KeyEvent.isMediaSessionKey()
case KeyEvent.KEYCODE_MEDIA_PLAY:
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -3876,4 +3878,9 @@
mDecor.updateLogTag(params);
}
}
+
+ @Override
+ public WindowInsetsController getInsetsController() {
+ return mDecor.getWindowInsetsController();
+ }
}
diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
new file mode 100644
index 0000000..100c6ee
--- /dev/null
+++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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
+ */
+
+package com.android.internal.policy;
+
+import com.android.internal.R;
+
+import android.content.res.Resources;
+
+/**
+ * Utility functions for screen decorations used by both window manager and System UI.
+ */
+public class ScreenDecorationsUtils {
+
+ /**
+ * Corner radius that should be used on windows in order to cover the display.
+ * These values are expressed in pixels because they should not respect display or font
+ * scaling, this means that we don't have to reload them on config changes.
+ */
+ public static float getWindowCornerRadius(Resources resources) {
+ // Radius that should be used in case top or bottom aren't defined.
+ float defaultRadius = resources.getDimension(R.dimen.rounded_corner_radius);
+
+ float topRadius = resources.getDimension(R.dimen.rounded_corner_radius_top);
+ if (topRadius == 0) {
+ topRadius = defaultRadius;
+ }
+ float bottomRadius = resources.getDimension(R.dimen.rounded_corner_radius_bottom);
+ if (bottomRadius == 0) {
+ bottomRadius = defaultRadius;
+ }
+
+ // Always use the smallest radius to make sure the rounded corners will
+ // completely cover the display.
+ return Math.min(topRadius, bottomRadius);
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 604537f..53b56f2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -18,7 +18,7 @@
import android.content.ComponentName;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -29,7 +29,7 @@
{
void setIcon(String slot, in StatusBarIcon icon);
void removeIcon(String slot);
- void disable(int state1, int state2);
+ void disable(int displayId, int state1, int state2);
void animateExpandNotificationsPanel();
void animateExpandSettingsPanel(String subPanel);
void animateCollapsePanels();
@@ -38,8 +38,9 @@
void showWirelessChargingAnimation(int batteryLevel);
/**
- * Notifies the status bar of a System UI visibility flag change.
+ * Notifies System UI side of a visibility flag change on the specified display.
*
+ * @param displayId the id of the display to notify
* @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported
* separately in fullscreenStackVis and dockedStackVis
* @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack,
@@ -50,13 +51,13 @@
* @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
* @param dockedBounds the current bounds of the docked stack, in screen coordinates
*/
- void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
- in Rect fullscreenBounds, in Rect dockedBounds);
+ void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
+ int mask, in Rect fullscreenBounds, in Rect dockedBounds);
- void topAppWindowChanged(boolean menuVisible);
- void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+ void topAppWindowChanged(int displayId, boolean menuVisible);
+ void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
- void setWindowState(int window, int state);
+ void setWindowState(int display, int window, int state);
void showRecentApps(boolean triggeredFromAltTab);
void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
@@ -70,30 +71,38 @@
void toggleKeyboardShortcutsMenu(int deviceId);
/**
- * Notifies the status bar that an app transition is pending to delay applying some flags with
- * visual impact until {@link #appTransitionReady} is called.
- */
- void appTransitionPending();
-
- /**
- * Notifies the status bar that a pending app transition has been cancelled.
- */
- void appTransitionCancelled();
-
- /**
- * Notifies the status bar that an app transition is now being executed.
+ * Notifies System UI on the specified display that an app transition is pending to delay
+ * applying some flags with visual impact until {@link #appTransitionReady} is called.
*
+ * @param displayId the id of the display to notify
+ */
+ void appTransitionPending(int displayId);
+
+ /**
+ * Notifies System UI on the specified display that a pending app transition has been cancelled.
+ *
+ * @param displayId the id of the display to notify
+ */
+ void appTransitionCancelled(int displayId);
+
+ /**
+ * Notifies System UI on the specified display that an app transition is now being executed.
+ *
+ * @param displayId the id of the display to notify
* @param statusBarAnimationsStartTime the desired start time for all visual animations in the
* status bar caused by this app transition in uptime millis
* @param statusBarAnimationsDuration the duration for all visual animations in the status
* bar caused by this app transition in millis
*/
- void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
+ void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
+ long statusBarAnimationsDuration);
/**
- * Notifies the status bar that an app transition is done.
+ * Notifies System UI on the specified display that an app transition is done.
+ *
+ * @param displayId the id of the display to notify
*/
- void appTransitionFinished();
+ void appTransitionFinished(int displayId);
void showAssistDisclosure();
void startAssist(in Bundle args);
@@ -141,7 +150,7 @@
void showShutdownUi(boolean isReboot, String reason);
// Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
boolean requireConfirmation, int userId);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
@@ -151,4 +160,6 @@
void onBiometricError(String error);
// Used to hide the biometric dialog when the AuthenticationClient is stopped
void hideBiometricDialog();
+ // Used to request the "try again" button for authentications which requireConfirmation=true
+ void showBiometricTryAgain();
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 69ba070..5118e5f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -16,11 +16,12 @@
package com.android.internal.statusbar;
+import android.app.Notification;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
@@ -40,6 +41,7 @@
void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
void setIconVisibility(String slot, boolean visible);
void removeIcon(String slot);
+ // TODO(b/117478341): support back button change when IME is showing on a external display.
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);
@@ -55,7 +57,7 @@
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
void clearNotificationEffects();
void onNotificationClick(String key, in NotificationVisibility nv);
- void onNotificationActionClick(String key, int actionIndex, in NotificationVisibility nv);
+ void onNotificationActionClick(String key, int actionIndex, in Notification.Action action, in NotificationVisibility nv, boolean generatedByAssistant);
void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message, int userId);
void onClearAllNotifications(int userId);
@@ -68,7 +70,7 @@
void onNotificationSmartRepliesAdded(in String key, in int replyCount);
void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
void onNotificationSettingsViewed(String key);
- void setSystemUiVisibility(int vis, int mask, String cause);
+ void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
void onGlobalActionsShown();
void onGlobalActionsHidden();
@@ -91,7 +93,7 @@
void showPinningEscapeToast();
// Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+ void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
boolean requireConfirmation, int userId);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated();
@@ -101,4 +103,6 @@
void onBiometricError(String error);
// Used to hide the biometric dialog when the AuthenticationClient is stopped
void hideBiometricDialog();
+ // Used to request the "try again" button for authentications which requireConfirmation=true
+ void showBiometricTryAgain();
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 4b66267..f669e94 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -32,6 +32,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.IntFunction;
/**
* ArrayUtils contains some methods that you can call to find out
@@ -656,4 +657,30 @@
throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
}
}
+
+ /**
+ * Returns an array with values from {@code val} minus {@code null} values
+ *
+ * @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new}
+ */
+ public static <T> T[] filterNotNull(T[] val, IntFunction<T[]> arrayConstructor) {
+ int nullCount = 0;
+ int size = size(val);
+ for (int i = 0; i < size; i++) {
+ if (val[i] == null) {
+ nullCount++;
+ }
+ }
+ if (nullCount == 0) {
+ return val;
+ }
+ T[] result = arrayConstructor.apply(size - nullCount);
+ int outIdx = 0;
+ for (int i = 0; i < size; i++) {
+ if (val[i] != null) {
+ result[outIdx++] = val[i];
+ }
+ }
+ return result;
+ }
}
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index 17d5a2e3..6158145 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -28,7 +28,7 @@
/**
* A utility class for handling unsigned integers and unsigned arithmetics, as well as syntactic
- * sugar methods for ByteBuffer. Useful for networking and packet manipulations.
+ * sugar methods for {@link ByteBuffer}. Useful for networking and packet manipulations.
* {@hide}
*/
public final class BitUtils {
@@ -151,4 +151,11 @@
TextUtils.wrap(builder, "[", "]");
return builder.toString();
}
+
+ /**
+ * Converts long to byte array
+ */
+ public static byte[] toBytes(long l) {
+ return ByteBuffer.allocate(8).putLong(l).array();
+ }
}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 083c0c9..151901b 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -27,6 +27,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -190,6 +191,13 @@
}
/**
+ * Returns the size of the given map, or 0 if null
+ */
+ public static int size(@Nullable Map<?, ?> cur) {
+ return cur != null ? cur.size() : 0;
+ }
+
+ /**
* Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null}
*/
public static boolean isEmpty(@Nullable Collection<?> cur) {
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 5da587b..344d7ef 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -63,7 +63,7 @@
public static final int
convertValueToList(CharSequence value, String[] options, int defaultValue)
{
- if (null != value) {
+ if (!TextUtils.isEmpty(value)) {
for (int i = 0; i < options.length; i++) {
if (value.equals(options[i]))
return i;
@@ -79,8 +79,9 @@
{
boolean result = false;
- if (null == value)
+ if (TextUtils.isEmpty(value)) {
return defaultValue;
+ }
if (value.equals("1")
|| value.equals("true")
@@ -94,8 +95,9 @@
public static final int
convertValueToInt(CharSequence charSeq, int defaultValue)
{
- if (null == charSeq)
+ if (TextUtils.isEmpty(charSeq)) {
return defaultValue;
+ }
String nm = charSeq.toString();
@@ -138,7 +140,7 @@
}
public static int convertValueToUnsignedInt(String value, int defaultValue) {
- if (null == value) {
+ if (TextUtils.isEmpty(value)) {
return defaultValue;
}
@@ -1674,7 +1676,7 @@
public static boolean readBooleanAttribute(XmlPullParser in, String name,
boolean defaultValue) {
final String value = in.getAttributeValue(null, name);
- if (value == null) {
+ if (TextUtils.isEmpty(value)) {
return defaultValue;
} else {
return Boolean.parseBoolean(value);
@@ -1711,7 +1713,7 @@
public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
final String value = in.getAttributeValue(null, name);
- if (value != null) {
+ if (!TextUtils.isEmpty(value)) {
return Base64.decode(value, Base64.DEFAULT);
} else {
return null;
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaConsumer.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/NonaConsumer.java
index 27d25b8..3e7ce2b 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaConsumer.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Consumer}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface NonaConsumer<A, B, C, D, E, F, G, H, I> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaFunction.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/NonaFunction.java
index 27d25b8..560b4f1 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaFunction.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Function}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface NonaFunction<A, B, C, D, E, F, G, H, I, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaPredicate.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/NonaPredicate.java
index 27d25b8..c1e6f37 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaPredicate.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Predicate}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface NonaPredicate<A, B, C, D, E, F, G, H, I> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctConsumer.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctConsumer.java
index 27d25b8..83ee305 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctConsumer.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Consumer}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface OctConsumer<A, B, C, D, E, F, G, H> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctFunction.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctFunction.java
index 27d25b8..cb16624 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctFunction.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Function}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface OctFunction<A, B, C, D, E, F, G, H, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctPredicate.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctPredicate.java
index 27d25b8..7f36d6ac 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctPredicate.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Predicate}
+ *
* @hide
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public interface OctPredicate<A, B, C, D, E, F, G, H> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h);
}
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
index 4ffe441..d74e715 100755
--- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java
+++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
@@ -22,6 +22,10 @@
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintConsumer;
@@ -39,61 +43,62 @@
*
* @hide
*/
-abstract class OmniFunction<A, B, C, D, E, F, G, R> implements
+abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements
PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>,
QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>,
HexFunction<A, B, C, D, E, F, R>, HeptFunction<A, B, C, D, E, F, G, R>,
+ OctFunction<A, B, C, D, E, F, G, H, R>, NonaFunction<A, B, C, D, E, F, G, H, I, R>,
PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>,
QuintConsumer<A, B, C, D, E>, HexConsumer<A, B, C, D, E, F>,
- HeptConsumer<A, B, C, D, E, F, G>,
- PooledPredicate<A>, BiPredicate<A, B>,
+ HeptConsumer<A, B, C, D, E, F, G>, OctConsumer<A, B, C, D, E, F, G, H>,
+ NonaConsumer<A, B, C, D, E, F, G, H, I>, PooledPredicate<A>, BiPredicate<A, B>,
PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>,
PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble {
- abstract R invoke(A a, B b, C c, D d, E e, F f, G g);
+ abstract R invoke(A a, B b, C c, D d, E e, F f, G g, H h, I i);
@Override
public R apply(A o, B o2) {
- return invoke(o, o2, null, null, null, null, null);
+ return invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public R apply(A o) {
- return invoke(o, null, null, null, null, null, null);
+ return invoke(o, null, null, null, null, null, null, null, null);
}
- public abstract <V> OmniFunction<A, B, C, D, E, F, G, V> andThen(
+ public abstract <V> OmniFunction<A, B, C, D, E, F, G, H, I, V> andThen(
Function<? super R, ? extends V> after);
- public abstract OmniFunction<A, B, C, D, E, F, G, R> negate();
+ public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> negate();
@Override
public void accept(A o, B o2) {
- invoke(o, o2, null, null, null, null, null);
+ invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public void accept(A o) {
- invoke(o, null, null, null, null, null, null);
+ invoke(o, null, null, null, null, null, null, null, null);
}
@Override
public void run() {
- invoke(null, null, null, null, null, null, null);
+ invoke(null, null, null, null, null, null, null, null, null);
}
@Override
public R get() {
- return invoke(null, null, null, null, null, null, null);
+ return invoke(null, null, null, null, null, null, null, null, null);
}
@Override
public boolean test(A o, B o2) {
- return (Boolean) invoke(o, o2, null, null, null, null, null);
+ return (Boolean) invoke(o, o2, null, null, null, null, null, null, null);
}
@Override
public boolean test(A o) {
- return (Boolean) invoke(o, null, null, null, null, null, null);
+ return (Boolean) invoke(o, null, null, null, null, null, null, null, null);
}
@Override
@@ -108,52 +113,72 @@
@Override
public R apply(A a, B b, C c) {
- return invoke(a, b, c, null, null, null, null);
+ return invoke(a, b, c, null, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c) {
- invoke(a, b, c, null, null, null, null);
+ invoke(a, b, c, null, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d) {
- return invoke(a, b, c, d, null, null, null);
+ return invoke(a, b, c, d, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e) {
- return invoke(a, b, c, d, e, null, null);
+ return invoke(a, b, c, d, e, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f) {
- return invoke(a, b, c, d, e, f, null);
+ return invoke(a, b, c, d, e, f, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f, G g) {
- return invoke(a, b, c, d, e, f, g);
+ return invoke(a, b, c, d, e, f, g, null, null);
+ }
+
+ @Override
+ public R apply(A a, B b, C c, D d, E e, F f, G g, H h) {
+ return invoke(a, b, c, d, e, f, g, h, null);
+ }
+
+ @Override
+ public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+ return invoke(a, b, c, d, e, f, g, h, i);
}
@Override
public void accept(A a, B b, C c, D d) {
- invoke(a, b, c, d, null, null, null);
+ invoke(a, b, c, d, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e) {
- invoke(a, b, c, d, e, null, null);
+ invoke(a, b, c, d, e, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f) {
- invoke(a, b, c, d, e, f, null);
+ invoke(a, b, c, d, e, f, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f, G g) {
- invoke(a, b, c, d, e, f, g);
+ invoke(a, b, c, d, e, f, g, null, null);
+ }
+
+ @Override
+ public void accept(A a, B b, C c, D d, E e, F f, G g, H h) {
+ invoke(a, b, c, d, e, f, g, h, null);
+ }
+
+ @Override
+ public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+ invoke(a, b, c, d, e, f, g, h, i);
}
@Override
@@ -167,5 +192,5 @@
}
@Override
- public abstract OmniFunction<A, B, C, D, E, F, G, R> recycleOnUse();
+ public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> recycleOnUse();
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index af3c752..c00932e 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -25,6 +25,10 @@
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintConsumer;
@@ -176,7 +180,8 @@
Consumer<? super A> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -192,7 +197,8 @@
Predicate<? super A> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -208,7 +214,8 @@
Function<? super A, ? extends R> function,
A arg1) {
return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
+ null);
}
/**
@@ -238,7 +245,8 @@
A arg1) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null);
+ function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -257,7 +265,8 @@
BiConsumer<? super A, ? super B> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -274,7 +283,8 @@
BiPredicate<? super A, ? super B> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -291,7 +301,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -308,7 +319,8 @@
BiConsumer<? super A, ? super B> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -325,7 +337,8 @@
BiPredicate<? super A, ? super B> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -342,7 +355,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -359,7 +373,8 @@
BiConsumer<? super A, ? super B> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -376,7 +391,8 @@
BiPredicate<? super A, ? super B> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -393,7 +409,8 @@
BiFunction<? super A, ? super B, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+ function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+ null);
}
/**
@@ -424,7 +441,8 @@
A arg1, B arg2) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+ function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -444,7 +462,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -462,7 +481,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -480,7 +500,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -498,7 +519,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -516,7 +538,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -534,7 +557,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -552,7 +576,8 @@
TriConsumer<? super A, ? super B, ? super C> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -570,7 +595,8 @@
TriFunction<? super A, ? super B, ? super C, ? extends R> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+ null);
}
/**
@@ -602,7 +628,8 @@
A arg1, B arg2, C arg3) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+ function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -623,7 +650,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -642,7 +670,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -661,7 +690,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -680,7 +710,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -699,7 +730,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -718,7 +750,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -737,7 +770,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -756,7 +790,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -775,7 +810,8 @@
QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -794,7 +830,8 @@
QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
}
/**
@@ -827,7 +864,8 @@
A arg1, B arg2, C arg3, D arg4) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+ function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -849,7 +887,8 @@
QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function,
A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
- function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
}
/**
@@ -869,7 +908,8 @@
QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R>
function, A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
- function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
}
/**
@@ -904,7 +944,8 @@
A arg1, B arg2, C arg3, D arg4, E arg5) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null);
+ function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -927,7 +968,8 @@
HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function,
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
- function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
}
/**
@@ -948,7 +990,8 @@
HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
- function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
}
/**
@@ -984,7 +1027,8 @@
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null);
+ function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+ null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1008,7 +1052,8 @@
HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
- function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
}
/**
@@ -1031,7 +1076,8 @@
? super G, ? extends R> function,
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
- function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
}
/**
@@ -1068,7 +1114,195 @@
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
- function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+ null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
+ */
+ static <A, B, C, D, E, F, G, H> PooledRunnable obtainRunnable(
+ OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G,
+ ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7,
+ H arg8) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
+ */
+ static <A, B, C, D, E, F, G, H, R> PooledSupplier<R> obtainSupplier(
+ OctFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8) } when handled
+ */
+ static <A, B, C, D, E, F, G, H> Message obtainMessage(
+ OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G,
+ ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7,
+ H arg8) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
+ */
+ static <A, B, C, D, E, F, G, H, I> PooledRunnable obtainRunnable(
+ NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4,
+ E arg5, F arg6, G arg7, H arg8, I arg9) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
+ */
+ static <A, B, C, D, E, F, G, H, I, R> PooledSupplier<R> obtainSupplier(
+ NonaFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8, arg9) } when handled
+ */
+ static <A, B, C, D, E, F, G, H, I> Message obtainMessage(
+ NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4,
+ E arg5, F arg6, G arg7, H arg8, I arg9) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index eea1e5f..6be626a 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -30,6 +30,12 @@
import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.HexPredicate;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.NonaPredicate;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.OctPredicate;
import com.android.internal.util.function.QuadConsumer;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuadPredicate;
@@ -54,12 +60,12 @@
* @hide
*/
final class PooledLambdaImpl<R> extends OmniFunction<Object,
- Object, Object, Object, Object, Object, Object, R> {
+ Object, Object, Object, Object, Object, Object, Object, Object, R> {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "PooledLambdaImpl";
- private static final int MAX_ARGS = 7;
+ private static final int MAX_ARGS = 9;
private static final int MAX_POOL_SIZE = 50;
@@ -125,7 +131,7 @@
/**
* Bit schema:
- * AAAAAAABCDEEEEEEFFFFFF
+ * AAAAAAAAABCDEEEEEEFFFFFF
*
* Where:
* A - whether {@link #mArgs arg} at corresponding index was specified at
@@ -161,17 +167,19 @@
}
@Override
- R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
+ R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7,
+ Object a8, Object a9) {
checkNotRecycled();
if (DEBUG) {
Log.i(LOG_TAG, this + ".invoke("
+ commaSeparateFirstN(
- new Object[] { a1, a2, a3, a4, a5, a6, a7 },
+ new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9 },
LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS)))
+ ")");
}
- final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3)
- && fillInArg(a4) && fillInArg(a5) && fillInArg(a6) && fillInArg(a7);
+ final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4)
+ && fillInArg(a5) && fillInArg(a6) && fillInArg(a7) && fillInArg(a8)
+ && fillInArg(a9);
int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE));
if (argCount != LambdaType.MASK_ARG_COUNT) {
for (int i = 0; i < argCount; i++) {
@@ -335,7 +343,7 @@
popArg(2), popArg(3), popArg(4), popArg(5));
}
}
- }
+ } break;
case 7: {
switch (returnType) {
@@ -356,7 +364,49 @@
popArg(5), popArg(6));
}
}
- }
+ } break;
+
+ case 8: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((OctConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((OctPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3),
+ popArg(4), popArg(5), popArg(6), popArg(7));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((OctFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7));
+ }
+ }
+ } break;
+
+ case 9: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((NonaConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((NonaPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7), popArg(8));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((NonaFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8));
+ }
+ }
+ } break;
}
throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
}
@@ -419,8 +469,8 @@
* Internal non-typesafe factory method for {@link PooledLambdaImpl}
*/
static <E extends PooledLambda> E acquire(Pool pool, Object func,
- int fNumArgs, int numPlaceholders, int fReturnType,
- Object a, Object b, Object c, Object d, Object e, Object f, Object g) {
+ int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
+ Object d, Object e, Object f, Object g, Object h, Object i) {
PooledLambdaImpl r = acquire(pool);
if (DEBUG) {
Log.i(LOG_TAG,
@@ -436,6 +486,8 @@
+ ", e = " + e
+ ", f = " + f
+ ", g = " + g
+ + ", h = " + h
+ + ", i = " + i
+ ")");
}
r.mFunc = func;
@@ -449,6 +501,8 @@
setIfInBounds(r.mArgs, 4, e);
setIfInBounds(r.mArgs, 5, f);
setIfInBounds(r.mArgs, 6, g);
+ setIfInBounds(r.mArgs, 7, h);
+ setIfInBounds(r.mArgs, 8, i);
return (E) r;
}
@@ -474,13 +528,14 @@
}
@Override
- public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> negate() {
+ public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ R> negate() {
throw new UnsupportedOperationException();
}
@Override
- public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, V> andThen(
- Function<? super R, ? extends V> after) {
+ public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ V> andThen(Function<? super R, ? extends V> after) {
throw new UnsupportedOperationException();
}
@@ -500,7 +555,8 @@
}
@Override
- public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> recycleOnUse() {
+ public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+ R> recycleOnUse() {
if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()");
mFlags |= FLAG_RECYCLE_ON_USE;
return this;
@@ -584,6 +640,8 @@
case 5: return "Quint";
case 6: return "Hex";
case 7: return "Hept";
+ case 8: return "Oct";
+ case 9: return "Nona";
default: throw new IllegalArgumentException("" + argCount);
}
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 137ca7f..c8834a8 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -27,6 +27,8 @@
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.PointerIcon;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import com.android.internal.os.IResultReceiver;
@@ -53,6 +55,15 @@
}
@Override
+ public void insetsChanged(InsetsState insetsState) {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) throws RemoteException {
+ }
+
+ @Override
public void moved(int newX, int newY) {
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 7635a72..b7e656b 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -814,7 +814,14 @@
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.alwaysShow && child.getVisibility() != GONE) {
- measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+ if (lp.maxHeight != -1) {
+ final int remainingHeight = heightSize - heightUsed;
+ measureChildWithMargins(child, widthSpec, widthPadding,
+ MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
+ lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
+ } else {
+ measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+ }
heightUsed += child.getMeasuredHeight();
}
}
@@ -824,9 +831,17 @@
// And now the rest.
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.alwaysShow && child.getVisibility() != GONE) {
- measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+ if (lp.maxHeight != -1) {
+ final int remainingHeight = heightSize - heightUsed;
+ measureChildWithMargins(child, widthSpec, widthPadding,
+ MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
+ lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
+ } else {
+ measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+ }
heightUsed += child.getMeasuredHeight();
}
}
@@ -938,6 +953,7 @@
public boolean alwaysShow;
public boolean ignoreOffset;
public boolean hasNestedScrollIndicator;
+ public int maxHeight;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
@@ -953,6 +969,8 @@
hasNestedScrollIndicator = a.getBoolean(
R.styleable.ResolverDrawerLayout_LayoutParams_layout_hasNestedScrollIndicator,
false);
+ maxHeight = a.getDimensionPixelSize(
+ R.styleable.ResolverDrawerLayout_LayoutParams_layout_maxHeight, -1);
a.recycle();
}
@@ -965,6 +983,7 @@
this.alwaysShow = source.alwaysShow;
this.ignoreOffset = source.ignoreOffset;
this.hasNestedScrollIndicator = source.hasNestedScrollIndicator;
+ this.maxHeight = source.maxHeight;
}
public LayoutParams(MarginLayoutParams source) {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 8495850..b97a9fa 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -934,7 +934,7 @@
// If the storage model feature flag is disabled, we need to fiddle
// around with permission definitions to return us to pre-Q behavior.
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) ||
newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) ||
newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index bdd5f83..31bb1d5 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -34,7 +34,6 @@
],
cppflags: ["-Wno-conversion-null"],
- cpp_std: "c++17",
srcs: [
"AndroidRuntime.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index eada690..f9879cc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1056,12 +1056,18 @@
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
- LOG_FATAL("No root directory specified, and /android does not exist.");
+ LOG_FATAL("No root directory specified, and /system does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
+ const char* runtimeRootDir = getenv("ANDROID_RUNTIME_ROOT");
+ if (runtimeRootDir == NULL) {
+ LOG_FATAL("No runtime directory specified with ANDROID_RUNTIME_ROOT environment variable.");
+ return;
+ }
+
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
@@ -1450,8 +1456,8 @@
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
- REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioSystem),
+ REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioTrack),
REG_JNI(register_android_media_JetPlayer),
REG_JNI(register_android_media_MicrophoneInfo),
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index dcb7874..cfe742d 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -59,7 +59,7 @@
static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
{
ALOG_ASSERT(codepoint <= 0xFFFF);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint->setTextEncoding(kGlyphID_SkTextEncoding);
SkScalar skWidth;
SkRect skBounds;
@@ -84,7 +84,7 @@
{
HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
SkPaint* paint = hbFontData->m_paint;
- paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
+ paint->setTextEncoding(kUTF32_SkTextEncoding);
if (unicode > 0x10ffff) {
unicode = 0xfffd;
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index a8b0640..c249e20 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -71,7 +71,7 @@
static void defaultSettingsForAndroid(Paint* paint) {
// GlyphID encoding is required because we are using Harfbuzz shaping
- paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
+ paint->setTextEncoding(kGlyphID_SkTextEncoding);
}
namespace PaintGlue {
@@ -321,7 +321,7 @@
x += MinikinUtils::xOffsetForTextAlign(paint, layout);
Paint::Align align = paint->getTextAlign();
paint->setTextAlign(Paint::kLeft_Align);
- paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
+ paint->setTextEncoding(kGlyphID_SkTextEncoding);
GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
MinikinUtils::forFontRun(layout, paint, f);
paint->setTextAlign(align);
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 68f5bef..ed6a84b 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -64,11 +64,10 @@
jint tileModeX, jint tileModeY) {
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
sk_sp<SkImage> image;
- sk_sp<SkColorFilter> colorFilter;
if (jbitmap) {
// Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
// we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
- image = android::bitmap::toBitmap(env, jbitmap).makeImage(&colorFilter);
+ image = android::bitmap::toBitmap(env, jbitmap).makeImage();
}
if (!image.get()) {
@@ -81,9 +80,6 @@
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
}
- if(colorFilter) {
- shader = shader->makeWithColorFilter(colorFilter);
- }
ThrowIAE_IfNull(env, shader.get());
return reinterpret_cast<jlong>(shader.release());
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index e02741f..719cf74 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -514,14 +514,14 @@
sp<ANativeWindow> anw;
if ((anw = getNativeWindow(env, surface)) == NULL) {
- jniThrowException(env, "java/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
"Could not retrieve native window from surface.");
return BAD_VALUE;
}
int32_t usage = 0;
status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
if(err != NO_ERROR) {
- jniThrowException(env, "java/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
"Error while querying surface usage bits");
OVERRIDE_SURFACE_ERROR(err);
return err;
@@ -542,7 +542,7 @@
status_t err = native_window_api_disconnect(anw.get(), NATIVE_WINDOW_API_CAMERA);
if(err != NO_ERROR) {
- jniThrowException(env, "java/lang/UnsupportedOperationException;",
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
"Error while disconnecting surface");
OVERRIDE_SURFACE_ERROR(err);
return err;
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 05f6556..e74aafe 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -40,6 +40,7 @@
jfieldID deviceWidth;
jfieldID deviceHeight;
jfieldID uniqueId;
+ jfieldID physicalPort;
jfieldID type;
} gDisplayViewportClassInfo;
@@ -54,6 +55,9 @@
status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject viewportObj,
DisplayViewport* viewport) {
+ static const jclass byteClass = FindClassOrDie(env, "java/lang/Byte");
+ static const jmethodID byteValue = env->GetMethodID(byteClass, "byteValue", "()B");
+
viewport->displayId = env->GetIntField(viewportObj, gDisplayViewportClassInfo.displayId);
viewport->orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
viewport->deviceWidth = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceWidth);
@@ -65,6 +69,12 @@
viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str();
}
+ viewport->physicalPort = std::nullopt;
+ jobject physicalPort = env->GetObjectField(viewportObj, gDisplayViewportClassInfo.physicalPort);
+ if (physicalPort != nullptr) {
+ viewport->physicalPort = std::make_optional(env->CallByteMethod(physicalPort, byteValue));
+ }
+
viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj,
gDisplayViewportClassInfo.type));
@@ -112,6 +122,9 @@
gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env,
gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;");
+ gDisplayViewportClassInfo.physicalPort = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "physicalPort", "Ljava/lang/Byte;");
+
gDisplayViewportClassInfo.type = GetFieldIDOrDie(env,
gDisplayViewportClassInfo.clazz, "type", "I");
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 5887fa7..10005dd 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -22,6 +22,7 @@
#include <utils/threads.h>
#include "android_hardware_input_InputApplicationHandle.h"
+#include "android_util_Binder.h"
namespace android {
@@ -29,6 +30,7 @@
jfieldID ptr;
jfieldID name;
jfieldID dispatchingTimeoutNanos;
+ jfieldID token;
} gInputApplicationHandleClassInfo;
static Mutex gHandleMutex;
@@ -75,6 +77,15 @@
mInfo->dispatchingTimeout = env->GetLongField(obj,
gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
+ jobject tokenObj = env->GetObjectField(obj,
+ gInputApplicationHandleClassInfo.token);
+ if (tokenObj) {
+ mInfo->token = ibinderForJavaObject(env, tokenObj);
+ env->DeleteLocalRef(tokenObj);
+ } else {
+ mInfo->token.clear();
+ }
+
env->DeleteLocalRef(obj);
return true;
}
@@ -153,6 +164,9 @@
clazz,
"dispatchingTimeoutNanos", "J");
+ GET_FIELD_ID(gInputApplicationHandleClassInfo.token, clazz,
+ "token", "Landroid/os/IBinder;");
+
return 0;
}
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 6ecb5de..76920f5 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -21,19 +21,19 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
-#include <android_view_InputChannel.h>
#include <android/graphics/Region.h>
#include <ui/Region.h>
#include "android_hardware_input_InputWindowHandle.h"
#include "android_hardware_input_InputApplicationHandle.h"
+#include "android_util_Binder.h"
namespace android {
static struct {
jfieldID ptr;
jfieldID inputApplicationHandle;
- jfieldID inputChannel;
+ jfieldID token;
jfieldID name;
jfieldID layoutParamsFlags;
jfieldID layoutParamsType;
@@ -42,6 +42,7 @@
jfieldID frameTop;
jfieldID frameRight;
jfieldID frameBottom;
+ jfieldID surfaceInset;
jfieldID scaleFactor;
jfieldID touchableRegion;
jfieldID visible;
@@ -61,9 +62,7 @@
// --- NativeInputWindowHandle ---
-NativeInputWindowHandle::NativeInputWindowHandle(
- const sp<InputApplicationHandle>& inputApplicationHandle, jweak objWeak) :
- InputWindowHandle(inputApplicationHandle),
+NativeInputWindowHandle::NativeInputWindowHandle(jweak objWeak) :
mObjWeak(objWeak) {
}
@@ -86,13 +85,12 @@
mInfo.touchableRegion.clear();
- jobject inputChannelObj = env->GetObjectField(obj,
- gInputWindowHandleClassInfo.inputChannel);
- if (inputChannelObj) {
- mInfo.inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
- env->DeleteLocalRef(inputChannelObj);
+ jobject tokenObj = env->GetObjectField(obj,
+ gInputWindowHandleClassInfo.token);
+ if (tokenObj) {
+ mInfo.token = ibinderForJavaObject(env, tokenObj);
} else {
- mInfo.inputChannel.clear();
+ mInfo.token.clear();
}
jstring nameObj = jstring(env->GetObjectField(obj,
@@ -120,7 +118,9 @@
gInputWindowHandleClassInfo.frameRight);
mInfo.frameBottom = env->GetIntField(obj,
gInputWindowHandleClassInfo.frameBottom);
- mInfo.scaleFactor = env->GetFloatField(obj,
+ mInfo.surfaceInset = env->GetIntField(obj,
+ gInputWindowHandleClassInfo.surfaceInset);
+ mInfo.globalScaleFactor = env->GetFloatField(obj,
gInputWindowHandleClassInfo.scaleFactor);
jobject regionObj = env->GetObjectField(obj,
@@ -155,6 +155,18 @@
mInfo.displayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.displayId);
+ jobject inputApplicationHandleObj = env->GetObjectField(obj,
+ gInputWindowHandleClassInfo.inputApplicationHandle);
+ if (inputApplicationHandleObj) {
+ sp<InputApplicationHandle> inputApplicationHandle =
+ android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
+ if (inputApplicationHandle != nullptr) {
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *(inputApplicationHandle->getInfo());
+ }
+ env->DeleteLocalRef(inputApplicationHandleObj);
+ }
+
env->DeleteLocalRef(obj);
return true;
}
@@ -175,14 +187,8 @@
if (ptr) {
handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
} else {
- jobject inputApplicationHandleObj = env->GetObjectField(inputWindowHandleObj,
- gInputWindowHandleClassInfo.inputApplicationHandle);
- sp<InputApplicationHandle> inputApplicationHandle =
- android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
- env->DeleteLocalRef(inputApplicationHandleObj);
-
jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
- handle = new NativeInputWindowHandle(inputApplicationHandle, objWeak);
+ handle = new NativeInputWindowHandle(objWeak);
handle->incStrong((void*)android_server_InputWindowHandle_getHandle);
env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
reinterpret_cast<jlong>(handle));
@@ -236,8 +242,8 @@
clazz,
"inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
- GET_FIELD_ID(gInputWindowHandleClassInfo.inputChannel, clazz,
- "inputChannel", "Landroid/view/InputChannel;");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.token, clazz,
+ "token", "Landroid/os/IBinder;");
GET_FIELD_ID(gInputWindowHandleClassInfo.name, clazz,
"name", "Ljava/lang/String;");
@@ -263,6 +269,9 @@
GET_FIELD_ID(gInputWindowHandleClassInfo.frameBottom, clazz,
"frameBottom", "I");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.surfaceInset, clazz,
+ "surfaceInset", "I");
+
GET_FIELD_ID(gInputWindowHandleClassInfo.scaleFactor, clazz,
"scaleFactor", "F");
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index 2be267e..54b89f5 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -26,8 +26,7 @@
class NativeInputWindowHandle : public InputWindowHandle {
public:
- NativeInputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
- jweak objWeak);
+ NativeInputWindowHandle(jweak objWeak);
virtual ~NativeInputWindowHandle();
jobject getInputWindowHandleObjLocalRef(JNIEnv* env);
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 1ea4ed1..12a8343b4 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -842,6 +842,18 @@
}
// ----------------------------------------------------------------------------
+static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) {
+ sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+ if (lpRecorder == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioRecord pointer for getId()");
+ return (jint)AUDIO_PORT_HANDLE_NONE;
+ }
+ return (jint)lpRecorder->getPortId();
+}
+
+
+// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
@@ -883,6 +895,7 @@
(void *)android_media_AudioRecord_get_timestamp},
{"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
(void *)android_media_AudioRecord_get_active_microphones},
+ {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
};
// field names found in android/media/AudioRecord.java
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7410b52..adab8e2 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -175,6 +175,17 @@
jmethodID postRecordConfigEventFromNative;
} gAudioPolicyEventHandlerMethods;
+//
+// JNI Initialization for OpenSLES routing
+//
+jmethodID gMidAudioTrackRoutingProxy_ctor;
+jmethodID gMidAudioTrackRoutingProxy_release;
+jmethodID gMidAudioRecordRoutingProxy_ctor;
+jmethodID gMidAudioRecordRoutingProxy_release;
+
+jclass gClsAudioTrackRoutingProxy;
+jclass gClsAudioRecordRoutingProxy;
+
static Mutex gLock;
enum AudioError {
@@ -2017,6 +2028,39 @@
return (jint)nativeToJavaStatus(status);
}
+static jint android_media_AudioSystem_get_FCC_8(JNIEnv *env, jobject thiz) {
+ return FCC_8;
+}
+
+static jint
+android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid)
+{
+ status_t status = AudioSystem::setAssistantUid(uid);
+ return (jint)nativeToJavaStatus(status);
+}
+
+static jint
+android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) {
+ std::vector<uid_t> nativeUidsVector;
+
+ if (uids != nullptr) {
+ jsize len = env->GetArrayLength(uids);
+
+ if (len > 0) {
+ int *nativeUids = nullptr;
+ nativeUids = env->GetIntArrayElements(uids, 0);
+ if (nativeUids != nullptr) {
+ for (size_t i = 0; i < len; i++) {
+ nativeUidsVector.push_back(nativeUids[i]);
+ }
+ env->ReleaseIntArrayElements(uids, nativeUids, 0);
+ }
+ }
+ }
+ status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector);
+ return (jint)nativeToJavaStatus(status);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -2077,9 +2121,10 @@
{"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
{"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats},
{"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
+ {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
+ {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
};
-
static const JNINativeMethod gEventHandlerMethods[] = {
{"native_setup",
"(Ljava/lang/Object;)V",
@@ -2089,8 +2134,15 @@
(void *)android_media_AudioSystem_eventHandlerFinalize},
};
+static const JNINativeMethod gGetFCC8Methods[] = {
+ {"native_get_FCC_8", "()I", (void *)android_media_AudioSystem_get_FCC_8},
+};
+
int register_android_media_AudioSystem(JNIEnv *env)
{
+ // This needs to be done before hooking up methods AudioTrackRoutingProxy (below)
+ RegisterMethodsOrDie(env, kClassPathName, gGetFCC8Methods, NELEM(gGetFCC8Methods));
+
jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
@@ -2247,6 +2299,28 @@
gAudioAttributesFields.mFormattedTags = GetFieldIDOrDie(env,
audioAttributesClass, "mFormattedTags", "Ljava/lang/String;");
+ // AudioTrackRoutingProxy methods
+ gClsAudioTrackRoutingProxy =
+ android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy");
+ // make sure this reference doesn't get deleted
+ gClsAudioTrackRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioTrackRoutingProxy);
+
+ gMidAudioTrackRoutingProxy_ctor =
+ android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "<init>", "(J)V");
+ gMidAudioTrackRoutingProxy_release =
+ android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "native_release", "()V");
+
+ // AudioRecordRoutingProxy
+ gClsAudioRecordRoutingProxy =
+ android::FindClassOrDie(env, "android/media/AudioRecordRoutingProxy");
+ // make sure this reference doesn't get deleted
+ gClsAudioRecordRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioRecordRoutingProxy);
+
+ gMidAudioRecordRoutingProxy_ctor =
+ android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "<init>", "(J)V");
+ gMidAudioRecordRoutingProxy_release =
+ android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V");
+
AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index bf22dd2..d927972 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1226,10 +1226,6 @@
pJniStorage->mDeviceCallback.clear();
}
-static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
- return FCC_8;
-}
-
// Pass through the arguments to the AudioFlinger track implementation.
static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
jobject jconfig, jobject joperation) {
@@ -1288,6 +1284,17 @@
}
// ----------------------------------------------------------------------------
+static jint android_media_AudioTrack_get_port_id(JNIEnv *env, jobject thiz) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return (jint)AUDIO_PORT_HANDLE_NONE;
+ }
+ return (jint)lpTrack->getPortId();
+}
+
+// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
// name, signature, funcPtr
@@ -1351,7 +1358,6 @@
{"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
{"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
{"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
- {"native_get_FCC_8", "()I", (void *)android_media_AudioTrack_get_FCC_8},
{"native_applyVolumeShaper",
"(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
(void *)android_media_AudioTrack_apply_volume_shaper},
@@ -1359,6 +1365,7 @@
"(I)Landroid/media/VolumeShaper$State;",
(void *)android_media_AudioTrack_get_volume_shaper_state},
{"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
+ {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
};
@@ -1384,7 +1391,6 @@
}
}
-
// ----------------------------------------------------------------------------
int register_android_media_AudioTrack(JNIEnv *env)
{
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 49d5007..fa1da4b 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -33,8 +33,6 @@
#include <iomanip>
#include <string>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
#include <debuggerd/client.h>
#include <log/log.h>
#include <utils/misc.h>
@@ -50,10 +48,6 @@
namespace android
{
-static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
- return UniqueFile(fopen(path, mode), safeFclose);
-}
-
enum {
HEAP_UNKNOWN,
HEAP_DALVIK,
diff --git a/core/jni/android_os_Debug.h b/core/jni/android_os_Debug.h
index 81270ca..c7b731b 100644
--- a/core/jni/android_os_Debug.h
+++ b/core/jni/android_os_Debug.h
@@ -19,6 +19,8 @@
#include <memory>
#include <stdio.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
namespace android {
@@ -27,6 +29,11 @@
}
using UniqueFile = std::unique_ptr<FILE, decltype(&safeFclose)>;
+
+inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
+ return UniqueFile(fopen(path, mode), safeFclose);
+}
+
UniqueFile OpenSmapsOrRollup(int pid);
} // namespace android
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 8885aac..c6ea4e1 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -25,9 +25,10 @@
#include "unicode/uchar.h"
#define PROPERTY_UNDEFINED (-1)
+#define JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY 18
// ICU => JDK mapping
-static int directionality_map[U_CHAR_DIRECTION_COUNT] = {
+static int directionality_map[JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY + 1] = {
0, // U_LEFT_TO_RIGHT (0) => DIRECTIONALITY_LEFT_TO_RIGHT (0)
1, // U_RIGHT_TO_LEFT (1) => DIRECTIONALITY_RIGHT_TO_LEFT (1)
3, // U_EUROPEAN_NUMBER (2) => DIRECTIONALITY_EUROPEAN_NUMBER (3)
@@ -75,7 +76,8 @@
int c = 0x00010000 + ((src[i] - 0xD800) << 10) +
(src[i + 1] & 0x3FF);
int dir = u_charDirection(c);
- if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
+ if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY
+ || u_charType(c) == U_UNASSIGNED)
dir = PROPERTY_UNDEFINED;
else
dir = directionality_map[dir];
@@ -85,7 +87,8 @@
} else {
int c = src[i];
int dir = u_charDirection(c);
- if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
+ if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY
+ || u_charType(c) == U_UNASSIGNED)
dest[i] = PROPERTY_UNDEFINED;
else
dest[i] = directionality_map[dir];
@@ -96,7 +99,7 @@
static jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input)
{
int width = u_getIntPropertyValue(input, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH))
width = PROPERTY_UNDEFINED;
return width;
@@ -121,6 +124,7 @@
return;
}
+ int maxWidth = u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH);
for (int i = 0; i < count; i++) {
const int srci = start + i;
if (src[srci] >= 0xD800 && src[srci] <= 0xDBFF &&
@@ -129,7 +133,7 @@
int c = 0x00010000 + ((src[srci] - 0xD800) << 10) +
(src[srci + 1] & 0x3FF);
int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > maxWidth)
width = PROPERTY_UNDEFINED;
dest[i++] = width;
@@ -137,7 +141,7 @@
} else {
int c = src[srci];
int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
- if (width < 0 || width >= U_EA_COUNT)
+ if (width < 0 || width > maxWidth)
width = PROPERTY_UNDEFINED;
dest[i] = width;
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 4c7defb..377e65c 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1128,6 +1128,39 @@
return pss * 1024;
}
+static jlongArray android_os_Process_getRss(JNIEnv* env, jobject clazz, jint pid)
+{
+ // total, file, anon, swap
+ jlong rss[4] = {0, 0, 0, 0};
+ std::string status_path =
+ android::base::StringPrintf("/proc/%d/status", pid);
+ UniqueFile file = MakeUniqueFile(status_path.c_str(), "re");
+
+ char line[256];
+ while (fgets(line, sizeof(line), file.get())) {
+ jlong v;
+ if ( sscanf(line, "VmRSS: %" SCNd64 " kB", &v) == 1) {
+ rss[0] = v;
+ } else if ( sscanf(line, "RssFile: %" SCNd64 " kB", &v) == 1) {
+ rss[1] = v;
+ } else if ( sscanf(line, "RssAnon: %" SCNd64 " kB", &v) == 1) {
+ rss[2] = v;
+ } else if ( sscanf(line, "VmSwap: %" SCNd64 " kB", &v) == 1) {
+ rss[3] = v;
+ }
+ }
+
+ jlongArray rssArray = env->NewLongArray(4);
+ if (rssArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ env->SetLongArrayRegion(rssArray, 0, 4, rss);
+
+ return rssArray;
+}
+
jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz,
jobjectArray commandNames)
{
@@ -1253,6 +1286,7 @@
{"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine},
{"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime},
{"getPss", "(I)J", (void*)android_os_Process_getPss},
+ {"getRss", "(I)[J", (void*)android_os_Process_getRss},
{"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands},
//{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject},
{"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index e89b593..752624b 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -468,6 +468,10 @@
return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark();
}
+static jlong android_view_RenderNode_getUniqueId(jlong renderNodePtr) {
+ return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId();
+}
+
// ----------------------------------------------------------------------------
// RenderProperties - Animations
// ----------------------------------------------------------------------------
@@ -694,6 +698,7 @@
{ "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight },
{ "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
{ "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark },
+ { "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId },
};
int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ec9c860..ea6e017 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -377,6 +377,14 @@
transaction->setCrop_legacy(ctrl, crop);
}
+static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jfloat cornerRadius) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ transaction->setCornerRadius(ctrl, cornerRadius);
+}
+
static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint layerStack) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -883,6 +891,8 @@
(void*)nativeSetFlags },
{"nativeSetWindowCrop", "(JJIIII)V",
(void*)nativeSetWindowCrop },
+ {"nativeSetCornerRadius", "(JJF)V",
+ (void*)nativeSetCornerRadius },
{"nativeSetLayerStack", "(JJI)V",
(void*)nativeSetLayerStack },
{"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;",
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 7de8020..4c1fc5c 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -550,6 +550,10 @@
}
optional MultiSim multi_sim = 76;
+ // Whether we've enabled native flags health check on this device. Takes effect on
+ // reboot. The value "1" enables native flags health check; otherwise it's disabled.
+ optional SettingProto native_flags_health_check_enabled = 144 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
message Netstats {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -757,6 +761,13 @@
}
optional SmartSelection smart_selection = 108;
+ message SmartSuggestions {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto service_explicitly_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional SmartSuggestions smart_suggestions = 145;
+
message Sms {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -865,6 +876,8 @@
// Temperature at which the high temperature warning notification should
// be shown.
optional SettingProto warning_temperature_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // USB temperature at which the high temperature alarm notification should be shown.
+ optional SettingProto usb_alarm_temperature_level = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional TemperatureWarning temperature_warning = 119;
@@ -987,5 +1000,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 144;
+ // Next tag = 146;
}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6e661e1..0e052fe 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -234,6 +234,18 @@
}
optional Location location = 31;
+ // How frequently will the user be reminded about location permission grants
+ message LocationAccessCheck {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ // Time in between periodic checks
+ optional SettingProto interval_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ // Time in between the user granting a location permission and a check
+ optional SettingProto delay_millis = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional LocationAccessCheck location_access_check = 73;
+
message LockScreen {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -515,5 +527,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 73;
+ // Next tag = 74;
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 99f096d..3767ed5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -156,7 +156,7 @@
optional int32 rotation = 11;
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
optional DisplayFramesProto display_frames = 13;
- optional int32 surface_size = 14;
+ optional int32 surface_size = 14 [deprecated=true];
optional string focused_app = 15;
optional AppTransitionProto app_transition = 16;
}
@@ -211,7 +211,7 @@
repeated AppWindowTokenProto app_window_tokens = 3;
optional bool fills_parent = 4;
optional .android.graphics.RectProto bounds = 5;
- optional .android.graphics.RectProto temp_inset_bounds = 6;
+ optional .android.graphics.RectProto displayed_bounds = 6;
optional bool defer_removal = 7;
optional int32 surface_width = 8;
optional int32 surface_height = 9;
diff --git a/core/proto/android/service/runtime.proto b/core/proto/android/service/runtime.proto
new file mode 100644
index 0000000..ecbccef
--- /dev/null
+++ b/core/proto/android/service/runtime.proto
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.service.runtime;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "RuntimeServiceProto";
+
+// Represents dumpsys info from RuntimeService.
+message RuntimeServiceInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Generic debug information to include.
+ repeated DebugEntryProto debug_entry = 1;
+}
+
+// A piece of key / value debug information.
+message DebugEntryProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string key = 1;
+
+ optional string string_value = 2;
+}
diff --git a/core/proto/android/stats/devicepolicy/Android.bp b/core/proto/android/stats/devicepolicy/Android.bp
new file mode 100644
index 0000000..6ae54e2
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+java_library_static {
+ name: "devicepolicyprotosnano",
+ proto: {
+ type: "nano",
+ },
+ srcs: [
+ "*.proto",
+ ],
+ java_version: "1.8",
+ target: {
+ android: {
+ jarjar_rules: "jarjar-rules.txt",
+ },
+ host: {
+ static_libs: ["libprotobuf-java-nano"],
+ }
+ },
+ no_framework_libs: true,
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/proto/android/stats/devicepolicy/device_policy.proto
similarity index 72%
rename from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
rename to core/proto/android/stats/devicepolicy/device_policy.proto
index 27d25b8..af30cf3 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/proto/android/stats/devicepolicy/device_policy.proto
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+syntax = "proto2";
+
+package android.stats.devicepolicy;
+option java_multiple_files = true;
+
+message StringList {
+ repeated string string_value = 1;
}
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
new file mode 100644
index 0000000..8fbea12
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.stats.devicepolicy;
+option java_multiple_files = true;
+
+/**
+ * Id for device policy features.
+ */
+enum EventId {
+ SET_PASSWORD_QUALITY = 1;
+ SET_PASSWORD_MINIMUM_LENGTH = 2;
+ SET_PASSWORD_MINIMUM_NUMERIC = 3;
+ SET_PASSWORD_MINIMUM_NON_LETTER = 4;
+ SET_PASSWORD_MINIMUM_LETTERS = 5;
+ SET_PASSWORD_MINIMUM_LOWER_CASE = 6;
+ SET_PASSWORD_MINIMUM_UPPER_CASE = 7;
+ SET_PASSWORD_MINIMUM_SYMBOLS = 8;
+ SET_KEYGUARD_DISABLED_FEATURES = 9;
+ LOCK_NOW = 10;
+ WIPE_DATA_WITH_REASON = 11;
+ ADD_USER_RESTRICTION = 12;
+ REMOVE_USER_RESTRICTION = 13;
+ SET_SECURE_SETTING = 14;
+ SET_SECURITY_LOGGING_ENABLED = 15;
+ RETRIEVE_SECURITY_LOGS = 16;
+ RETRIEVE_PRE_REBOOT_SECURITY_LOGS = 17;
+ SET_PERMISSION_POLICY = 18;
+ SET_PERMISSION_GRANT_STATE = 19;
+ INSTALL_KEY_PAIR = 20;
+ INSTALL_CA_CERT = 21;
+ ON_CHOOSE_KEY_ALIAS = 22;
+ REMOVE_KEY_PAIR = 23;
+ UNINSTALL_CA_CERTS = 24;
+ SET_CERT_INSTALLER_PACKAGE = 25;
+ SET_ALWAYS_ON_VPN_PACKAGE = 26;
+ SET_PERMITTED_INPUT_METHODS = 27;
+ SET_PERMITTED_ACCESSIBILITY_SERVICES = 28;
+ SET_SCREEN_CAPTURE_DISABLE = 29;
+ SET_CAMERA_DISABLED = 30;
+ QUERY_SUMMARY_FOR_USER = 31;
+ QUERY_SUMMARY = 32;
+ QUERY_DETAILS = 33;
+ REBOOT = 34;
+ SET_MASTER_VOLUME_MUTED = 35;
+ SET_AUTO_TIME_REQUIRED = 36;
+ SET_KEYGUARD_DISABLED = 37;
+ SET_STATUS_BAR_DISABLED = 38;
+ SET_ORGANIZATION_COLOR = 39;
+ SET_PROFILE_NAME = 40;
+ SET_USER_ICON = 41;
+ SET_DEVICE_OWNER_LOCKSCREEN_INFO = 42;
+ SET_SHORT_SUPPORT_MESSAGE = 43;
+ SET_LONG_SUPPORT_MESSAGE = 44;
+ SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED = 45;
+ SET_CROSS_PROFILE_CALLER_DISABLED = 46;
+ SET_BLUETOOTH_CONTACT_SHARING_DISABLED = 47;
+ ADD_CROSS_PROFILE_INTENT_FILTER = 48;
+ ADD_CROSS_PROFILE_WIDGET_PROVIDER = 49;
+ SET_SYSTEM_UPDATE_POLICY = 50;
+ SET_LOCKTASK_PACKAGES = 51;
+ ADD_PERSISTENT_PREFERRED_ACTIVITY = 52;
+ REQUEST_BUGREPORT = 53;
+ GET_WIFI_MAC_ADDRESS = 54;
+ REQUEST_QUIET_MODE_ENABLED = 55;
+ WORK_PROFILE_LOCATION_CHANGED = 56;
+ DO_USER_INFO_CLICKED = 57;
+ TRANSFER_OWNERSHIP = 58;
+ GENERATE_KEY_PAIR = 59;
+ SET_KEY_PAIR_CERTIFICATE = 60;
+ SET_KEEP_UNINSTALLED_PACKAGES = 61;
+ SET_APPLICATION_RESTRICTIONS = 62;
+ SET_APPLICATION_HIDDEN = 63;
+ ENABLE_SYSTEM_APP = 64;
+ ENABLE_SYSTEM_APP_WITH_INTENT = 65;
+ INSTALL_EXISTING_PACKAGE = 66;
+ SET_UNINSTALL_BLOCKED = 67;
+ SET_PACKAGES_SUSPENDED = 68;
+ ON_LOCK_TASK_MODE_ENTERING = 69;
+ ADD_CROSS_PROFILE_CALENDAR_PACKAGE = 70;
+ REMOVE_CROSS_PROFILE_CALENDAR_PACKAGE = 71;
+ GET_USER_PASSWORD_COMPLEXITY_LEVEL = 72;
+ INSTALL_SYSTEM_UPDATE = 73;
+ INSTALL_SYSTEM_UPDATE_ERROR = 74;
+ IS_MANAGED_KIOSK = 75;
+ IS_UNATTENDED_MANAGED_KIOSK = 76;
+ PROVISIONING_TO_COMP = 77;
+ PROVISIONING_FORCED_DO = 78;
+
+ // existing Tron logs to be migrated to WestWorld
+ PROVISIONING_ENTRY_POINT_NFC = 79;
+ PROVISIONING_ENTRY_POINT_QR_CODE = 80;
+ PROVISIONING_ENTRY_POINT_ZERO_TOUCH = 81;
+ PROVISIONING_ENTRY_POINT_ADB = 82;
+ PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE = 83;
+ PROVISIONING_DPC_PACKAGE_NAME = 84;
+ PROVISIONING_DPC_INSTALLED_BY_PACKAGE = 85;
+ PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS = 86;
+ PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87;
+ PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88;
+ PROVISIONING_WEB_ACTIVITY_TIME_MS = 89;
+ PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90;
+ PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91;
+ PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92;
+ PROVISIONING_NETWORK_TYPE = 93;
+ PROVISIONING_ACTION = 94;
+ PROVISIONING_EXTRAS = 95;
+ PROVISIONING_COPY_ACCOUNT_TASK_MS = 96;
+ PROVISIONING_CREATE_PROFILE_TASK_MS = 97;
+ PROVISIONING_START_PROFILE_TASK_MS = 98;
+ PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS = 99;
+ PROVISIONING_INSTALL_PACKAGE_TASK_MS = 100;
+ PROVISIONING_CANCELLED = 101;
+ PROVISIONING_ERROR = 102;
+ PROVISIONING_COPY_ACCOUNT_STATUS = 103;
+ PROVISIONING_TOTAL_TASK_TIME_MS = 104;
+ PROVISIONING_SESSION_STARTED = 105;
+ PROVISIONING_SESSION_COMPLETED = 106;
+ PROVISIONING_TERMS_ACTIVITY_TIME_MS = 107;
+ PROVISIONING_TERMS_COUNT = 108;
+ PROVISIONING_TERMS_READ = 109;
+}
diff --git a/core/proto/android/stats/devicepolicy/jarjar-rules.txt b/core/proto/android/stats/devicepolicy/jarjar-rules.txt
new file mode 100644
index 0000000..40043a86
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index fba2e51..4777169 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -51,6 +51,7 @@
NETWORK_TYPE_TD_SCDMA = 17;
NETWORK_TYPE_IWLAN = 18;
NETWORK_TYPE_LTE_CA = 19;
+ NETWORK_TYPE_NR = 20;
}
// Signal strength levels, primarily used by android/telephony/SignalStrength.java.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index eacdfa3..3018614 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -620,11 +620,20 @@
<protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
+ <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
<eat-comment />
+ <!-- Grouping for platform runtime permissions is not accessible to apps
+ @hide
+ @SystemApi
+ -->
+ <permission-group android:name="android.permission-group.UNDEFINED"
+ android:priority="100" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing user's contacts including personal profile -->
<!-- ====================================================================== -->
@@ -643,14 +652,17 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readContacts"
android:description="@string/permdesc_readContacts"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write the user's contacts data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeContacts"
android:description="@string/permdesc_writeContacts"
android:protectionLevel="dangerous" />
@@ -672,14 +684,17 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCalendar"
android:description="@string/permdesc_readCalendar"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write the user's calendar data.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeCalendar"
android:description="@string/permdesc_writeCalendar"
android:protectionLevel="dangerous" />
@@ -701,6 +716,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.SEND_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sendSms"
android:description="@string/permdesc_sendSms"
android:permissionFlags="costsMoney"
@@ -710,33 +726,41 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveSms"
android:description="@string/permdesc_receiveSms"
- android:protectionLevel="dangerous"/>
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to read SMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readSms"
android:description="@string/permdesc_readSms"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to receive WAP push messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_WAP_PUSH"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveWapPush"
android:description="@string/permdesc_receiveWapPush"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to monitor incoming MMS messages.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECEIVE_MMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_receiveMms"
android:description="@string/permdesc_receiveMms"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
@@ -751,9 +775,11 @@
<p>Protection level: dangerous
@hide Pending API council approval -->
<permission android:name="android.permission.READ_CELL_BROADCASTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCellBroadcasts"
android:description="@string/permdesc_readCellBroadcasts"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing external storage -->
@@ -792,6 +818,7 @@
@deprecated replaced by new strongly-typed permission groups in Q.
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardRead"
android:description="@string/permdesc_sdcardRead"
android:protectionLevel="dangerous"
@@ -813,6 +840,7 @@
@deprecated replaced by new strongly-typed permission groups in Q.
-->
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardWrite"
android:description="@string/permdesc_sdcardWrite"
android:protectionLevel="dangerous"
@@ -829,9 +857,11 @@
<!-- Allows an application to read the user's shared audio collection. -->
<permission android:name="android.permission.READ_MEDIA_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_audioRead"
android:description="@string/permdesc_audioRead"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Runtime permission controlling access to the user's shared visual media
collection, including images and videos. -->
@@ -844,22 +874,28 @@
<!-- Allows an application to read the user's shared images collection. -->
<permission android:name="android.permission.READ_MEDIA_IMAGES"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_imagesRead"
android:description="@string/permdesc_imagesRead"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to read the user's shared video collection. -->
<permission android:name="android.permission.READ_MEDIA_VIDEO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_videoRead"
android:description="@string/permdesc_videoRead"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to access any geographic locations persisted in the
user's shared collection. -->
<permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_mediaLocation"
android:description="@string/permdesc_mediaLocation"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- @hide @SystemApi
Allows an application to modify OBB files visible to other apps. -->
@@ -887,20 +923,24 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_FINE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an app to access approximate location.
Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessCoarseLocation"
android:description="@string/permdesc_accessCoarseLocation"
android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an app to access location in the background. If you
are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}.
@@ -909,9 +949,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_accessBackgroundLocation"
android:description="@string/permdesc_accessBackgroundLocation"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the call log -->
@@ -949,9 +991,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readCallLog"
android:description="@string/permdesc_readCallLog"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an application to write (but not read) the user's
call log data.
@@ -967,6 +1011,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.WRITE_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_writeCallLog"
android:description="@string/permdesc_writeCallLog"
android:protectionLevel="dangerous" />
@@ -977,9 +1022,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_processOutgoingCalls"
android:description="@string/permdesc_processOutgoingCalls"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device telephony -->
@@ -1008,23 +1055,28 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_PHONE_STATE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneState"
android:description="@string/permdesc_readPhoneState"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
<p>Protection level: dangerous-->
<permission android:name="android.permission.READ_PHONE_NUMBERS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneNumbers"
android:description="@string/permdesc_readPhoneNumbers"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- Allows an application to initiate a phone call without going through
the Dialer user interface for the user to confirm the call.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CALL_PHONE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:permissionFlags="costsMoney"
android:label="@string/permlab_callPhone"
android:description="@string/permdesc_callPhone"
@@ -1034,6 +1086,7 @@
<p>Protection level: dangerous
-->
<permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_addVoicemail"
android:description="@string/permdesc_addVoicemail"
android:protectionLevel="dangerous" />
@@ -1042,6 +1095,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.USE_SIP"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:description="@string/permdesc_use_sip"
android:label="@string/permlab_use_sip"
android:protectionLevel="dangerous"/>
@@ -1050,6 +1104,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ANSWER_PHONE_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_answerPhoneCalls"
android:description="@string/permdesc_answerPhoneCalls"
android:protectionLevel="dangerous|runtime" />
@@ -1077,6 +1132,7 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCEPT_HANDOVER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android.label="@string/permlab_acceptHandover"
android:description="@string/permdesc_acceptHandovers"
android:protectionLevel="dangerous" />
@@ -1100,9 +1156,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.RECORD_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_recordAudio"
android:description="@string/permdesc_recordAudio"
- android:protectionLevel="dangerous|instant"/>
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for activity recognition -->
@@ -1121,9 +1179,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACTIVITY_RECOGNITION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_activityRecognition"
android:description="@string/permdesc_activityRecognition"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the UCE Service -->
@@ -1169,9 +1229,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CAMERA"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_camera"
android:description="@string/permdesc_camera"
- android:protectionLevel="dangerous|instant" />
+ android:protectionLevel="dangerous|instant"
+ android:usageInfoRequired="true" />
<!-- ====================================================================== -->
@@ -1192,9 +1254,11 @@
measure what is happening inside his/her body, such as heart rate.
<p>Protection level: dangerous -->
<permission android:name="android.permission.BODY_SENSORS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_bodySensors"
android:description="@string/permdesc_bodySensors"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="dangerous"
+ android:usageInfoRequired="true" />
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
@@ -1678,9 +1742,11 @@
<p>Protection level: dangerous
-->
<permission android:name="android.permission.GET_ACCOUNTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:protectionLevel="dangerous"
android:description="@string/permdesc_getAccounts"
- android:label="@string/permlab_getAccounts" />
+ android:label="@string/permlab_getAccounts"
+ android:usageInfoRequired="true" />
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- @SystemApi Allows applications to call into AccountAuthenticators.
@@ -2981,12 +3047,12 @@
<permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by a android.service.intelligence.IntelligenceService,
+ <!-- Must be required by a android.service.intelligence.SmartSuggestionsService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
<p>Protection level: signature
-->
- <permission android:name="android.permission.BIND_INTELLIGENCE_SERVICE"
+ <permission android:name="android.permission.BIND_SMART_SUGGESTIONS_SERVICE"
android:protectionLevel="signature" />
<!-- Must be required by hotword enrollment application,
@@ -4111,6 +4177,11 @@
<permission android:name="android.permission.MANAGE_AUTO_FILL"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an application to manage the smart suggestions service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_SMART_SUGGESTIONS"
+ android:protectionLevel="signature" />
+
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@hide <p>Not for use by third-party applications.</p> -->
@@ -4211,6 +4282,28 @@
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
android:protectionLevel="signature|appop" />
+ <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use
+ {@link android.app.Notification.Builder#setFullScreenIntent notification full screen
+ intents}. -->
+ <permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+ android:protectionLevel="normal" />
+
+ <!-- @SystemApi Allows requesting the framework broadcast the
+ {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent.
+ @hide -->
+ <permission android:name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Permission that protects the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY}
+ intent.
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
+ android:protectionLevel="signature|preinstalled" />
+ <!-- @SystemApi Allows wallpaper to be rendered in ambient mode.
+ @hide -->
+ <permission android:name="android.permission.AMBIENT_WALLPAPER"
+ android:protectionLevel="signature|preinstalled" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
@@ -4530,6 +4623,13 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.WallpaperUpdateReceiver"
+ android:permission="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY">
+ <intent-filter>
+ <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+ </intent-filter>
+ </receiver>
+
<service android:name="android.hardware.location.GeofenceHardwareService"
android:permission="android.permission.LOCATION_HARDWARE"
android:exported="false" />
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 433ae39..4bf1ad6 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -126,7 +126,6 @@
android:visibility="gone"
android:contentDescription="@string/notification_alerted_content_description"
android:src="@drawable/ic_notifications_alerted"
- android:tint="@color/notification_secondary_text_color_light"
/>
<ImageView
android:id="@+id/profile_badge"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6c4861b..f55e48e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1915,6 +1915,9 @@
<enum name="KEYCODE_SYSTEM_NAVIGATION_RIGHT" value="283" />
<enum name="KEYCODE_ALL_APPS" value="284" />
<enum name="KEYCODE_REFRESH" value="285" />
+ <enum name="KEYCODE_THUMBS_UP" value="286" />
+ <enum name="KEYCODE_THUMBS_DOWN" value="287" />
+ <enum name="KEYCODE_PROFILE_SWITCH" value="288" />
</attr>
<!-- ***************************************************************** -->
@@ -5532,7 +5535,7 @@
<attr name="solidColor" format="color|reference" />
<!-- @hide The divider for making the selection area. -->
<attr name="selectionDivider" format="reference" />
- <!-- @hide The height of the selection divider. -->
+ <!-- The height of the selection divider. -->
<attr name="selectionDividerHeight" format="dimension" />
<!-- @hide The distance between the two selection dividers. -->
<attr name="selectionDividersDistance" format="dimension" />
@@ -7935,7 +7938,9 @@
wallpaper. -->
<attr name="showMetadataInPreview" format="boolean" />
- <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. -->
+ <!-- Wallpapers optimized and capable of drawing in ambient mode will return true.
+ This feature requires the android.permission.AMBIENT_WALLPAPER permission.
+ @hide @SystemApi -->
<attr name="supportsAmbientMode" format="boolean" />
<!-- Uri that specifies a settings Slice for this wallpaper. -->
@@ -8803,6 +8808,7 @@
<attr name="layout_ignoreOffset" format="boolean" />
<attr name="layout_gravity" />
<attr name="layout_hasNestedScrollIndicator" format="boolean" />
+ <attr name="layout_maxHeight" />
</declare-styleable>
<!-- @hide -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6fc0f5b..089c59f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1601,6 +1601,10 @@
<attr name="request" />
<attr name="protectionLevel" />
<attr name="permissionFlags" />
+ <!-- If {@code true} applications that target Q <em>must</em> specify the permission usage
+ attributes in their {@code uses-permission} elements or the permission will not be
+ granted. -->
+ <attr name="usageInfoRequired" format="boolean" />
</declare-styleable>
<!-- The <code>permission-group</code> tag declares a logical grouping of
@@ -1700,6 +1704,81 @@
requested. If it does support the feature, it will be as if the manifest didn't
request it at all. -->
<attr name="requiredNotFeature" format="string" />
+
+ <!-- Specify if the app uploads data, or derived data, guarded by this permission.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataSentOffDevice">
+ <!-- The application may send data, or derived data, guarded by this permission off of the
+ device. -->
+ <enum name="yes" value="1" />
+ <!-- The application may send data, or derived data, guarded by this permission off of the
+ device, however it will only do so when explicitly triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application does not send data, or derived data, guarded by this permission off
+ of the device. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify if the application or its related off-device services provide data,
+ or derived data, guarded by this permission to third parties outside of the developer's
+ organization that do not qualify as data processors.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataSharedWithThirdParty">
+ <!-- The application or its services may provide data, or derived data, guarded by this
+ permission to third party organizations. -->
+ <enum name="yes" value="1" />
+ <!-- The application or its services may provide data, or derived data, guarded by this
+ permission to third party organizations, however it will only do so when explicitly
+ triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application or its services does not provide data, or derived data, guarded by
+ this permission to third party organizations. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify if the application or its related off-device services use data,
+ or derived data, guarded by this permission for monetization purposes.
+
+ For example, if the data is sold to another party or used for targeting advertisements
+ this must be set to {@code yes}.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataUsedForMonetization">
+ <!-- The application or its services may use data, or derived data, guarded by this
+ permission for monetization purposes. -->
+ <enum name="yes" value="1" />
+ <!-- The application or its services may use data, or derived data, guarded by this
+ permission for monetization purposes, however it will only do so when explicity
+ triggered by a user action. -->
+ <enum name="userTriggered" value="2" />
+ <!-- The application or its services does not use data, or derived data, guarded by
+ this permission for monetization purposes. -->
+ <enum name="no" value="3" />
+ </attr>
+
+ <!-- Specify how long the application or its related off-device services store
+ data, or derived data, guarded by this permission.
+
+ This can be one of "notRetained", "userSelected", "unlimited", or a number
+ representing the number of weeks the data is retained.
+
+ If the permission is defined with {@link android.R.attr#usageInfoRequired}
+ {@code true} this <em>must</em> be specified by apps that target Android Q or the
+ permission will not be granted, it will be as if the manifest didn't request it at all.
+ -->
+ <attr name="dataRetentionTime" format="string" />
+
</declare-styleable>
<!-- The <code>uses-configuration</code> tag specifies
@@ -2148,6 +2227,21 @@
<attr name="visibleToInstantApps" />
<!-- The code for this component is located in the given split. -->
<attr name="splitName" />
+ <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service
+ will be spawned from an Application Zygote, instead of the regular Zygote.
+ <p>
+ The Application Zygote will pre-initialize the application's class loader,
+ and call a static callback into the application to allow it to perform
+ application-specific preloads (such as loading a shared library). Therefore,
+ spawning from the Application Zygote will typically reduce the service
+ launch time and reduce its memory usage. The downside of using this flag
+ is that you will have an additional process (the app zygote itself) that
+ is taking up memory. Whether actual memory usage is improved therefore strongly
+ depends on the number of isolated services that an application starts,
+ and how much memory those services save by preloading. Therefore, it is
+ recommended to measure memory usage under typical workloads to determine
+ whether it makes sense to use this flag. -->
+ <attr name="useAppZygote" format="boolean" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1d80961..c62071b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2125,6 +2125,9 @@
during initialization when the setting is still null. -->
<bool name="config_dozeAlwaysOnEnabled">true</bool>
+ <!-- If AOD can show an ambient version of the wallpaper -->
+ <bool name="config_dozeSupportsAodWallpaper">true</bool>
+
<!-- Whether the display blanks itself when transitioning from a doze to a non-doze state -->
<bool name="config_displayBlanksAfterDoze">false</bool>
@@ -3347,6 +3350,14 @@
-->
<string name="config_defaultTextClassifierPackage" translatable="false"></string>
+ <!-- The package name for the system's smart suggestion service.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ If no service with the specified name exists on the device, content capture and
+ smart suggestions will be disabled.
+ Example: "com.android.intelligence/.SmartSuggestionsService"
+ -->
+ <string name="config_defaultSmartSuggestionsService" translatable="false"></string>
+
<!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
<bool name="config_useDefaultFocusHighlight">true</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index f7b9961..05a156b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -430,7 +430,7 @@
<dimen name="notification_badge_size">12dp</dimen>
<!-- Size of the alerted icon for notifications -->
- <dimen name="notification_alerted_size">18dp</dimen>
+ <dimen name="notification_alerted_size">12dp</dimen>
<!-- Keyguard dimensions -->
<!-- TEMP -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 64e5bc0..bbe3ff9 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -190,4 +190,7 @@
<!-- A tag used to save the view added to a transition overlay -->
<item type="id" name="transition_overlay_view_tag" />
+
+ <!-- A tag used to save the notification action object -->
+ <item type="id" name="notification_action_index_tag" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3373d14..5e8af62 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2909,6 +2909,7 @@
<public name="opticalInsetRight" />
<public name="opticalInsetBottom" />
<public name="forceDarkAllowed" />
+ <!-- @hide @SystemApi -->
<public name="supportsAmbientMode" />
<!-- @hide For use by platform and tools only. Developers should not specify this value. -->
<public name="usesNonSdkApi" />
@@ -2921,6 +2922,13 @@
<public name="interactiveUiTimeout" />
<public name="importantForContentCapture" />
<public name="supportsMultipleDisplays" />
+ <public name="useAppZygote" />
+ <public name="usageInfoRequired" />
+ <public name="dataSentOffDevice" />
+ <public name="dataSharedWithThirdParty" />
+ <public name="dataUsedForMonetization" />
+ <public name="dataRetentionTime" />
+ <public name="selectionDividerHeight" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index bd6d976..a33f6b2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1444,9 +1444,14 @@
<!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] -->
<string name="biometric_dialog_default_title">Application <xliff:g id="app" example="Gmail">%s</xliff:g> wants to authenticate.</string>
-
<!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
<string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
+ <!-- Message shown when biometric authentication was canceled by the user [CHAR LIMIT=50] -->
+ <string name="biometric_error_user_canceled">Authentication canceled</string>
+ <!-- Message shown by the biometric dialog when biometric is not recognized -->
+ <string name="biometric_not_recognized">Not recognized</string>
+ <!-- Message shown when biometric authentication has been canceled [CHAR LIMIT=50] -->
+ <string name="biometric_error_canceled">Authentication canceled</string>
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
<string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
@@ -1462,8 +1467,6 @@
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <!-- Message shown by the biometric dialog when biometric is not recognized -->
- <string name="biometric_not_recognized">Not recognized</string>
<!-- Accessibility message announced when a fingerprint has been authenticated [CHAR LIMIT=NONE] -->
<string name="fingerprint_authenticated">Fingerprint authenticated</string>
<!-- Accessibility message announced when a face has been authenticated [CHAR LIMIT=NONE] -->
@@ -1585,14 +1588,14 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
<string name="permlab_sdcardRead">read the contents of your shared storage</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
<string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
<string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
<string name="permdesc_sdcardWrite">Allows the app to write the contents of your shared storage.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2983,7 +2986,7 @@
<!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] -->
<string name="translate">Translate</string>
-
+
<!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] -->
<string name="translate_desc">Translate selected text</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9264f90..6854a84e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2406,7 +2406,9 @@
<!-- Biometric messages -->
<java-symbol type="string" name="biometric_dialog_default_title" />
<java-symbol type="string" name="biometric_error_hw_unavailable" />
+ <java-symbol type="string" name="biometric_error_user_canceled" />
<java-symbol type="string" name="biometric_not_recognized" />
+ <java-symbol type="string" name="biometric_error_canceled" />
<!-- Fingerprint messages -->
<java-symbol type="string" name="fingerprint_error_unable_to_process" />
@@ -2683,6 +2685,7 @@
<java-symbol type="id" name="smart_reply_container" />
<java-symbol type="id" name="remote_input_tag" />
<java-symbol type="id" name="pending_intent_tag" />
+ <java-symbol type="id" name="notification_action_index_tag" />
<java-symbol type="attr" name="seekBarDialogPreferenceStyle" />
<java-symbol type="string" name="ext_media_status_removed" />
@@ -3262,6 +3265,7 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
+ <java-symbol type="string" name="config_defaultSmartSuggestionsService" />
<java-symbol type="string" name="notification_channel_foreground_service" />
<java-symbol type="string" name="foreground_service_app_in_background" />
@@ -3307,6 +3311,7 @@
<java-symbol type="integer" name="config_autoGroupAtCount" />
<java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
<java-symbol type="bool" name="config_dozeAlwaysOnEnabled" />
+ <java-symbol type="bool" name="config_dozeSupportsAodWallpaper" />
<java-symbol type="bool" name="config_displayBlanksAfterDoze" />
<java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
<java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
@@ -3498,4 +3503,8 @@
<java-symbol type="string" name="config_defaultAssistantComponentName" />
<java-symbol type="id" name="transition_overlay_view_tag" />
+
+ <java-symbol type="dimen" name="rounded_corner_radius" />
+ <java-symbol type="dimen" name="rounded_corner_radius_top" />
+ <java-symbol type="dimen" name="rounded_corner_radius_bottom" />
</resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f60d8d0ad..46d4a47 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -91,6 +91,8 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.KILL_UID" />
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+
<!-- location test permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
@@ -1316,6 +1318,12 @@
<service android:name="android.os.BinderThreadPriorityService"
android:process=":BinderThreadPriorityService" />
+ <!-- Used by BinderWorkSourceTest -->
+ <service android:name="android.os.BinderWorkSourceService"
+ android:process=":BinderWorkSourceService" />
+ <service android:name="android.os.BinderWorkSourceNestedService"
+ android:process=":BinderWorkSourceNestedService" />
+
<!-- Application components used for search manager tests -->
<activity android:name="android.app.activity.SearchableActivity"
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e1cb911..e89a4d3 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -106,9 +106,9 @@
int backgroundColor = 0xff585868;
int initialForegroundColor = 0xff505868;
builder.setColorPalette(backgroundColor, initialForegroundColor);
- int primaryTextColor = builder.getPrimaryTextColor();
+ int primaryTextColor = builder.getPrimaryTextColor(builder.mParams);
assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor));
- int secondaryTextColor = builder.getSecondaryTextColor();
+ int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams);
assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java
new file mode 100644
index 0000000..dddeda3
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+* Service used by {@link BinderWorkSourceTest}.
+*/
+public class BinderWorkSourceNestedService extends Service {
+ private final IBinderWorkSourceNestedService.Stub mBinder =
+ new IBinderWorkSourceNestedService.Stub() {
+
+ public int[] nestedCallWithWorkSourceToSet(int uidToBlame) {
+ final int uid = Binder.getCallingWorkSourceUid();
+ if (uidToBlame != ThreadLocalWorkSource.UID_NONE) {
+ Binder.setCallingWorkSourceUid(uidToBlame);
+ }
+ final int nestedUid = callGetIncomingWorkSourceUid();
+ return new int[] {uid, nestedUid};
+ }
+
+ public int[] nestedCall() {
+ final int uid = Binder.getCallingWorkSourceUid();
+ final int nestedUid = callGetIncomingWorkSourceUid();
+ return new int[] {uid, nestedUid};
+ }
+
+ private int callGetIncomingWorkSourceUid() {
+ BlockingQueue<IBinderWorkSourceService> blockingQueue =
+ new LinkedBlockingQueue<>();
+ ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ blockingQueue.add(IBinderWorkSourceService.Stub.asInterface(service));
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+
+ Context context = getApplicationContext();
+ context.bindService(
+ new Intent(context, BinderWorkSourceService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+
+ final IBinderWorkSourceService service;
+ try {
+ service = blockingQueue.poll(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (service == null) {
+ throw new RuntimeException("Gave up waiting for BinderWorkSourceService");
+ }
+
+ try {
+ return service.getIncomingWorkSourceUid();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ context.unbindService(mConnection);
+ }
+ }
+ };
+
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceService.java b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
new file mode 100644
index 0000000..ac8d7ab9
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.app.Service;
+import android.content.Intent;
+
+/**
+ * Service used by {@link BinderWorkSourceTest}.
+ */
+public class BinderWorkSourceService extends Service {
+ private final IBinderWorkSourceService.Stub mBinder =
+ new IBinderWorkSourceService.Stub() {
+ public int getIncomingWorkSourceUid() {
+ return Binder.getCallingWorkSourceUid();
+ }
+ };
+
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
new file mode 100644
index 0000000..ec17803
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test whether Binder calls work source is propagated correctly.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BinderWorkSourceTest {
+ private static Context sContext;
+ private static final int UID = 100;
+ private static final int SECOND_UID = 200;
+ private static final int UID_NONE = ThreadLocalWorkSource.UID_NONE;
+
+ private IBinderWorkSourceService mService;
+ private IBinderWorkSourceNestedService mNestedService;
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = IBinderWorkSourceService.Stub.asInterface(service);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ private ServiceConnection mNestedConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mNestedService = IBinderWorkSourceNestedService.Stub.asInterface(service);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mNestedService = null;
+ }
+ };
+
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ sContext = InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ sContext.bindService(
+ new Intent(sContext, BinderWorkSourceService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ sContext.bindService(
+ new Intent(sContext, BinderWorkSourceNestedService.class),
+ mNestedConnection, Context.BIND_AUTO_CREATE);
+
+ final long timeoutMs = System.currentTimeMillis() + 30_000;
+ while ((mService == null || mNestedService == null)
+ && System.currentTimeMillis() < timeoutMs) {
+ Thread.sleep(1_000);
+ }
+ assertNotNull("Gave up waiting for BinderWorkSourceService", mService);
+ assertNotNull("Gave up waiting for BinderWorkSourceNestedService", mNestedService);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ sContext.unbindService(mConnection);
+ sContext.unbindService(mNestedConnection);
+ }
+
+ @Test
+ public void setWorkSource() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void clearWorkSource() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+ Binder.clearCallingWorkSource();
+ assertEquals(UID_NONE, mService.getIncomingWorkSourceUid());
+ assertEquals(UID_NONE, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void setWorkSource_propagatedForMultipleCalls() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void restoreWorkSource() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+ long token = Binder.clearCallingWorkSource();
+ Binder.restoreCallingWorkSource(token);
+
+ assertEquals(UID, mService.getIncomingWorkSourceUid());
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void nestedSetWorkSoucePropagated() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+
+ int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(SECOND_UID);
+ assertEquals(UID, workSources[0]);
+ // UID set in ested call.
+ assertEquals(SECOND_UID, workSources[1]);
+ // Initial work source restored.
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void nestedSetWorkSouceDoesNotEnablePropagation() throws Exception {
+ int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(UID);
+ assertEquals(UID_NONE, workSources[0]);
+ // UID set in ested call.
+ assertEquals(UID, workSources[1]);
+ // Initial work source restored.
+ assertEquals(UID_NONE, Binder.getCallingWorkSourceUid());
+ }
+
+ @Test
+ public void nestedSetWorkSouceNotPropagated() throws Exception {
+ Binder.setCallingWorkSourceUid(UID);
+
+ int[] workSources = mNestedService.nestedCall();
+ assertEquals(UID, workSources[0]);
+ // No UID propagated.
+ assertEquals(UID_NONE, workSources[1]);
+ // Initial work source restored.
+ assertEquals(UID, Binder.getCallingWorkSourceUid());
+ }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
index 27d25b8..365aebb 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package android.os;
+
+interface IBinderWorkSourceNestedService {
+ int[] nestedCallWithWorkSourceToSet(int uidToBlame);
+ int[] nestedCall();
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
index 27d25b8..05d4e82 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -13,12 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package android.os;
+
+interface IBinderWorkSourceService {
+ int getIncomingWorkSourceUid();
}
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index c8bc35c..9e15231 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -16,6 +16,10 @@
package android.os;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.RedactingFileDescriptor.removeRange;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -58,8 +62,8 @@
@Test
public void testSingleByte() throws Exception {
- final FileDescriptor fd = RedactingFileDescriptor
- .open(mContext, mFile, new long[] { 10, 11 }).getFileDescriptor();
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+ new long[] { 10, 11 }).getFileDescriptor();
final byte[] buf = new byte[1_000];
assertEquals(buf.length, Os.read(fd, buf, 0, buf.length));
@@ -74,8 +78,8 @@
@Test
public void testRanges() throws Exception {
- final FileDescriptor fd = RedactingFileDescriptor
- .open(mContext, mFile, new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+ new long[] { 100, 200, 300, 400 }).getFileDescriptor();
final byte[] buf = new byte[10];
assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90));
@@ -96,8 +100,8 @@
@Test
public void testEntireFile() throws Exception {
- final FileDescriptor fd = RedactingFileDescriptor
- .open(mContext, mFile, new long[] { 0, 5_000_000 }).getFileDescriptor();
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+ new long[] { 0, 5_000_000 }).getFileDescriptor();
try (FileInputStream in = new FileInputStream(fd)) {
int val;
@@ -106,4 +110,61 @@
}
}
}
+
+ @Test
+ public void testReadWrite() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+ new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+
+ // Redacted at first
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+ assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 0, 0, 0 }, buf);
+
+ // But we can see data that we've written
+ Os.pwrite(fd, new byte[] { 32, 32 }, 0, 2, 102);
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+ assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 32, 32, 0 }, buf);
+ }
+
+ @Test
+ public void testRemoveRange() throws Exception {
+ // Removing outside ranges should have no changes
+ assertArrayEquals(new long[] { 100, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 0, 100));
+ assertArrayEquals(new long[] { 100, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 200, 300));
+ assertArrayEquals(new long[] { 100, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 400, 500));
+
+ // Removing full regions
+ assertArrayEquals(new long[] { 100, 200 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 300, 400));
+ assertArrayEquals(new long[] { 100, 200 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 250, 450));
+ assertArrayEquals(new long[] { 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 50, 250));
+ assertArrayEquals(new long[] { },
+ removeRange(new long[] { 100, 200, 300, 400 }, 0, 5_000_000));
+ }
+
+ @Test
+ public void testRemoveRange_Partial() throws Exception {
+ assertArrayEquals(new long[] { 150, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 50, 150));
+ assertArrayEquals(new long[] { 100, 150, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 150, 250));
+ assertArrayEquals(new long[] { 100, 150, 350, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 150, 350));
+ assertArrayEquals(new long[] { 100, 150 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 150, 500));
+ }
+
+ @Test
+ public void testRemoveRange_Hole() throws Exception {
+ assertArrayEquals(new long[] { 100, 125, 175, 200, 300, 400 },
+ removeRange(new long[] { 100, 200, 300, 400 }, 125, 175));
+ assertArrayEquals(new long[] { 100, 200 },
+ removeRange(new long[] { 100, 200 }, 150, 150));
+ }
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 2c001c9..ed9c3d5 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -126,6 +126,7 @@
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS,
Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
+ Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
@@ -192,6 +193,10 @@
Settings.Global.DATA_ROAMING,
Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
+ Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
+ Settings.Global.DATA_STALL_EVALUATION_TYPE,
+ Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+ Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
Settings.Global.DEBUG_APP,
Settings.Global.DEBUG_VIEW_ATTRIBUTES,
Settings.Global.DEFAULT_DNS_SERVER,
@@ -310,6 +315,7 @@
Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
Settings.Global.MULTI_SIM_VOICE_PROMPT,
+ Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
Settings.Global.NETSTATS_DEV_BUCKET_DURATION,
Settings.Global.NETSTATS_DEV_DELETE_AGE,
Settings.Global.NETSTATS_DEV_PERSIST_BYTES,
@@ -409,6 +415,7 @@
Settings.Global.SHOW_TEMPERATURE_WARNING,
Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
+ Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED,
Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED,
Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
@@ -471,6 +478,7 @@
Settings.Global.USER_SWITCHER_ENABLED,
Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
Settings.Global.WARNING_TEMPERATURE,
+ Settings.Global.USB_ALARM_TEMPERATURE,
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
Settings.Global.WEBVIEW_MULTIPROCESS,
@@ -657,7 +665,10 @@
Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION,
Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
Settings.Secure.FLASHLIGHT_AVAILABLE,
- Settings.Secure.FLASHLIGHT_ENABLED);
+ Settings.Secure.FLASHLIGHT_ENABLED,
+ Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+ Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS,
+ Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
new file mode 100644
index 0000000..0c7e730
--- /dev/null
+++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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,d
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class AndroidCharacterTest {
+
+ @Test
+ public void testGetDirectionalities_nonSupplementaryCharacters() {
+ int size = Character.MAX_VALUE + 1
+ - (Character.MAX_SURROGATE - Character.MIN_SURROGATE + 1);
+ char[] chars = new char[size];
+ byte[] java_lang_results = new byte[size];
+ int index = 0;
+ for (int cp = 0; cp <= Character.MAX_VALUE; cp++) {
+ if (cp < Character.MIN_SURROGATE || cp > Character.MAX_SURROGATE) {
+ chars[index] = (char) cp;
+ java_lang_results[index] = Character.getDirectionality(cp);
+ index++;
+ }
+ }
+
+ byte[] android_text_results = new byte[size];
+ AndroidCharacter.getDirectionalities(chars, android_text_results, index);
+ assertArrayEquals(java_lang_results, android_text_results);
+ }
+
+ @Test
+ public void testGetDirectionalities_supplementaryCharacters() {
+ int maxNumberOfChars = Character.MAX_CODE_POINT - Character.MIN_SUPPLEMENTARY_CODE_POINT
+ + 1;
+ int size = maxNumberOfChars * 2;
+ char[] chars = new char[size];
+ byte[] java_lang_results = new byte[size];
+ int index = 0;
+ for (int cp = Character.MIN_SUPPLEMENTARY_CODE_POINT; cp <= Character.MAX_CODE_POINT;
+ cp++) {
+ chars[index] = Character.highSurrogate(cp);
+ chars[index + 1] = Character.lowSurrogate(cp);
+ java_lang_results[index] = java_lang_results[index + 1] = Character
+ .getDirectionality(cp);
+ index += 2;
+ }
+
+ byte[] android_text_results = new byte[size];
+ AndroidCharacter.getDirectionalities(chars, android_text_results, index);
+ assertArrayEquals(java_lang_results, android_text_results);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
new file mode 100644
index 0000000..ed80cd7
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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
+ */
+
+package android.view;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsControllerTest {
+
+ private InsetsController mController = new InsetsController();
+
+ private SurfaceSession mSession = new SurfaceSession();
+ private SurfaceControl mLeash;
+
+ @Before
+ public void setup() {
+ mLeash = new SurfaceControl.Builder(mSession)
+ .setName("testSurface")
+ .build();
+ }
+
+ @Test
+ public void testControlsChanged() {
+ InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash);
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
+ assertEquals(mLeash,
+ mController.getSourceConsumer(TYPE_TOP_BAR).getControl().getLeash());
+ }
+
+ @Test
+ public void testControlsRevoked() {
+ InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash);
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
+ mController.onControlsChanged(new InsetsSourceControl[0]);
+ assertNull(mController.getSourceConsumer(TYPE_TOP_BAR).getControl());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
new file mode 100644
index 0000000..5a20ba2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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
+ */
+
+package android.view;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl.Transaction;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsSourceConsumerTest {
+
+ private InsetsSourceConsumer mConsumer;
+
+ private SurfaceSession mSession = new SurfaceSession();
+ private SurfaceControl mLeash;
+ @Mock Transaction mMockTransaction;
+ @Mock InsetsController mMockController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mLeash = new SurfaceControl.Builder(mSession)
+ .setName("testSurface")
+ .build();
+ mConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, new InsetsState(),
+ () -> mMockTransaction);
+ mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
+ }
+
+ @Test
+ public void testHide() {
+ mConsumer.hide();
+ verify(mMockTransaction).hide(eq(mLeash));
+ }
+
+ @Test
+ public void testShow() {
+ mConsumer.hide();
+ mConsumer.show();
+ verify(mMockTransaction, atLeastOnce()).show(eq(mLeash));
+ }
+
+ @Test
+ public void testRestore() {
+ mConsumer.setControl(null);
+ reset(mMockTransaction);
+ mConsumer.hide();
+ verifyZeroInteractions(mMockTransaction);
+ mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
+ verify(mMockTransaction).hide(eq(mLeash));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
new file mode 100644
index 0000000..ed472d2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsSourceTest {
+
+ private InsetsSource mSource = new InsetsSource(TYPE_NAVIGATION_BAR);
+
+ @Before
+ public void setUp() {
+ mSource.setVisible(true);
+ }
+
+ @Test
+ public void testCalculateInsetsTop() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsetsBottom() {
+ mSource.setFrame(new Rect(0, 400, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
+ public void testCalculateInsetsLeft() {
+ mSource.setFrame(new Rect(0, 0, 100, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(100, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsetsRight() {
+ mSource.setFrame(new Rect(400, 0, 500, 500));
+ Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 0, 100, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_overextend() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_invisible() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisible(false);
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateInsets_ignoreVisibility() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisible(false);
+ Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+ true /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ // Parcel and equals already tested via InsetsStateTest
+}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
new file mode 100644
index 0000000..6bb9539
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsStateTest {
+
+ private InsetsState mState = new InsetsState();
+ private InsetsState mState2 = new InsetsState();
+
+ @Test
+ public void testCalculateInsets() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_TOP_BAR).setVisible(true);
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(TYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT);
+ assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+ }
+
+ @Test
+ public void testCalculateInsets_imeAndNav() {
+ mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(TYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT);
+ assertEquals(100, insets.getStableInsetBottom());
+ assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
+ }
+
+ @Test
+ public void testCalculateInsets_navRightStatusTop() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_TOP_BAR).setVisible(true);
+ mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+ mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT);
+ assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
+ }
+
+ @Test
+ public void testStripForDispatch() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_TOP_BAR).setVisible(true);
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(TYPE_IME).setVisible(true);
+ mState.removeSource(TYPE_IME);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT);
+ assertEquals(0, insets.getSystemWindowInsetBottom());
+ }
+
+ @Test
+ public void testEquals_differentRect() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10));
+ assertNotEquals(mState, mState2);
+ }
+
+ @Test
+ public void testEquals_differentSource() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ assertNotEquals(mState, mState2);
+ }
+
+ @Test
+ public void testEquals_sameButDifferentInsertOrder() {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ assertEquals(mState, mState2);
+ }
+
+ @Test
+ public void testEquals_visibility() {
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_IME).setVisible(true);
+ mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ assertNotEquals(mState, mState2);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_IME).setVisible(true);
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ Parcel p = Parcel.obtain();
+ mState.writeToParcel(p, 0 /* flags */);
+ mState2.readFromParcel(p);
+ p.recycle();
+ assertEquals(mState, mState2);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 2ec35e9..fbcb629 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.content.Intent;
import android.os.LocaleList;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -64,6 +65,8 @@
@Parameterized.Parameter
public String mTextClassifierType;
+ private static final TextClassificationConstants TC_CONSTANTS =
+ TextClassificationConstants.loadFromString("");
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
private static final String NO_TYPE = null;
@@ -80,7 +83,7 @@
}
@Test
- public void testSmartSelection() {
+ public void testSuggestSelection() {
if (isTextClassifierDisabled()) return;
String text = "Contact me at droid@android.com";
@@ -101,7 +104,7 @@
}
@Test
- public void testSmartSelection_url() {
+ public void testSuggestSelection_url() {
if (isTextClassifierDisabled()) return;
String text = "Visit http://www.android.com for more information";
@@ -157,7 +160,7 @@
}
@Test
- public void testTextClassifyText_url() {
+ public void testClassifyText_url() {
if (isTextClassifierDisabled()) return;
String text = "Visit www.android.com for more information";
@@ -174,7 +177,7 @@
}
@Test
- public void testTextClassifyText_address() {
+ public void testClassifyText_address() {
if (isTextClassifierDisabled()) return;
String text = "Brandschenkestrasse 110, Zürich, Switzerland";
@@ -188,7 +191,7 @@
}
@Test
- public void testTextClassifyText_url_inCaps() {
+ public void testClassifyText_url_inCaps() {
if (isTextClassifierDisabled()) return;
String text = "Visit HTTP://ANDROID.COM for more information";
@@ -205,7 +208,7 @@
}
@Test
- public void testTextClassifyText_date() {
+ public void testClassifyText_date() {
if (isTextClassifierDisabled()) return;
String text = "Let's meet on January 9, 2018.";
@@ -222,7 +225,7 @@
}
@Test
- public void testTextClassifyText_datetime() {
+ public void testClassifyText_datetime() {
if (isTextClassifierDisabled()) return;
String text = "Let's meet 2018/01/01 10:30:20.";
@@ -240,6 +243,30 @@
}
@Test
+ public void testClassifyText_foreignText() {
+ LocaleList originalLocales = LocaleList.getDefault();
+ LocaleList.setDefault(LocaleList.forLanguageTags("en"));
+ String foreignText = "これは日本語のテキストです";
+
+ Context context = new FakeContextBuilder()
+ .setIntentComponent(Intent.ACTION_TRANSLATE, FakeContextBuilder.DEFAULT_COMPONENT)
+ .build();
+ TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
+ TextClassification.Request request = new TextClassification.Request.Builder(
+ foreignText, 0, foreignText.length())
+ .setDefaultLocales(LOCALES)
+ .build();
+
+ TextClassification classification = classifier.classifyText(request);
+ assertEquals(1, classification.getActions().size());
+ assertEquals(
+ context.getString(com.android.internal.R.string.translate),
+ classification.getActions().get(0).getTitle());
+
+ LocaleList.setDefault(originalLocales);
+ }
+
+ @Test
public void testGenerateLinks_phone() {
if (isTextClassifierDisabled()) return;
String text = "The number is +12122537077. See you tonight!";
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 4456122..36792bb 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -467,6 +467,21 @@
assertArrayEquals(container.mSharedViewNames, new String[] {"e0", "e1", "e2"});
}
+ @Test
+ public void setIntTag() {
+ RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+ int index = 10;
+ views.setIntTag(
+ R.id.layout, com.android.internal.R.id.notification_action_index_tag, index);
+
+ RemoteViews recovered = parcelAndRecreate(views);
+ RemoteViews cloned = new RemoteViews(recovered);
+ View inflated = cloned.apply(mContext, mContainer);
+
+ assertEquals(
+ index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag));
+ }
+
private class WidgetContainer extends AppWidgetHostView {
int[] mSharedViewIds;
String[] mSharedViewNames;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 9fcb06e..404c99c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -103,6 +103,49 @@
}
@Test
+ public void setMaxHeight() throws Exception {
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ waitForIdle();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ final View resolverList = activity.findViewById(R.id.resolver_list);
+ final int initialResolverHeight = resolverList.getHeight();
+
+ activity.runOnUiThread(() -> {
+ ResolverDrawerLayout layout = (ResolverDrawerLayout)
+ activity.findViewById(
+ R.id.contentPanel);
+ ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+ = initialResolverHeight - 1;
+ // Force a relayout
+ layout.invalidate();
+ layout.requestLayout();
+ });
+ waitForIdle();
+ assertThat("Drawer should be capped at maxHeight",
+ resolverList.getHeight() == (initialResolverHeight - 1));
+
+ activity.runOnUiThread(() -> {
+ ResolverDrawerLayout layout = (ResolverDrawerLayout)
+ activity.findViewById(
+ R.id.contentPanel);
+ ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+ = initialResolverHeight + 1;
+ // Force a relayout
+ layout.invalidate();
+ layout.requestLayout();
+ });
+ waitForIdle();
+ assertThat("Drawer should not change height if its height is less than maxHeight",
+ resolverList.getHeight() == initialResolverHeight);
+ }
+
+ @Test
public void setShowAtTopToTrue() throws Exception {
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 30309cf..02a76f8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -17,6 +17,7 @@
package com.android.internal.os;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
@@ -387,8 +388,7 @@
@Test
public void testNoDataCollectedBeforeInitialDeviceStateSet() {
- TestBinderCallsStats bcs = new TestBinderCallsStats();
- bcs.setDeviceState(null);
+ TestBinderCallsStats bcs = new TestBinderCallsStats(null);
bcs.setDetailedTracking(true);
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
@@ -557,6 +557,83 @@
assertEquals(0, bcs.getExceptionCounts().size());
}
+ @Test
+ public void testOverflow_sameEntry() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.setSamplingInterval(1);
+ bcs.setMaxBinderCallStats(2);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+ assertEquals(1, callStatsList.size());
+ BinderCallsStats.CallStat callStats = callStatsList.get(0);
+ assertEquals(3, callStats.callCount);
+ }
+
+ @Test
+ public void testOverflow_overflowEntry() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.setSamplingInterval(1);
+ bcs.setMaxBinderCallStats(1);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 2);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats();
+ assertEquals(2, callStatsList.size());
+ BinderCallsStats.ExportedCallStat callStats = callStatsList.get(0);
+ assertEquals(1, callStats.callCount);
+ assertEquals("1", callStats.methodName);
+ assertEquals("android.os.Binder", callStats.className);
+ assertEquals(CALLING_UID, callStats.callingUid);
+
+ callStats = callStatsList.get(1);
+ assertEquals(1, callStats.callCount);
+ assertEquals("-1", callStats.methodName);
+ assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
+ callStats.className);
+ assertEquals(CALLING_UID, callStats.callingUid);
+ }
+
+ @Test
+ public void testAddsDebugEntries() {
+ long startTime = System.currentTimeMillis();
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setAddDebugEntries(true);
+ ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats();
+ assertEquals(3, callStats.size());
+ BinderCallsStats.ExportedCallStat debugEntry1 = callStats.get(0);
+ assertEquals("", debugEntry1.className);
+ assertEquals("__DEBUG_start_time_millis", debugEntry1.methodName);
+ assertTrue(startTime <= debugEntry1.maxReplySizeBytes);
+ BinderCallsStats.ExportedCallStat debugEntry2 = callStats.get(1);
+ assertEquals("", debugEntry2.className);
+ assertEquals("__DEBUG_end_time_millis", debugEntry2.methodName);
+ assertTrue(debugEntry1.maxReplySizeBytes <= debugEntry2.maxReplySizeBytes);
+ BinderCallsStats.ExportedCallStat debugEntry3 = callStats.get(2);
+ assertEquals("", debugEntry3.className);
+ assertEquals("__DEBUG_battery_time_millis", debugEntry3.methodName);
+ assertTrue(debugEntry3.maxReplySizeBytes >= 0);
+ }
+
class TestBinderCallsStats extends BinderCallsStats {
public int callingUid = CALLING_UID;
public int workSourceUid = WORKSOURCE_UID;
@@ -564,6 +641,10 @@
public long elapsedTime = 0;
TestBinderCallsStats() {
+ this(mDeviceState);
+ }
+
+ TestBinderCallsStats(CachedDeviceState deviceState) {
// Make random generator not random.
super(new Injector() {
public Random getRandomGenerator() {
@@ -577,7 +658,10 @@
}
});
setSamplingInterval(1);
- setDeviceState(mDeviceState.getReadonlyClient());
+ setAddDebugEntries(false);
+ if (deviceState != null) {
+ setDeviceState(deviceState.getReadonlyClient());
+ }
}
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index 385bad5..b242a34 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,9 +39,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
-import java.util.List;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -58,8 +56,8 @@
1000, 2000, 3000, 4000,
};
private static final int[][] THREAD_CPU_TIMES = {
- {100, 0, 0, 100},
- {0, 0, 9999999, 0},
+ {1, 0, 0, 1},
+ {0, 0, 0, 0},
{1000, 1000, 1000, 1000},
{0, 1, 2, 3},
};
@@ -110,42 +108,6 @@
}
@Test
- public void testReader_filtersLowTotalCpuUsage() throws IOException {
- KernelCpuThreadReader.Injector processUtils =
- new KernelCpuThreadReader.Injector() {
- @Override
- public int myPid() {
- return PROCESS_ID;
- }
-
- @Override
- public int myUid() {
- return UID;
- }
-
- @Override
- public int getUidForPid(int pid) {
- return 0;
- }
- };
- setupDirectory(mProcDirectory.toPath().resolve("self"), new int[]{1, 2}, PROCESS_NAME,
- THREAD_NAMES, new int[]{1000, 2000}, new int[][]{{0, 1}, {100, 0}});
-
- final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
- mProcDirectory.toPath(),
- mProcDirectory.toPath().resolve("self/task/1/time_in_state"),
- processUtils);
- final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
- kernelCpuThreadReader.getCurrentProcessCpuUsage();
-
- List<Integer> threadIds = processCpuUsage.threadCpuUsages.stream()
- .map(t -> t.threadId)
- .collect(Collectors.toList());
- assertEquals(1, threadIds.size());
- assertEquals(2, (long) threadIds.get(0));
- }
-
- @Test
public void testReader_byUids() throws IOException {
int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
@@ -172,7 +134,7 @@
setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
new int[]{uid * 10},
"process" + uid, new String[]{"thread" + uid}, new int[]{1000},
- new int[][]{{uid + 100}});
+ new int[][]{{uid}});
}
final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
mProcDirectory.toPath(),
@@ -189,7 +151,7 @@
int uid = expectedUids[i];
checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(),
uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid},
- new int[]{1000}, new int[][]{{uid + 100}});
+ new int[]{1000}, new int[][]{{uid}});
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 31dde5c..3d7801c 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -273,8 +273,7 @@
@Test
public void testDataNotCollectedBeforeDeviceStateSet() {
- TestableLooperStats looperStats = new TestableLooperStats(1, 100);
- looperStats.setDeviceState(null);
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100, null);
Object token1 = looperStats.messageDispatchStarting();
looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
@@ -439,7 +438,7 @@
looperStats.messageDispatched(token, message);
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
- assertThat(entries).hasSize(3);
+ assertThat(entries).hasSize(4);
LooperStats.ExportedEntry debugEntry1 = entries.get(1);
assertThat(debugEntry1.handlerClassName).isEqualTo("");
assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
@@ -448,6 +447,10 @@
assertThat(debugEntry2.handlerClassName).isEqualTo("");
assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
assertThat(debugEntry2.maxDelayMillis).isAtLeast(looperStats.getStartTimeMillis());
+ LooperStats.ExportedEntry debugEntry3 = entries.get(3);
+ assertThat(debugEntry3.handlerClassName).isEqualTo("");
+ assertThat(debugEntry3.messageName).isEqualTo("__DEBUG_battery_time_millis");
+ assertThat(debugEntry3.maxDelayMillis).isAtLeast(0L);
}
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
@@ -468,10 +471,16 @@
private int mSamplingInterval;
TestableLooperStats(int samplingInterval, int sizeCap) {
+ this(samplingInterval, sizeCap, mDeviceState);
+ }
+
+ TestableLooperStats(int samplingInterval, int sizeCap, CachedDeviceState deviceState) {
super(samplingInterval, sizeCap);
- this.mSamplingInterval = samplingInterval;
- this.setDeviceState(mDeviceState.getReadonlyClient());
- this.setAddDebugEntries(false);
+ mSamplingInterval = samplingInterval;
+ setAddDebugEntries(false);
+ if (deviceState != null) {
+ setDeviceState(deviceState.getReadonlyClient());
+ }
}
void tickRealtime(long micros) {
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 3b0dc9d..135c137 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -64,7 +64,7 @@
public boolean isRecordingFor(Object o) { return false; }
// may be null
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521088)
private Bitmap mBitmap;
// optional field set by the caller
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 2e1d81a..2227cf5 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1779,6 +1779,70 @@
}
/**
+ * <p>Computes the chromaticity coordinates of a CIE series D illuminant
+ * from the specified correlated color temperature (CCT). The specified CCT
+ * must be greater than 0. A meaningful CCT range is [4000, 25000].</p>
+ *
+ * <p>The transform is computed using the methods referred to in Kang et
+ * al., <i>Design of Advanced Color - Temperature Control System for HDTV
+ * Applications</i>, Journal of Korean Physical Society 41, 865-871
+ * (2002).</p>
+ *
+ * @param cct The correlated color temperature, in Kelvin
+ * @return Corresponding XYZ values
+ * @throws IllegalArgumentException If cct is invalid
+ */
+ @NonNull
+ @Size(3)
+ public static float[] cctToIlluminantdXyz(@IntRange(from = 1) int cct) {
+ if (cct < 1) {
+ throw new IllegalArgumentException("Temperature must be greater than 0");
+ }
+
+ final float icct = 1.0f / cct;
+ final float icct2 = icct * icct;
+ final float x = cct <= 7000.0f ?
+ 0.244063f + 0.09911e3f * icct + 2.9678e6f * icct2 - 4.6070e9f * icct2 * icct :
+ 0.237040f + 0.24748e3f * icct + 1.9018e6f * icct2 - 2.0064e9f * icct2 * icct;
+ final float y = -3.0f * x * x + 2.87f * x - 0.275f;
+ return xyYToXyz(new float[] {x, y});
+ }
+
+ /**
+ * <p>Computes the chromatic adaptation transform from the specified
+ * source white point to the specified destination white point.</p>
+ *
+ * <p>The transform is computed using the von Kries method, described
+ * in more details in the documentation of {@link Adaptation}. The
+ * {@link Adaptation} enum provides different matrices that can be
+ * used to perform the adaptation.</p>
+ *
+ * @param adaptation The adaptation method
+ * @param srcWhitePoint The white point to adapt from
+ * @param dstWhitePoint The white point to adapt to
+ * @return A 3x3 matrix as a non-null array of 9 floats
+ */
+ @NonNull
+ @Size(9)
+ public static float[] chromaticAdaptation(@NonNull Adaptation adaptation,
+ @NonNull @Size(min = 2, max = 3) float[] srcWhitePoint,
+ @NonNull @Size(min = 2, max = 3) float[] dstWhitePoint) {
+ float[] srcXyz = srcWhitePoint.length == 3 ?
+ Arrays.copyOf(srcWhitePoint, 3) : xyYToXyz(srcWhitePoint);
+ float[] dstXyz = dstWhitePoint.length == 3 ?
+ Arrays.copyOf(dstWhitePoint, 3) : xyYToXyz(dstWhitePoint);
+
+ if (compare(srcXyz, dstXyz)) {
+ return new float[] {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+ };
+ }
+ return chromaticAdaptation(adaptation.mTransform, srcXyz, dstXyz);
+ }
+
+ /**
* Implementation of the CIE XYZ color space. Assumes the white point is D50.
*/
@AnyThread
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index de110c8..d9da27c 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -82,6 +82,17 @@
}
/**
+ * Add two Insets.
+ *
+ * @param a The first Insets to add.
+ * @param b The second Insets to add.
+ * @return a + b, i. e. all insets on every side are added together.
+ */
+ public static @NonNull Insets add(@NonNull Insets a, @NonNull Insets b) {
+ return Insets.of(a.left + b.left, a.top + b.top, a.right + b.right, a.bottom + b.bottom);
+ }
+
+ /**
* Two Insets instances are equal iff they belong to the same class and their fields are
* pairwise equal.
*
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index c4dc0ad..40a32f3 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -106,6 +106,20 @@
}
/**
+ * @hide
+ */
+ public Rect(@Nullable Insets r) {
+ if (r == null) {
+ left = top = right = bottom = 0;
+ } else {
+ left = r.left;
+ top = r.top;
+ right = r.right;
+ bottom = r.bottom;
+ }
+ }
+
+ /**
* Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
*
* @hide
@@ -418,6 +432,18 @@
}
/**
+ * Insets the rectangle on all sides specified by the dimensions of {@code insets}.
+ * @hide
+ * @param insets The insets to inset the rect by.
+ */
+ public void inset(Insets insets) {
+ left += insets.left;
+ top += insets.top;
+ right -= insets.right;
+ bottom -= insets.bottom;
+ }
+
+ /**
* Insets the rectangle on all sides specified by the insets.
* @hide
* @param left The amount to add from the rectangle's left
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 45d7a21..d6f08b9 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -1173,6 +1173,22 @@
return nGetAllowForceDark(mNativeRenderNode);
}
+ /**
+ * Returns the unique ID that identifies this RenderNode. This ID is unique for the
+ * lifetime of the process. IDs are reset on process death, and are unique only within
+ * the process.
+ *
+ * This ID is intended to be used with debugging tools to associate a particular
+ * RenderNode across different debug dumping & inspection tools. For example
+ * a View layout inspector should include the unique ID for any RenderNodes that it owns
+ * to associate the drawing content with the layout content.
+ *
+ * @return the unique ID for this RenderNode
+ */
+ public long getUniqueId() {
+ return nGetUniqueId(mNativeRenderNode);
+ }
+
///////////////////////////////////////////////////////////////////////////
// Animations
///////////////////////////////////////////////////////////////////////////
@@ -1479,4 +1495,7 @@
@CriticalNative
private static native boolean nGetAllowForceDark(long renderNode);
+
+ @CriticalNative
+ private static native long nGetUniqueId(long renderNode);
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index bf969ef..9b86b77 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -163,9 +163,6 @@
private int[] mSupportedAxes;
private static final int[] EMPTY_AXES = {};
- // The underlying font families.
- private final FontFamily[] mFamilies;
-
@UnsupportedAppUsage
private static void setDefault(Typeface t) {
sDefaultTypeface = t;
@@ -252,7 +249,22 @@
if (familyBuilder == null) {
return Typeface.DEFAULT;
}
- typeface = new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
+ final FontFamily family = familyBuilder.build();
+ final FontStyle normal = new FontStyle(FontStyle.FONT_WEIGHT_NORMAL,
+ FontStyle.FONT_SLANT_UPRIGHT);
+ Font bestFont = family.getFont(0);
+ int bestScore = normal.getMatchScore(bestFont.getStyle());
+ for (int i = 1; i < family.getSize(); ++i) {
+ final Font candidate = family.getFont(i);
+ final int score = normal.getMatchScore(candidate.getStyle());
+ if (score < bestScore) {
+ bestFont = candidate;
+ bestScore = score;
+ }
+ }
+ typeface = new Typeface.CustomFallbackBuilder(family)
+ .setStyle(bestFont.getStyle())
+ .build();
} catch (IOException e) {
typeface = Typeface.DEFAULT;
}
@@ -652,6 +664,18 @@
private @Nullable FontStyle mStyle;
/**
+ * Returns the maximum capacity of custom fallback families.
+ *
+ * This includes the the first font family passed to the constructor.
+ * It is guaranteed that the value will be greater than or equal to 64.
+ *
+ * @return the maximum number of font families for the custom fallback
+ */
+ public static @IntRange(from = 64) int getMaxCustomFallbackCount() {
+ return MAX_CUSTOM_FALLBACK;
+ }
+
+ /**
* Constructs a builder with a font family.
*
* @param family a family object
@@ -706,8 +730,8 @@
*/
public CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) {
Preconditions.checkNotNull(family);
- Preconditions.checkArgument(mFamilies.size() < MAX_CUSTOM_FALLBACK,
- "Custom fallback limit exceeded(" + MAX_CUSTOM_FALLBACK + ")");
+ Preconditions.checkArgument(mFamilies.size() < getMaxCustomFallbackCount(),
+ "Custom fallback limit exceeded(" + getMaxCustomFallbackCount() + ")");
mFamilies.add(family);
return this;
}
@@ -720,21 +744,17 @@
public Typeface build() {
final int userFallbackSize = mFamilies.size();
final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName);
- final FontFamily[] fullFamilies = new FontFamily[fallback.length + userFallbackSize];
final long[] ptrArray = new long[fallback.length + userFallbackSize];
for (int i = 0; i < userFallbackSize; ++i) {
ptrArray[i] = mFamilies.get(i).getNativePtr();
- fullFamilies[i] = mFamilies.get(i);
}
for (int i = 0; i < fallback.length; ++i) {
ptrArray[i + userFallbackSize] = fallback[i].getNativePtr();
- fullFamilies[i + userFallbackSize] = fallback[i];
}
final int weight = mStyle == null ? 400 : mStyle.getWeight();
final int italic =
(mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ? 0 : 1;
-
- return new Typeface(nativeCreateFromArray(ptrArray, weight, italic), fullFamilies);
+ return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
}
}
@@ -799,7 +819,7 @@
}
}
- typeface = new Typeface(nativeCreateFromTypeface(ni, style), family.mFamilies);
+ typeface = new Typeface(nativeCreateFromTypeface(ni, style));
styles.put(style, typeface);
}
return typeface;
@@ -867,8 +887,7 @@
}
typeface = new Typeface(
- nativeCreateFromTypefaceWithExactStyle(
- base.native_instance, weight, italic), base.mFamilies);
+ nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic));
innerCache.put(key, typeface);
}
return typeface;
@@ -878,8 +897,7 @@
public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
@NonNull List<FontVariationAxis> axes) {
final Typeface base = family == null ? Typeface.DEFAULT : family;
- return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
- base.mFamilies);
+ return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes));
}
/**
@@ -985,7 +1003,7 @@
ptrArray[i] = families[i].getNativePtr();
}
return new Typeface(nativeCreateFromArray(ptrArray,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), families);
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
}
/**
@@ -1033,19 +1051,6 @@
}
native_instance = ni;
- mFamilies = new FontFamily[0];
- sRegistry.registerNativeAllocation(this, native_instance);
- mStyle = nativeGetStyle(ni);
- mWeight = nativeGetWeight(ni);
- }
-
- private Typeface(long ni, @NonNull FontFamily[] families) {
- if (ni == 0) {
- throw new IllegalStateException("native typeface cannot be made");
- }
-
- native_instance = ni;
- mFamilies = families;
sRegistry.registerNativeAllocation(this, native_instance);
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
@@ -1072,8 +1077,7 @@
final Typeface base = systemFontMap.get(alias.getToName());
final int weight = alias.getWeight();
final Typeface newFace = weight == 400 ? base :
- new Typeface(nativeCreateWeightAlias(base.native_instance, weight),
- base.mFamilies);
+ new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
systemFontMap.put(alias.getName(), newFace);
}
}
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index dea2f45..cb12a7c 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -150,11 +151,14 @@
*/
AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
mLayerState = createConstantState(state, res);
-
- if (sMask == null) {
- sMask = PathParser.createPathFromPathData(
- Resources.getSystem().getString(R.string.config_icon_mask));
- }
+ // config_icon_mask from context bound resource may have been chaged using
+ // OverlayManager. Read that one first.
+ Resources r = ActivityThread.currentActivityThread() == null
+ ? Resources.getSystem()
+ : ActivityThread.currentActivityThread().getApplication().getResources();
+ // TODO: either make sMask update only when config_icon_mask changes OR
+ // get rid of it all-together in layoutlib
+ sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
mMask = new Path(sMask);
mMaskScaleOnly = new Path(mMask);
mMaskMatrix = new Matrix();
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index caf610b..5bd59d4 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -713,11 +713,12 @@
}
/**
- * Whether this drawable requests projection.
+ * Whether this drawable requests projection. Indicates that the
+ * {@link android.graphics.RenderNode} this Drawable will draw into should be drawn immediately
+ * after the closest ancestor RenderNode containing a projection receiver.
*
- * @hide magic!
+ * @see android.graphics.RenderNode#setProjectBackwards(boolean)
*/
- @UnsupportedAppUsage
public boolean isProjected() {
return false;
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 8740234..991847a 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -1571,15 +1571,32 @@
st.mGradient = a.getInt(
R.styleable.GradientDrawableGradient_type, st.mGradient);
- // TODO: Update these to be themeable.
+ final boolean hasGradientColors = st.mGradientColors != null;
+ final boolean hasGradientCenter = st.hasCenterColor();
+ final int prevStart = hasGradientColors ? st.mGradientColors[0] : 0;
+ final int prevCenter = hasGradientCenter ? st.mGradientColors[1] : 0;
+ final int prevEnd;
+
+ if (st.hasCenterColor()) {
+ // if there is a center color, the end color is the last of the 3 values
+ prevEnd = st.mGradientColors[2];
+ } else if (hasGradientColors) {
+ // if there is not a center color but there are already colors configured, then
+ // the end color is the 2nd value in the array
+ prevEnd = st.mGradientColors[1];
+ } else {
+ // otherwise, there isn't a previously configured end color
+ prevEnd = 0;
+ }
+
final int startColor = a.getColor(
- R.styleable.GradientDrawableGradient_startColor, 0);
+ R.styleable.GradientDrawableGradient_startColor, prevStart);
final boolean hasCenterColor = a.hasValue(
- R.styleable.GradientDrawableGradient_centerColor);
+ R.styleable.GradientDrawableGradient_centerColor) || hasGradientCenter;
final int centerColor = a.getColor(
- R.styleable.GradientDrawableGradient_centerColor, 0);
+ R.styleable.GradientDrawableGradient_centerColor, prevCenter);
final int endColor = a.getColor(
- R.styleable.GradientDrawableGradient_endColor, 0);
+ R.styleable.GradientDrawableGradient_endColor, prevEnd);
if (hasCenterColor) {
st.mGradientColors = new int[3];
@@ -1943,6 +1960,10 @@
}
}
+ public boolean hasCenterColor() {
+ return mGradientColors != null && mGradientColors.length == 3;
+ }
+
private void applyDensityScaling(int sourceDensity, int targetDensity) {
if (mInnerRadius > 0) {
mInnerRadius = Drawable.scaleFromDensity(
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
index 82fc7ac..af517d6 100644
--- a/graphics/java/android/graphics/fonts/FontStyle.java
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.util.Preconditions;
@@ -232,6 +233,16 @@
return mSlant;
}
+ /**
+ * Compute the matching score for another style.
+ *
+ * The smaller is better.
+ * @hide
+ */
+ public int getMatchScore(@NonNull FontStyle o) {
+ return Math.abs((getWeight() - o.getWeight())) / 100 + (getSlant() == o.getSlant() ? 0 : 2);
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 7ab12b1..ad9ec02 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -217,10 +217,19 @@
ATRACE_NAME("AssetManager::GetResourceConfigurations");
std::set<ResTable_config> configurations;
for (const PackageGroup& package_group : package_groups_) {
+ bool found_system_package = false;
for (const ConfiguredPackage& package : package_group.packages_) {
if (exclude_system && package.loaded_package_->IsSystem()) {
+ found_system_package = true;
continue;
}
+
+ if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
+ // Overlays must appear after the target package to take effect. Any overlay found in the
+ // same package as a system package is able to overlay system resources.
+ continue;
+ }
+
package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
}
}
@@ -232,10 +241,19 @@
ATRACE_NAME("AssetManager::GetResourceLocales");
std::set<std::string> locales;
for (const PackageGroup& package_group : package_groups_) {
+ bool found_system_package = false;
for (const ConfiguredPackage& package : package_group.packages_) {
if (exclude_system && package.loaded_package_->IsSystem()) {
+ found_system_package = true;
continue;
}
+
+ if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
+ // Overlays must appear after the target package to take effect. Any overlay found in the
+ // same package as a system package is able to overlay system resources.
+ continue;
+ }
+
package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
}
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 68d216d..c20c720 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -583,7 +583,65 @@
loaded_package->dynamic_package_map_.emplace_back(std::move(package_name),
dtohl(entry_iter->packageId));
}
+ } break;
+ case RES_TABLE_OVERLAYABLE_TYPE: {
+ const ResTable_overlayable_header* header =
+ child_chunk.header<ResTable_overlayable_header>();
+ if (header == nullptr) {
+ LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
+ return {};
+ }
+
+ // Iterate over the overlayable policy chunks
+ ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
+ while (overlayable_iter.HasNext()) {
+ const Chunk overlayable_child_chunk = overlayable_iter.Next();
+
+ switch (overlayable_child_chunk.type()) {
+ case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
+ const ResTable_overlayable_policy_header* policy_header =
+ overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
+ if (policy_header == nullptr) {
+ LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
+ return {};
+ }
+
+ if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
+ < dtohl(policy_header->entry_count)) {
+ LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
+ return {};
+ }
+
+ // Retrieve all the ids belonging to this policy
+ std::unordered_set<uint32_t> ids;
+ const auto ids_begin =
+ reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
+ const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
+ for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
+ ids.insert(dtohl(id_iter->ident));
+ }
+
+ // Add the pairing of overlayable properties to resource ids to the package
+ OverlayableInfo overlayable_info;
+ overlayable_info.policy_flags = policy_header->policy_flags;
+ loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
+ break;
+ }
+
+ default:
+ LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+ break;
+ }
+ }
+
+ if (overlayable_iter.HadError()) {
+ LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s",
+ overlayable_iter.GetLastError().c_str());
+ if (overlayable_iter.HadFatalError()) {
+ return {};
+ }
+ }
} break;
default:
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index f7fb89e..63b2527 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -7035,40 +7035,70 @@
return NO_ERROR;
}
-struct IdmapMatchingResources {
- void Add(uint32_t targetResId, uint32_t overlayResId) {
- uint8_t targetTypeid = Res_GETTYPE(targetResId);
- if (typeMappings.find(targetTypeid) == typeMappings.end()) {
- typeMappings.emplace(targetTypeid, std::set<std::pair<uint32_t, uint32_t>>());
+class IdmapMatchingResources;
+
+class IdmapTypeMapping {
+public:
+ void add(uint32_t targetResId, uint32_t overlayResId) {
+ uint8_t targetTypeId = Res_GETTYPE(targetResId);
+ if (mData.find(targetTypeId) == mData.end()) {
+ mData.emplace(targetTypeId, std::set<std::pair<uint32_t, uint32_t>>());
}
- auto& entries = typeMappings[targetTypeid];
+ auto& entries = mData[targetTypeId];
entries.insert(std::make_pair(targetResId, overlayResId));
}
- void FixPadding() {
- for (auto ti = typeMappings.cbegin(); ti != typeMappings.cend(); ++ti) {
- uint32_t last_seen = 0xffffffff;
- size_t total_entries = 0;
+ bool empty() const {
+ return mData.empty();
+ }
+
+private:
+ // resource type ID in context of target -> set of resource entries mapping target -> overlay
+ std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> mData;
+
+ friend IdmapMatchingResources;
+};
+
+class IdmapMatchingResources {
+public:
+ IdmapMatchingResources(std::unique_ptr<IdmapTypeMapping> tm) : mTypeMapping(std::move(tm)) {
+ assert(mTypeMapping);
+ for (auto ti = mTypeMapping->mData.cbegin(); ti != mTypeMapping->mData.cend(); ++ti) {
+ uint32_t lastSeen = 0xffffffff;
+ size_t totalEntries = 0;
for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) {
- assert(last_seen == 0xffffffff || last_seen < ei->first);
- entryPadding[ei->first] = (last_seen == 0xffffffff) ? 0 : ei->first - last_seen - 1;
- last_seen = ei->first;
- total_entries += 1 + entryPadding[ei->first];
+ assert(lastSeen == 0xffffffff || lastSeen < ei->first);
+ mEntryPadding[ei->first] = (lastSeen == 0xffffffff) ? 0 : ei->first - lastSeen - 1;
+ lastSeen = ei->first;
+ totalEntries += 1 + mEntryPadding[ei->first];
}
- numberOfEntriesIncludingPadding[ti->first] = total_entries;
+ mNumberOfEntriesIncludingPadding[ti->first] = totalEntries;
}
}
+ const std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>>& getTypeMapping() const {
+ return mTypeMapping->mData;
+ }
+
+ size_t getNumberOfEntriesIncludingPadding(uint8_t type) const {
+ return mNumberOfEntriesIncludingPadding.at(type);
+ }
+
+ size_t getPadding(uint32_t resid) const {
+ return mEntryPadding.at(resid);
+ }
+
+private:
// resource type ID in context of target -> set of resource entries mapping target -> overlay
- std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> typeMappings;
+ const std::unique_ptr<IdmapTypeMapping> mTypeMapping;
// resource ID in context of target -> trailing padding for that resource (call FixPadding
// before use)
- std::map<uint32_t, size_t> entryPadding;
+ std::map<uint32_t, size_t> mEntryPadding;
// resource type ID in context of target -> total number of entries, including padding entries,
// for that type (call FixPadding before use)
- std::map<uint8_t, size_t> numberOfEntriesIncludingPadding;
+ std::map<uint8_t, size_t> mNumberOfEntriesIncludingPadding;
};
status_t ResTable::createIdmap(const ResTable& targetResTable,
@@ -7098,7 +7128,8 @@
return UNKNOWN_ERROR;
}
- const ResTable_package* targetPackageStruct = targetResTable.mPackageGroups[0]->packages[0]->package;
+ const ResTable_package* targetPackageStruct =
+ targetResTable.mPackageGroups[0]->packages[0]->package;
const size_t tmpNameSize = arraysize(targetPackageStruct->name);
char16_t tmpName[tmpNameSize];
strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize);
@@ -7106,11 +7137,8 @@
const PackageGroup* packageGroup = mPackageGroups[0];
- // the number of resources overlaid that were not explicitly marked overlayable
- size_t forcedOverlayCount = 0u;
-
// find the resources that exist in both packages
- IdmapMatchingResources matchingResources;
+ auto typeMapping = std::make_unique<IdmapTypeMapping>();
for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
const TypeList& typeList = packageGroup->types[typeIndex];
if (typeList.isEmpty()) {
@@ -7139,29 +7167,25 @@
continue;
}
- if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
- ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
- ++forcedOverlayCount;
- }
-
- matchingResources.Add(target_resid, overlay_resid);
+ typeMapping->add(target_resid, overlay_resid);
}
}
- if (matchingResources.typeMappings.empty()) {
+ if (typeMapping->empty()) {
ALOGE("idmap: no matching resources");
return UNKNOWN_ERROR;
}
- matchingResources.FixPadding();
+ const IdmapMatchingResources matchingResources(std::move(typeMapping));
// write idmap
*outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc
*outSize += 2 * sizeof(uint16_t); // target package id, type count
- const auto typesEnd = matchingResources.typeMappings.cend();
- for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) {
+ auto fixedTypeMapping = matchingResources.getTypeMapping();
+ const auto typesEnd = fixedTypeMapping.cend();
+ for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
*outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset
- *outSize += matchingResources.numberOfEntriesIncludingPadding[ti->first] *
+ *outSize += matchingResources.getNumberOfEntriesIncludingPadding(ti->first) *
sizeof(uint32_t); // entries
}
if ((*outData = malloc(*outSize)) == NULL) {
@@ -7190,11 +7214,11 @@
uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
*typeData++ = htods(targetPackageStruct->id); // write: target package id
*typeData++ =
- htods(static_cast<uint16_t>(matchingResources.typeMappings.size())); // write: type count
+ htods(static_cast<uint16_t>(fixedTypeMapping.size())); // write: type count
// write idmap data
- for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) {
- const size_t entryCount = matchingResources.numberOfEntriesIncludingPadding[ti->first];
+ for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
+ const size_t entryCount = matchingResources.getNumberOfEntriesIncludingPadding(ti->first);
auto ei = ti->second.cbegin();
*typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id
*typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id
@@ -7202,7 +7226,7 @@
*typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset
uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData);
for (; ei != ti->second.cend(); ++ei) {
- const size_t padding = matchingResources.entryPadding[ei->first];
+ const size_t padding = matchingResources.getPadding(ei->first);
for (size_t i = 0; i < padding; ++i) {
*entryData++ = htodl(0xffffffff); // write: padding
}
@@ -7211,10 +7235,6 @@
typeData += entryCount * 2;
}
- if (forcedOverlayCount > 0) {
- ALOGW("idmap: overlaid %zu resources not marked overlayable", forcedOverlayCount);
- }
-
return NO_ERROR;
}
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index 99a52dc..a0f2343 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -89,7 +89,9 @@
len_(len),
last_error_(nullptr) {
CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
- VerifyNextChunk();
+ if (len_ != 0) {
+ VerifyNextChunk();
+ }
}
Chunk Next();
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 349b379..8c5c3b7 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -20,6 +20,7 @@
#include <memory>
#include <set>
#include <vector>
+#include <unordered_set>
#include "android-base/macros.h"
@@ -76,6 +77,10 @@
// TypeSpecPtr is a managed pointer that knows how to delete itself.
using TypeSpecPtr = util::unique_cptr<TypeSpec>;
+struct OverlayableInfo {
+ uint32_t policy_flags;
+};
+
class LoadedPackage {
public:
class iterator {
@@ -216,6 +221,18 @@
}
}
+ // Retrieve the overlayable properties of the specified resource. If the resource is not
+ // overlayable, this will return a null pointer.
+ const OverlayableInfo* GetOverlayableInfo(uint32_t resid) const {
+ for (const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>& overlayable_info_ids
+ : overlayable_infos_) {
+ if (overlayable_info_ids.second.find(resid) != overlayable_info_ids.second.end()) {
+ return &overlayable_info_ids.first;
+ }
+ }
+ return nullptr;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
@@ -233,6 +250,7 @@
ByteBucketArray<TypeSpecPtr> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
+ std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
};
// Read-only view into a resource table. This class validates all data
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index ad33fcf..91261aa 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -234,7 +234,9 @@
RES_TABLE_PACKAGE_TYPE = 0x0200,
RES_TABLE_TYPE_TYPE = 0x0201,
RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
- RES_TABLE_LIBRARY_TYPE = 0x0203
+ RES_TABLE_LIBRARY_TYPE = 0x0203,
+ RES_TABLE_OVERLAYABLE_TYPE = 0x0204,
+ RES_TABLE_OVERLAYABLE_POLICY_TYPE = 0x0205,
};
/**
@@ -1354,10 +1356,6 @@
enum : uint32_t {
// Additional flag indicating an entry is public.
SPEC_PUBLIC = 0x40000000u,
-
- // Additional flag indicating an entry is overlayable at runtime.
- // Added in Android-P.
- SPEC_OVERLAYABLE = 0x80000000u,
};
};
@@ -1607,6 +1605,49 @@
uint16_t packageName[128];
};
+/**
+ * Specifies the set of resources that are explicitly allowed to be overlaid by RROs.
+ */
+struct ResTable_overlayable_header
+{
+ struct ResChunk_header header;
+};
+
+/**
+ * Holds a list of resource ids that are protected from being overlaid by a set of policies. If
+ * the overlay fulfils at least one of the policies, then the overlay can overlay the list of
+ * resources.
+ */
+struct ResTable_overlayable_policy_header
+{
+ struct ResChunk_header header;
+
+ enum PolicyFlags {
+ // Any overlay can overlay these resources.
+ POLICY_PUBLIC = 0x00000001,
+
+ // The overlay must reside of the system partition or must have existed on the system partition
+ // before an upgrade to overlay these resources.
+ POLICY_SYSTEM_PARTITION = 0x00000002,
+
+ // The overlay must reside of the vendor partition or must have existed on the vendor partition
+ // before an upgrade to overlay these resources.
+ POLICY_VENDOR_PARTITION = 0x00000004,
+
+ // The overlay must reside of the product partition or must have existed on the product
+ // partition before an upgrade to overlay these resources.
+ POLICY_PRODUCT_PARTITION = 0x00000008,
+
+ // The overlay must reside of the product services partition or must have existed on the product
+ // services partition before an upgrade to overlay these resources.
+ POLICY_PRODUCT_SERVICES_PARTITION = 0x00000010,
+ };
+ uint32_t policy_flags;
+
+ // The number of ResTable_ref that follow this header.
+ uint32_t entry_count;
+};
+
struct alignas(uint32_t) Idmap_header {
// Always 0x504D4449 ('IDMP')
uint32_t magic;
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index ffa4836..441356b 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -22,12 +22,14 @@
#include "TestHelpers.h"
#include "data/basic/R.h"
#include "data/libclient/R.h"
+#include "data/overlayable/R.h"
#include "data/sparse/R.h"
#include "data/styles/R.h"
namespace app = com::android::app;
namespace basic = com::android::basic;
namespace libclient = com::android::libclient;
+namespace overlayable = com::android::overlayable;
namespace sparse = com::android::sparse;
using ::android::base::ReadFileToString;
@@ -273,10 +275,44 @@
ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
}
-// structs with size fields (like Res_value, ResTable_entry) should be
-// backwards and forwards compatible (aka checking the size field against
-// sizeof(Res_value) might not be backwards compatible.
-TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+TEST(LoadedArscTest, LoadOverlayable) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
+ "resources.arsc", &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
+ false /*load_as_shared_library*/);
+
+ ASSERT_THAT(loaded_arsc, NotNull());
+ const LoadedPackage* package = loaded_arsc->GetPackageById(
+ get_package_id(overlayable::R::string::not_overlayable));
+
+ const OverlayableInfo* info = package->GetOverlayableInfo(
+ overlayable::R::string::not_overlayable);
+ ASSERT_THAT(info, IsNull());
+
+ info = package->GetOverlayableInfo(overlayable::R::string::overlayable1);
+ ASSERT_THAT(info, NotNull());
+ EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+
+ info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
+ ASSERT_THAT(info, NotNull());
+ EXPECT_THAT(info->policy_flags,
+ Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
+ | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+
+ info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
+ ASSERT_THAT(info, NotNull());
+ EXPECT_THAT(info->policy_flags,
+ Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
+ | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION
+ | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+
+ info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
+ ASSERT_THAT(info, NotNull());
+ EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+}
TEST(LoadedArscTest, ResourceIdentifierIterator) {
std::string contents;
@@ -326,4 +362,9 @@
ASSERT_EQ(end, iter);
}
+// structs with size fields (like Res_value, ResTable_entry) should be
+// backwards and forwards compatible (aka checking the size field against
+// sizeof(Res_value) might not be backwards compatible.
+TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+
} // namespace android
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index 33f9611..d37874d 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/packages/SystemUI/res/anim/system_in.xml b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml
similarity index 69%
rename from packages/SystemUI/res/anim/system_in.xml
rename to libs/androidfw/tests/data/overlayable/AndroidManifest.xml
index 630fd72..abc2a45 100644
--- a/packages/SystemUI/res/anim/system_in.xml
+++ b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- 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.
@@ -14,9 +14,8 @@
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- >
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_longAnimTime"
- />
-</set>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlayable">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h
new file mode 100644
index 0000000..e46e264d
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/R.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef TESTS_DATA_OVERLAYABLE_R_H_
+#define TESTS_DATA_OVERLAYABLE_R_H_
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace overlayable {
+
+struct R {
+ struct string {
+ enum : uint32_t {
+ not_overlayable = 0x7f010000,
+ overlayable1 = 0x7f010001,
+ overlayable2 = 0x7f010002,
+ overlayable3 = 0x7f010003,
+ overlayable4 = 0x7f010004,
+ };
+ };
+};
+
+} // namespace overlayable
+} // namespace android
+} // namespace com
+
+#endif /* TESTS_DATA_OVERLAYABLE_R_H_ */
diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build
new file mode 100755
index 0000000..98fdc51
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/build
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+#
+
+set -e
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
new file mode 100644
index 0000000..85ab4be
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
new file mode 100644
index 0000000..11aa735
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+
+<resources>
+<overlayable>
+ <!-- Any overlay can overlay the value of @string/overlayable1 -->
+ <item type="string" name="overlayable1" />
+
+ <!-- Any overlay on the product or system partition can overlay the value of
+ @string/overlayable2 -->
+ <policy type="product|system">
+ <item type="string" name="overlayable2" />
+ </policy>
+
+ <!-- Any overlay can overlay the value of @string/overlayable4 -->
+ <policy type="public">
+ <item type="string" name="overlayable4" />
+ </policy>
+</overlayable>
+
+<overlayable>
+ <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of
+ @string/overlayable3 -->
+ <policy type="product_services|vendor|product">
+ <item type="string" name="overlayable3" />
+ </policy>
+</overlayable>
+</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml
new file mode 100644
index 0000000..5676d7c
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<resources>
+ <public type="string" name="not_overlayable" id="0x7f010000" />
+ <public type="string" name="overlayable1" id="0x7f010001" />
+ <public type="string" name="overlayable2" id="0x7f010002" />
+ <public type="string" name="overlayable3" id="0x7f010003" />
+ <public type="string" name="overlayable4" id="0x7f010004" />
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/system_in.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml
similarity index 61%
copy from packages/SystemUI/res/anim/system_in.xml
copy to libs/androidfw/tests/data/overlayable/res/values/values.xml
index 630fd72..a86b312 100644
--- a/packages/SystemUI/res/anim/system_in.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- 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.
@@ -14,9 +14,10 @@
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- >
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_longAnimTime"
- />
-</set>
+<resources>
+ <string name="not_overlayable">Not overlayable</string>
+ <string name="overlayable1">Overlayable One</string>
+ <string name="overlayable2">Overlayable Two</string>
+ <string name="overlayable3">Overlayable Three</string>
+ <string name="overlayable4">Overlayable Four</string>
+</resources>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 17d2db71..da77b99 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -9,8 +9,6 @@
"hwui_lto",
],
- cpp_std: "c++17",
-
cflags: [
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
@@ -45,9 +43,6 @@
],
product_variables: {
- device_uses_hwc2: {
- cflags: ["-DUSE_HWC2"],
- },
eng: {
lto: {
never: true,
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index 06e937a..0cfaa8c 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -146,4 +146,4 @@
return shouldInvert;
}
-}; // namespace android::uirenderer
\ No newline at end of file
+} // namespace android::uirenderer
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index b772e5b..3bee301 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -85,19 +85,18 @@
mUpdateTexImage = false;
sk_sp<SkImage> layerImage;
SkMatrix textureTransform;
- android_dataspace dataSpace;
bool queueEmpty = true;
// If the SurfaceTexture queue is in synchronous mode, need to discard all
// but latest frame. Since we can't tell which mode it is in,
// do this unconditionally.
do {
- layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty,
+ layerImage = mSurfaceTexture->dequeueImage(textureTransform, &queueEmpty,
mRenderState);
} while (layerImage.get() && (!queueEmpty));
if (layerImage.get()) {
// force filtration if buffer size != layer size
bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height();
- updateLayer(forceFilter, textureTransform, dataSpace, layerImage);
+ updateLayer(forceFilter, textureTransform, layerImage);
}
}
@@ -109,12 +108,11 @@
}
void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
- android_dataspace dataspace, const sk_sp<SkImage>& layerImage) {
+ const sk_sp<SkImage>& layerImage) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
mLayer->getTexTransform() = textureTransform;
- mLayer->setDataSpace(dataspace);
mLayer->setImage(layerImage);
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index b2c5131..a91c111 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -95,7 +95,7 @@
void detachSurfaceTexture();
void updateLayer(bool forceFilter, const SkMatrix& textureTransform,
- android_dataspace dataspace, const sk_sp<SkImage>& layerImage);
+ const sk_sp<SkImage>& layerImage);
void destroyLayer();
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index a952cc2..dc63e5d 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -31,5 +31,5 @@
*/
using DisplayList = skiapipeline::SkiaDisplayList;
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index ba72e93..237fc62 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -26,5 +26,5 @@
virtual void notify(const int64_t* buffer);
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index d920a99..75b8038 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -56,5 +56,5 @@
std::vector<sp<FrameMetricsObserver> > mObservers;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/GlFunctorLifecycleListener.h b/libs/hwui/GlFunctorLifecycleListener.h
index 5d07b46..5adc469 100644
--- a/libs/hwui/GlFunctorLifecycleListener.h
+++ b/libs/hwui/GlFunctorLifecycleListener.h
@@ -28,5 +28,5 @@
virtual void onGlFunctorReleased(Functor* functor) = 0;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 165fc48..a97c12c 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -256,4 +256,4 @@
return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
}
-}; // namespace android::uirenderer
+} // namespace android::uirenderer
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index c0113d8..6298013 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -25,4 +25,4 @@
static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
};
-}; // namespace android::uirenderer
+} // namespace android::uirenderer
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e7ae767..ccbb6c1 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -82,7 +82,6 @@
JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) {
mGlobalData = globalData;
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
-#if USE_HWC2
nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms);
nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset;
// There are two different offset cases. If the offsetDelta is positive
@@ -96,7 +95,6 @@
// return due to the staggering of VSYNC-app & VSYNC-sf.
mDequeueTimeForgiveness = offsetDelta + 4_ms;
}
-#endif
setFrameInterval(frameIntervalNanos);
}
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 32aaa54..a15ff22 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -33,7 +33,6 @@
// TODO: This is a violation of Android's typical ref counting, but it
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
- buildColorSpaceWithFilter();
renderState.registerLayer(this);
texTransform.setIdentity();
transform.setIdentity();
@@ -43,36 +42,6 @@
mRenderState.unregisterLayer(this);
}
-void Layer::setColorFilter(sk_sp<SkColorFilter> filter) {
- if (filter != mColorFilter) {
- mColorFilter = filter;
- buildColorSpaceWithFilter();
- }
-}
-
-void Layer::setDataSpace(android_dataspace dataspace) {
- if (dataspace != mCurrentDataspace) {
- mCurrentDataspace = dataspace;
- buildColorSpaceWithFilter();
- }
-}
-
-void Layer::buildColorSpaceWithFilter() {
- sk_sp<SkColorFilter> colorSpaceFilter;
- sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(mCurrentDataspace);
- if (colorSpace && !colorSpace->isSRGB()) {
- colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
- }
-
- if (mColorFilter && colorSpaceFilter) {
- mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter);
- } else if (colorSpaceFilter) {
- mColorSpaceWithFilter = colorSpaceFilter;
- } else {
- mColorSpaceWithFilter = mColorFilter;
- }
-}
-
void Layer::postDecStrong() {
mRenderState.postDecStrong(this);
}
@@ -85,5 +54,5 @@
}
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e4f96e9..ea3bfc9 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -69,15 +69,9 @@
SkBlendMode getMode() const;
- inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
+ inline sk_sp<SkColorFilter> getColorFilter() const { return mColorFilter; }
- void setColorFilter(sk_sp<SkColorFilter> filter);
-
- void setDataSpace(android_dataspace dataspace);
-
- void setColorSpace(sk_sp<SkColorSpace> colorSpace);
-
- inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
+ void setColorFilter(sk_sp<SkColorFilter> filter) { mColorFilter = filter; };
inline SkMatrix& getTexTransform() { return texTransform; }
@@ -98,24 +92,12 @@
RenderState& mRenderState;
private:
- void buildColorSpaceWithFilter();
-
/**
* Color filter used to draw this layer. Optional.
*/
sk_sp<SkColorFilter> mColorFilter;
/**
- * Colorspace of the contents of the layer. Optional.
- */
- android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN;
-
- /**
- * A color filter that is the combination of the mColorFilter and mColorSpace. Optional.
- */
- sk_sp<SkColorFilter> mColorSpaceWithFilter;
-
- /**
* Indicates raster data backing the layer is scaled, requiring filtration.
*/
bool forceFilter = false;
@@ -162,5 +144,5 @@
}; // struct Layer
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index 6857999..2c63af6 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -50,7 +50,7 @@
std::vector<Entry> mEntries;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
diff --git a/libs/hwui/Lighting.h b/libs/hwui/Lighting.h
index d972c21..ccfbb93 100644
--- a/libs/hwui/Lighting.h
+++ b/libs/hwui/Lighting.h
@@ -34,5 +34,5 @@
uint8_t spotShadowAlpha;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index d84ed32..d0dbff03 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -526,5 +526,5 @@
ALOGD("]");
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 1b5cb60..b33cfe2 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -245,5 +245,5 @@
typedef Matrix4 mat4;
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
index 082e95f..86d3cb9 100644
--- a/libs/hwui/NinePatchUtils.h
+++ b/libs/hwui/NinePatchUtils.h
@@ -103,5 +103,5 @@
}
}
-}; // namespace NinePatchUtils
-}; // namespace android
+} // namespace NinePatchUtils
+} // namespace android
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index ad599e9..808921d 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -304,5 +304,5 @@
return;
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index 474eb97..f5bebce 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -46,6 +46,6 @@
static void validateVerbAndPoints(char verb, size_t points, ParseResult* result);
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_PATHPARSER_H
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 3f2c616..4a3e10c 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -223,5 +223,5 @@
sRenderPipelineType = type;
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 542bc71..da53f66 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -289,7 +289,7 @@
static RenderPipelineType sRenderPipelineType;
}; // class Caches
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_PROPERTIES_H
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 80f2b57..2a48837 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -66,13 +66,10 @@
sk_sp<SkColorSpace> colorSpace =
DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
- sk_sp<SkColorFilter> colorSpaceFilter;
- if (colorSpace && !colorSpace->isSRGB()) {
- colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
- }
sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
- reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), kPremul_SkAlphaType);
- return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap);
+ reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()),
+ kPremul_SkAlphaType, colorSpace);
+ return copyImageInto(image, texTransform, srcRect, bitmap);
}
CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -83,20 +80,7 @@
transform.loadScale(1, -1, 1);
transform.translate(0, -1);
- // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage".
- // TODO: When this was attempted, it resulted in instability.
- sk_sp<SkColorFilter> colorSpaceFilter;
- sk_sp<SkColorSpace> colorSpace = hwBitmap->info().refColorSpace();
- if (colorSpace && !colorSpace->isSRGB()) {
- colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
- }
- sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
- reinterpret_cast<AHardwareBuffer*>(hwBitmap->graphicBuffer()), kPremul_SkAlphaType);
-
- // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format
- // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for
- // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test.
- return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap);
+ return copyImageInto(hwBitmap->makeImage(), transform, srcRect, bitmap);
}
CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
@@ -118,8 +102,7 @@
return copyResult;
}
-CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image,
- sk_sp<SkColorFilter>& colorSpaceFilter, Matrix4& texTransform,
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
const Rect& srcRect, SkBitmap* bitmap) {
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
@@ -157,11 +140,7 @@
return copyResult;
}
- // See Readback::copyLayerInto for an overview of color space conversion.
- // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder).
- // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion.
- // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally.
- Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc);
+ Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc);
bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
layer.setForceFilter(!disableFilter);
@@ -177,38 +156,6 @@
bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
SkBitmap* bitmap) {
- /*
- * In the past only TextureView readback was setting the temporary surface color space to null.
- * Now all 3 readback flows are drawing into a SkSurface with null color space.
- * At readback there are 3 options to convert the source image color space to the destination
- * color space requested in "bitmap->info().colorSpace()":
- * 1. Set color space for temporary surface render target to null (disables color management),
- * colorspace tag from source SkImage is ignored by Skia,
- * convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter,
- * do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2",
- * read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion.
- *
- * 2. Set color space for temporary surface render target to SRGB (not nullptr),
- * colorspace tag on the source SkImage is used by Skia to enable conversion,
- * convert SkImage to SRGB at draw time with drawImage (no filters),
- * do a readback from temporary SkSurface, which will do a color conversion from SRGB to
- * bitmap->info().colorSpace() on the CPU.
- *
- * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(),
- * colorspace tag on the source SkImage is used by Skia to enable conversion,
- * convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters),
- * do a readback from SkSurface, which will not do any color conversion, because
- * surface was created with the same color space as the "bitmap".
- *
- * Option 1 is used for all readback flows.
- * Options 2 and 3 are new, because skia added support for non-SRGB render targets without
- * linear blending.
- * TODO: evaluate if options 2 or 3 for color space conversion are better.
- */
-
- // drop the colorSpace from the temporary surface.
- SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
-
/* This intermediate surface is present to work around a bug in SwiftShader that
* prevents us from reading the contents of the layer's texture directly. The
* workaround involves first rendering that texture into an intermediate buffer and
@@ -217,70 +164,44 @@
* with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
*/
sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- SkBudgeted::kYes, surfaceInfo);
+ SkBudgeted::kYes, bitmap->info());
+ // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we
+ // attempt to do the intermediate rendering step in 8888
if (!tmpSurface.get()) {
- surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
+ SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
- surfaceInfo);
+ tmpInfo);
if (!tmpSurface.get()) {
- ALOGW("Unable to readback GPU contents into the provided bitmap");
+ ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
return false;
}
}
- if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
- tmpSurface->getCanvas(), layer, srcRect, dstRect,
- false)) {
- // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on
- // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has
- // disabled color conversion.
- SkColorSpace* destColorSpace = bitmap->info().colorSpace();
- SkBitmap tempSRGBBitmap;
- SkBitmap tmpN32Bitmap;
- SkBitmap* bitmapInSRGB;
- if (destColorSpace && !destColorSpace->isSRGB()) {
- tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB()));
- bitmapInSRGB = &tempSRGBBitmap; // Need to convert latter from SRGB to non-SRGB.
- } else {
- bitmapInSRGB = bitmap; // No need for color conversion - write directly into output.
- }
- bool success = false;
+ if (!skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
+ tmpSurface->getCanvas(), layer, srcRect, dstRect,
+ false)) {
+ ALOGW("Unable to draw content from GPU into the provided bitmap");
+ return false;
+ }
- // TODO: does any of the readbacks below clamp F16 exSRGB?
- // Readback into a SRGB SkBitmap.
- if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(),
- bitmapInSRGB->rowBytes(), 0, 0)) {
- success = true;
- } else {
- // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
- // 8888 and then convert that into the destination format before giving up.
- SkImageInfo bitmapInfo =
- SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(),
- SkColorSpace::MakeSRGB());
- if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) &&
- tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(),
- tmpN32Bitmap.rowBytes(), 0, 0)) {
- success = true;
- bitmapInSRGB = &tmpN32Bitmap;
- }
- }
-
- if (success) {
- if (bitmapInSRGB != bitmap) {
- // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to
- // destination bitmap color format if needed.
- if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(),
- bitmap->rowBytes(), 0, 0)) {
- return false;
- }
- }
- bitmap->notifyPixelsChanged();
- return true;
+ if (!tmpSurface->readPixels(*bitmap, 0, 0)) {
+ // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
+ // 8888 and then convert that into the destination format before giving up.
+ SkBitmap tmpBitmap;
+ SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
+ if (bitmap->info().colorType() == SkColorType::kN32_SkColorType ||
+ !tmpBitmap.tryAllocPixels(tmpInfo) ||
+ !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
+ !tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(),
+ bitmap->rowBytes(), 0, 0)) {
+ ALOGW("Unable to convert content into the provided bitmap");
+ return false;
}
}
- return false;
+ bitmap->notifyPixelsChanged();
+ return true;
}
} /* namespace uirenderer */
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index d9e10ce..e86a813 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -54,8 +54,8 @@
CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
private:
- CopyResult copyImageInto(const sk_sp<SkImage>& image, sk_sp<SkColorFilter>& colorSpaceFilter,
- Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap);
+ CopyResult copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
+ const Rect& srcRect, SkBitmap* bitmap);
bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
SkBitmap* bitmap);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index f928de9..c63e449 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -1028,5 +1028,5 @@
fDL->drawVectorDrawable(tree);
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 099e0be..08cfc62 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -216,5 +216,5 @@
DisplayListData* fDL;
};
-}; // namespace uirenderer
-}; // namespace android
\ No newline at end of file
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 0715187..d6362ef 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -262,5 +262,5 @@
}
}; // class Rect
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d2a8f02..4a63910 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -29,6 +29,7 @@
#include <SkPathOps.h>
#include <algorithm>
+#include <atomic>
#include <sstream>
#include <string>
@@ -47,8 +48,14 @@
TreeInfo* mTreeInfo;
};
+static int64_t generateId() {
+ static std::atomic<int64_t> sNextId{1};
+ return sNextId++;
+}
+
RenderNode::RenderNode()
- : mDirtyPropertyFields(0)
+ : mUniqueId(generateId())
+ , mDirtyPropertyFields(0)
, mNeedsDisplayListSync(false)
, mDisplayList(nullptr)
, mStagingDisplayList(nullptr)
@@ -444,5 +451,38 @@
return &mClippedOutlineCache.clippedOutline;
}
+using StringBuffer = FatVector<char, 128>;
+
+template <typename... T>
+static void format(StringBuffer& buffer, const std::string_view& format, T... args) {
+ buffer.resize(buffer.capacity());
+ while (1) {
+ int needed = snprintf(buffer.data(), buffer.size(),
+ format.data(), std::forward<T>(args)...);
+ if (needed < 0) {
+ buffer[0] = '\0';
+ buffer.resize(1);
+ return;
+ }
+ if (needed < buffer.size()) {
+ buffer.resize(needed + 1);
+ return;
+ }
+ buffer.resize(buffer.size() * 2);
+ }
+}
+
+void RenderNode::markDrawStart(SkCanvas& canvas) {
+ StringBuffer buffer;
+ format(buffer, "RenderNode(id=%d, name='%s')", uniqueId(), getName());
+ canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
+}
+
+void RenderNode::markDrawEnd(SkCanvas& canvas) {
+ StringBuffer buffer;
+ format(buffer, "/RenderNode(id=%d, name='%s')", uniqueId(), getName());
+ canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index be0b46b..6060123 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -213,6 +213,11 @@
UsageHint usageHint() const { return mUsageHint; }
+ int64_t uniqueId() const { return mUniqueId; }
+
+ void markDrawStart(SkCanvas& canvas);
+ void markDrawEnd(SkCanvas& canvas);
+
private:
void computeOrderingImpl(RenderNodeOp* opState,
std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
@@ -233,6 +238,7 @@
void incParentRefCount() { mParentCount++; }
void decParentRefCount(TreeObserver& observer, TreeInfo* info = nullptr);
+ const int64_t mUniqueId;
String8 mName;
sp<VirtualLightRefBase> mUserContext;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9a15ff2..6be7ef7 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -27,7 +27,6 @@
#include <SkAnimatedImage.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
-#include <SkColorSpaceXformCanvas.h>
#include <SkDeque.h>
#include <SkDrawable.h>
#include <SkFont.h>
@@ -61,19 +60,8 @@
SkiaCanvas::SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {}
SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
- sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
- mCanvasOwned =
- std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
- if (cs.get() == nullptr || cs->isSRGB()) {
- mCanvas = mCanvasOwned.get();
- } else {
- /** The wrapper is needed if we are drawing into a non-sRGB destination, since
- * we need to transform all colors (not just bitmaps via filters) into the
- * destination's colorspace.
- */
- mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), std::move(cs));
- mCanvas = mCanvasWrapper.get();
- }
+ mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
+ mCanvas = mCanvasOwned.get();
}
SkiaCanvas::~SkiaCanvas() {}
@@ -82,7 +70,6 @@
if (mCanvas != skiaCanvas) {
mCanvas = skiaCanvas;
mCanvasOwned.reset();
- mCanvasWrapper.reset();
}
mSaveStack.reset(nullptr);
}
@@ -92,18 +79,9 @@
// ----------------------------------------------------------------------------
void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
- sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
- std::unique_ptr<SkCanvas> newCanvas =
- std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
- std::unique_ptr<SkCanvas> newCanvasWrapper;
- if (cs.get() != nullptr && !cs->isSRGB()) {
- newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs));
- }
-
// deletes the previously owned canvas (if any)
- mCanvasOwned = std::move(newCanvas);
- mCanvasWrapper = std::move(newCanvasWrapper);
- mCanvas = mCanvasWrapper ? mCanvasWrapper.get() : mCanvasOwned.get();
+ mCanvasOwned.reset(new SkCanvas(bitmap));
+ mCanvas = mCanvasOwned.get();
// clean up the old save stack
mSaveStack.reset(nullptr);
@@ -548,40 +526,14 @@
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
-SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint,
- sk_sp<SkColorFilter> colorSpaceFilter) const {
- /* We don't apply the colorSpace filter if this canvas is already wrapped with
- * a SkColorSpaceXformCanvas since it already takes care of converting the
- * contents of the bitmap into the appropriate colorspace. The mCanvasWrapper
- * should only be used if this canvas is backed by a surface/bitmap that is known
- * to have a non-sRGB colorspace.
- */
- if (!mCanvasWrapper && colorSpaceFilter) {
- SkPaint& tmpPaint = paint.writeable();
- if (tmpPaint.getColorFilter()) {
- tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(),
- std::move(colorSpaceFilter)));
- LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
- } else {
- tmpPaint.setColorFilter(std::move(colorSpaceFilter));
- }
- }
- return filterPaint(std::move(paint));
-}
-
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
+ mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
-
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
+ mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
@@ -590,9 +542,7 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+ mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint),
SkCanvas::kFast_SrcRectConstraint);
}
@@ -674,13 +624,9 @@
PaintCoW paintCoW(paint);
SkPaint& tmpPaint = paintCoW.writeable();
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ sk_sp<SkImage> image = bitmap.makeImage();
sk_sp<SkShader> shader =
image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
- if (colorFilter) {
- shader = shader->makeWithColorFilter(std::move(colorFilter));
- }
tmpPaint.setShader(std::move(shader));
mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
@@ -711,10 +657,7 @@
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImageLattice(image.get(), lattice, dst,
- filterBitmap(paint, std::move(colorFilter)));
+ mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint));
}
double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
@@ -738,7 +681,7 @@
if (mPaintFilter) {
mPaintFilter->filter(&paintCopy);
}
- SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+ SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
// Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
// older.
if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0 &&
@@ -765,7 +708,7 @@
if (mPaintFilter) {
mPaintFilter->filter(&paintCopy);
}
- SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+ SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
const int N = end - start;
SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform)));
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3a877cf..24d9c08 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -232,7 +232,6 @@
class Clip;
- std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas
std::unique_ptr<SkCanvas> mCanvasOwned; // might own a canvas we allocated
SkCanvas* mCanvas; // we do NOT own this canvas, it must survive us
// unless it is the same as mCanvasOwned.get()
diff --git a/libs/hwui/UvMapper.h b/libs/hwui/UvMapper.h
index b495e33..833ca4a 100644
--- a/libs/hwui/UvMapper.h
+++ b/libs/hwui/UvMapper.h
@@ -124,7 +124,7 @@
float mMaxV;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_UV_MAPPER_H
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index d2c15ad..e6eea1c 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -113,7 +113,7 @@
}
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_VECTOR_H
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 6cf04bf..dd62bbb 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -691,7 +691,7 @@
return BitmapPalette::Unknown;
}
-}; // namespace VectorDrawable
+} // namespace VectorDrawable
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index f091277..28cabb9 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -73,7 +73,7 @@
REQUIRE_COMPATIBLE_LAYOUT(TextureVertex);
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_VERTEX_H
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 613cf4a..6543a22 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -174,7 +174,7 @@
void (*mCleanupIndexMethod)(void*);
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_VERTEX_BUFFER_H
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 75a6e72..6c77f9e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -32,7 +32,6 @@
#include <SkCanvas.h>
#include <SkImagePriv.h>
-#include <SkToSRGBColorFilter.h>
#include <SkHighContrastFilter.h>
#include <limits>
@@ -287,14 +286,8 @@
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
if (isHardware()) {
- outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
- info().colorType(), info().alphaType(), nullptr));
+ outBitmap->allocPixels(mInfo);
uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
- if (mInfo.colorSpace()) {
- sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
- outBitmap->setInfo(mInfo);
- outBitmap->setPixelRef(std::move(pixelRef), 0, 0);
- }
return;
}
outBitmap->setInfo(mInfo, rowBytes());
@@ -313,7 +306,7 @@
return nullptr;
}
-sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) {
+sk_sp<SkImage> Bitmap::makeImage() {
sk_sp<SkImage> image = mImage;
if (!image) {
SkASSERT(!isHardware());
@@ -325,9 +318,6 @@
// TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
}
- if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
- *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
- }
return image;
}
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 238c764..d446377 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -105,14 +105,8 @@
* Creates or returns a cached SkImage and is safe to be invoked from either
* the UI or RenderThread.
*
- * @param outputColorFilter is a required param that will be populated by
- * this function if the bitmap's colorspace is not sRGB. If populated the
- * filter will convert colors from the bitmaps colorspace into sRGB. It
- * is the callers responsibility to use this colorFilter when drawing
- * this image into any destination that is presumed to be sRGB (i.e. a
- * buffer that has no colorspace defined).
*/
- sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
+ sk_sp<SkImage> makeImage();
static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index e99742b..a5f21d8 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -76,8 +76,8 @@
namespace uirenderer {
namespace VectorDrawable {
class Tree;
-};
-};
+}
+}
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc;
@@ -318,4 +318,4 @@
friend class DrawTextOnPathFunctor;
};
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 769fce4..84292c8 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -43,7 +43,7 @@
static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint,
const minikin::MinikinPaint& paint,
const minikin::FontFakery& fakery) {
- skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ skPaint->setTextEncoding(kGlyphID_SkTextEncoding);
skPaint->setTextSize(paint.size);
skPaint->setTextScaleX(paint.scaleX);
skPaint->setTextSkewX(paint.skewX);
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index efef6de..bf19655 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -79,6 +79,6 @@
sp<uirenderer::CanvasPropertyPaint> mPaint;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index e4ba13d..2062194 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -172,6 +172,6 @@
std::string mIdent;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index 162d137..af3a056 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -48,6 +48,6 @@
const SkRect mBounds;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 90d5e71..4a87e75 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -216,6 +216,6 @@
}
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index b06f7f0..215979c 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -41,6 +41,6 @@
void onDraw(SkCanvas* canvas) override;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 0cd6406..f08ac17 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -89,7 +89,7 @@
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
- paint.setColorFilter(layer->getColorSpaceWithFilter());
+ paint.setColorFilter(layer->getColorFilter());
const bool nonIdentityMatrix = !matrix.isIdentity();
if (nonIdentityMatrix) {
canvas->save();
@@ -145,6 +145,6 @@
return layerImage != nullptr;
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 5c12590..95dc6d0 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -45,6 +45,6 @@
sp<DeferredLayerUpdater> mLayerUpdater;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index ea14d11..4494cb0 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -115,12 +115,26 @@
}
}
+class MarkDraw {
+public:
+ explicit MarkDraw(SkCanvas& canvas, RenderNode& node) : mCanvas(canvas), mNode(node) {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+ mNode.markDrawStart(mCanvas);
+ }
+ }
+ ~MarkDraw() {
+ if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+ mNode.markDrawEnd(mCanvas);
+ }
+ }
+private:
+ SkCanvas& mCanvas;
+ RenderNode& mNode;
+};
+
void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
RenderNode* renderNode = mRenderNode.get();
- if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
- SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
- canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
- }
+ MarkDraw _marker{*canvas, *renderNode};
// We only respect the nothingToDraw check when we are composing a layer. This
// ensures that we paint the layer even if it is not currently visible in the
@@ -318,6 +332,6 @@
}
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index d746978..6ba8e59 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -150,6 +150,6 @@
SkiaDisplayList* mProjectedDisplayList = nullptr;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index dba97fe..0a3c8f4 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -211,6 +211,6 @@
casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
index 26cfa90..cfc0f9b 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -74,6 +74,6 @@
StartReorderBarrierDrawable* mStartBarrier;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 3890513..ac6f6a3 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -141,6 +141,6 @@
mDisplayList.draw(&canvas);
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index ac7bb7b..d7879e7 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -36,7 +36,7 @@
namespace VectorDrawable {
class Tree;
-};
+}
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
namespace skiapipeline {
@@ -179,6 +179,6 @@
SkMatrix mParentMatrix;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 6ae5999..142bca9 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -97,7 +97,7 @@
SkASSERT(mRenderThread.getGrContext() != nullptr);
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType,
- nullptr, &props));
+ mSurfaceColorSpace, &props));
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
@@ -176,6 +176,7 @@
} else if (colorMode == ColorMode::WideColorGamut) {
mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
}
+ mSurfaceColorSpace = SkColorSpace::MakeSRGB();
if (mEglSurface != EGL_NO_SURFACE) {
const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 2dfe7c7..7a255c1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -169,7 +169,7 @@
if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
SkImageInfo info;
info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
- kPremul_SkAlphaType);
+ kPremul_SkAlphaType, getSurfaceColorSpace());
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
@@ -204,8 +204,7 @@
GrContext* context = thread.getGrContext();
if (context) {
ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
- sk_sp<SkColorFilter> colorFilter;
- auto image = bitmap->makeImage(&colorFilter);
+ auto image = bitmap->makeImage();
if (image.get() && !bitmap->isHardware()) {
SkImage_pinAsTexture(image.get(), context);
SkImage_unpinAsTexture(image.get(), context);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 45022e7..b682ab0 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -179,9 +179,8 @@
}
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
+ sk_sp<SkImage> image = bitmap.makeImage();
+ mRecorder.drawImage(image, left, top, filterPaint(paint), bitmap.palette());
// if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
// it is not safe to store a raw SkImage pointer, because the image object will be destroyed
// when this function ends.
@@ -194,9 +193,8 @@
SkAutoCanvasRestore acr(&mRecorder, true);
concat(matrix);
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
+ sk_sp<SkImage> image = bitmap.makeImage();
+ mRecorder.drawImage(image, 0, 0, filterPaint(paint), bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique()) {
mDisplayList->mMutableImages.push_back(image.get());
}
@@ -208,9 +206,8 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+ sk_sp<SkImage> image = bitmap.makeImage();
+ mRecorder.drawImageRect(image, srcRect, dstRect, filterPaint(paint),
SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
!dstRect.isEmpty()) {
@@ -247,10 +244,9 @@
if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
}
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ sk_sp<SkImage> image = bitmap.makeImage();
mRecorder.drawImageLattice(image, lattice, dst,
- filterBitmap(std::move(filteredPaint), std::move(colorFilter)),
+ filterPaint(std::move(filteredPaint)),
bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
@@ -263,6 +259,6 @@
return 0;
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 988728d..d6107a9 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -92,6 +92,6 @@
PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter);
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index a494e49..3607b23 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -122,8 +122,9 @@
mVkSurface = nullptr;
}
+ mSurfaceColorSpace = SkColorSpace::MakeSRGB();
if (surface) {
- mVkSurface = mVkManager.createSurface(surface, colorMode);
+ mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace);
}
if (colorMode == ColorMode::SRGB) {
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index a594206..004a558 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -219,6 +219,6 @@
});
}
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
index 3269cfb..8fe52c5 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -51,6 +51,6 @@
SkImageInfo mFBInfo;
};
-}; // namespace skiapipeline
-}; // namespace uirenderer
-}; // namespace android
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h
index efa9da2..9e1bb8e 100644
--- a/libs/hwui/private/hwui/DrawGlInfo.h
+++ b/libs/hwui/private/hwui/DrawGlInfo.h
@@ -83,7 +83,7 @@
};
}; // struct DrawGlInfo
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_DRAW_GL_INFO_H
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f1a522e..6869972 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -323,25 +323,6 @@
// the deadline for RT animations
info.out.canDrawThisFrame = false;
}
- /* This logic exists to try and recover from a display latch miss, which essentially
- * results in the bufferqueue being double-buffered instead of triple-buffered.
- * SurfaceFlinger itself now tries to handle & recover from this situation, so this
- * logic should no longer be necessary. As it's occasionally triggering when
- * undesired disable it.
- * TODO: Remove this entirely if the results are solid.
- else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
- (latestVsync - mLastDropVsync) < 500_ms) {
- // It's been several frame intervals, assume the buffer queue is fine
- // or the last drop was too recent
- info.out.canDrawThisFrame = true;
- } else {
- info.out.canDrawThisFrame = !isSwapChainStuffed();
- if (!info.out.canDrawThisFrame) {
- // dropping frame
- mLastDropVsync = mRenderThread.timeLord().latestVsync();
- }
- }
- */
} else {
info.out.canDrawThisFrame = true;
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6668c584..d9b789f 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -49,7 +49,7 @@
Reset = 1 << 1,
JankStats = 1 << 2,
};
-};
+}
/*
* RenderProxy is strictly single threaded. All methods must be invoked on the owning
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index d84ec85..2abb3d5 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -263,6 +263,15 @@
tailPNext = &blend->pNext;
}
+ VkPhysicalDeviceSamplerYcbcrConversionFeatures* ycbcrFeature;
+ ycbcrFeature = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*) malloc(
+ sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures));
+ LOG_ALWAYS_FATAL_IF(!ycbcrFeature);
+ ycbcrFeature->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+ ycbcrFeature->pNext = nullptr;
+ *tailPNext = ycbcrFeature;
+ tailPNext = &ycbcrFeature->pNext;
+
// query to get the physical device features
mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
// this looks like it would slow things down,
@@ -463,8 +472,9 @@
window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight);
if (windowWidth != surface->mWindowWidth || windowHeight != surface->mWindowHeight) {
ColorMode colorMode = surface->mColorMode;
+ sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
destroySurface(surface);
- *surfaceOut = createSurface(window, colorMode);
+ *surfaceOut = createSurface(window, colorMode, colorSpace);
surface = *surfaceOut;
}
@@ -638,7 +648,7 @@
imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
surface->mColorMode == ColorMode::WideColorGamut ? kRGBA_F16_SkColorType
- : kRGBA_8888_SkColorType, nullptr, &props);
+ : kRGBA_8888_SkColorType, surface->mColorSpace, &props);
}
SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -735,7 +745,7 @@
surface->mWindowWidth = extent.width;
surface->mWindowHeight = extent.height;
- uint32_t imageCount = caps.minImageCount + 2;
+ uint32_t imageCount = std::max<uint32_t>(3, caps.minImageCount);
if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
// Application must settle for fewer images than desired:
imageCount = caps.maxImageCount;
@@ -824,14 +834,15 @@
return true;
}
-VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode) {
+VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
+ sk_sp<SkColorSpace> surfaceColorSpace) {
initialize();
if (!window) {
return nullptr;
}
- VulkanSurface* surface = new VulkanSurface(colorMode, window);
+ VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace);
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 8594a1b..d67d2c8 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -38,8 +38,8 @@
class VulkanSurface {
public:
- VulkanSurface(ColorMode colorMode, ANativeWindow* window)
- : mColorMode(colorMode), mNativeWindow(window) {}
+ VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace)
+ : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace) {}
sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
@@ -79,6 +79,7 @@
ANativeWindow* mNativeWindow;
int mWindowWidth = 0;
int mWindowHeight = 0;
+ sk_sp<SkColorSpace> mColorSpace;
};
// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -96,7 +97,8 @@
// Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
// VulkanSurface object which is returned.
- VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode);
+ VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
+ sk_sp<SkColorSpace> surfaceColorSpace);
// Destroy the VulkanSurface and all associated vulkan objects.
void destroySurface(VulkanSurface* surface);
diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp
index c8220c6..85b3917 100644
--- a/libs/hwui/surfacetexture/EGLConsumer.cpp
+++ b/libs/hwui/surfacetexture/EGLConsumer.cpp
@@ -672,4 +672,4 @@
return image;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h
index eccb082..7dac3ef 100644
--- a/libs/hwui/surfacetexture/EGLConsumer.h
+++ b/libs/hwui/surfacetexture/EGLConsumer.h
@@ -308,4 +308,4 @@
sp<EglImage> mReleasedTexImage;
};
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 9ffccfb..15aec9f 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -22,6 +22,7 @@
#include "renderthread/EglManager.h"
#include "renderthread/RenderThread.h"
#include "renderthread/VulkanManager.h"
+#include "utils/Color.h"
// Macro for including the SurfaceTexture name in log messages
#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
@@ -44,13 +45,16 @@
mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
}
-void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer) {
- if (!mImage.get()) {
+void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
+ android_dataspace dataspace) {
+ if (!mImage.get() || dataspace != mDataspace) {
mImage = graphicBuffer.get()
? SkImage::MakeFromAHardwareBuffer(
reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
- kPremul_SkAlphaType)
+ kPremul_SkAlphaType,
+ uirenderer::DataSpaceToColorSpace(dataspace))
: nullptr;
+ mDataspace = dataspace;
}
}
@@ -66,7 +70,7 @@
int slot = st.mCurrentTexture;
if (slot != BufferItem::INVALID_BUFFER_SLOT) {
*queueEmpty = true;
- mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
return mImageSlots[slot].mImage;
}
}
@@ -145,7 +149,7 @@
st.computeCurrentTransformMatrixLocked();
*queueEmpty = false;
- mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
return mImageSlots[slot].mImage;
}
diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h
index 31ee8db..f0e55bb 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.h
+++ b/libs/hwui/surfacetexture/ImageConsumer.h
@@ -68,18 +68,21 @@
* ImageConsumer maintains about a BufferQueue buffer slot.
*/
struct ImageSlot {
- ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+ ImageSlot() : mDataspace(HAL_DATASPACE_UNKNOWN), mEglFence(EGL_NO_SYNC_KHR) {}
// mImage is the SkImage created from mGraphicBuffer.
sk_sp<SkImage> mImage;
+ // the dataspace associated with the current image
+ android_dataspace mDataspace;
+
/**
* mEglFence is the EGL sync object that must signal before the buffer
* associated with this buffer slot may be dequeued.
*/
EGLSyncKHR mEglFence;
- void createIfNeeded(sp<GraphicBuffer> graphicBuffer);
+ void createIfNeeded(sp<GraphicBuffer> graphicBuffer, android_dataspace dataspace);
};
/**
@@ -94,4 +97,4 @@
ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
};
-}; /* namespace android */
+} /* namespace android */
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp
index 4bff715..da09444 100644
--- a/libs/hwui/surfacetexture/SurfaceTexture.cpp
+++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp
@@ -470,8 +470,7 @@
ConsumerBase::dumpLocked(result, prefix);
}
-sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
- bool* queueEmpty,
+sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty,
uirenderer::RenderState& renderState) {
Mutex::Autolock _l(mMutex);
@@ -488,9 +487,8 @@
auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState);
if (image.get()) {
uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix);
- dataSpace = mCurrentDataSpace;
}
return image;
}
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h
index db392a9..b5d136f 100644
--- a/libs/hwui/surfacetexture/SurfaceTexture.h
+++ b/libs/hwui/surfacetexture/SurfaceTexture.h
@@ -258,8 +258,8 @@
*/
status_t attachToContext(uint32_t tex);
- sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
- bool* queueEmpty, uirenderer::RenderState& renderState);
+ sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty,
+ uirenderer::RenderState& renderState);
/**
* attachToView attaches a SurfaceTexture that is currently in the
@@ -449,4 +449,4 @@
};
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 66b9b85..f812022 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -72,9 +72,7 @@
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- SkMatrix identity;
- identity.setIdentity();
- layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr);
+ layerUpdater->updateLayer(true, SkMatrix::I(), nullptr);
return layerUpdater;
}
@@ -83,7 +81,7 @@
auto utf16 = asciiToUtf16(text);
uint32_t length = strlen(text);
SkPaint glyphPaint(paint);
- glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
canvas->drawText(
utf16.get(), length, // text buffer
0, length, // draw range
@@ -96,7 +94,7 @@
const SkPath& path) {
auto utf16 = asciiToUtf16(text);
SkPaint glyphPaint(paint);
- glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
nullptr);
}
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index 15039b5..ad11a1d 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -44,8 +44,7 @@
});
SkPaint paint;
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = hwuiBitmap->makeImage(&colorFilter);
+ sk_sp<SkImage> image = hwuiBitmap->makeImage();
sk_sp<SkShader> repeatShader =
image->makeShader(SkShader::TileMode::kRepeat_TileMode,
SkShader::TileMode::kRepeat_TileMode, nullptr);
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index f137562..448408d 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -72,8 +72,7 @@
void doFrame(int frameNr) override {}
sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) {
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ sk_sp<SkImage> image = bitmap.makeImage();
return image->makeShader(SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode);
}
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index c235715..210fced 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -54,8 +54,7 @@
// create an image and pin it so that we have something with a unique key in the cache
sk_sp<Bitmap> bitmap =
Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h));
- sk_sp<SkColorFilter> filter;
- sk_sp<SkImage> image = bitmap->makeImage(&filter);
+ sk_sp<SkImage> image = bitmap->makeImage();
ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
// attempt to trim all memory while we still hold strong refs
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index 6c8775b..a686979 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -43,7 +43,7 @@
SkBitmap bitmap;
bitmap.allocN32Pixels(16, 16);
sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
- layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage);
+ layerUpdater->updateLayer(true, scaledMatrix, layerImage);
// the backing layer should now have all the properties applied.
EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
index 217d63f..41714eb 100644
--- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
@@ -81,5 +81,5 @@
EXPECT_TRUE(queue.entries().empty());
}
-};
-};
+}
+}
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 634ceff..f3a7648 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -53,12 +53,12 @@
adobeBitmap->getSkBitmap(&adobeSkBitmap);
*adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red
- SkImageInfo info = adobeInfo.makeColorSpace(nullptr);
+ SkImageInfo info = adobeInfo.makeColorSpace(SkColorSpace::MakeSRGB());
sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
SkBitmap skBitmap;
bitmap->getSkBitmap(&skBitmap);
- // Create a software canvas.
+ // Create a software sRGB canvas.
SkiaCanvas canvas(skBitmap);
canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
// The result should be fully red, since we convert to sRGB at draw time.
@@ -77,7 +77,7 @@
picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
- // Playback to an software canvas. The result should be fully red.
+ // Playback to a software sRGB canvas. The result should be fully red.
canvas.asSkCanvas()->drawPicture(picture);
ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
}
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 02f740c..ee6beba 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -406,5 +406,5 @@
EXPECT_TRUE(shader->unique());
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h
index 8faeee6..bb750ca 100644
--- a/libs/hwui/thread/Barrier.h
+++ b/libs/hwui/thread/Barrier.h
@@ -48,7 +48,7 @@
mutable Condition mCondition;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_BARRIER_H
diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h
index 45f3102..df53348e 100644
--- a/libs/hwui/thread/Future.h
+++ b/libs/hwui/thread/Future.h
@@ -53,7 +53,7 @@
T mResult;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_FUTURE_H
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
index ffcd4b6..6d33ac4 100644
--- a/libs/hwui/thread/Signal.h
+++ b/libs/hwui/thread/Signal.h
@@ -53,7 +53,7 @@
mutable Condition mCondition;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_SIGNAL_H
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
index 276a22f..228ce19 100644
--- a/libs/hwui/thread/Task.h
+++ b/libs/hwui/thread/Task.h
@@ -48,7 +48,7 @@
sp<Future<T> > mFuture;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_TASK_H
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 54b55e4..26ff6eb 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -129,5 +129,5 @@
mSignal.signal();
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index 29b4fcd..c4c1291 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -101,7 +101,7 @@
std::vector<sp<WorkerThread> > mThreads;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_TASK_MANAGER_H
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index 1bc5646..763d1aa 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -178,5 +178,5 @@
}
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index bec3837..d6b41b8 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -41,7 +41,7 @@
int32_t width, int32_t height);
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_HWUI_BLUR_H
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 3fb6a31..dc347f6 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -221,5 +221,5 @@
static_cast<uint8_t>(rgb.b * 255));
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
index eafe2f1..8cc4d10 100644
--- a/libs/hwui/utils/FatVector.h
+++ b/libs/hwui/utils/FatVector.h
@@ -99,7 +99,7 @@
typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_FAT_VECTOR_H
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index fcd036c..c694e93 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -76,5 +76,5 @@
}
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 3e5021c..8baa4b77 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -249,5 +249,5 @@
ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index 03f685e..b401fcf 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -201,7 +201,7 @@
: std::vector<T, LinearStdAllocator<T>>(allocator) {}
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/libs/hwui/utils/Pair.h b/libs/hwui/utils/Pair.h
index 4bcd576..76f93cb 100644
--- a/libs/hwui/utils/Pair.h
+++ b/libs/hwui/utils/Pair.h
@@ -36,7 +36,7 @@
inline const S& getSecond() const { return second; }
};
-}; // namespace uirenderer
+} // namespace uirenderer
template <typename F, typename S>
struct trait_trivial_ctor<uirenderer::Pair<F, S> > {
@@ -55,6 +55,6 @@
enum { value = aggregate_traits<F, S>::has_trivial_move };
};
-}; // namespace android
+} // namespace android
#endif // ANDROID_HWUI_PAIR_H
diff --git a/libs/hwui/utils/Result.h b/libs/hwui/utils/Result.h
index 7f33f2e..bd20ba6 100644
--- a/libs/hwui/utils/Result.h
+++ b/libs/hwui/utils/Result.h
@@ -51,4 +51,4 @@
std::variant<R, Error<E>> result;
};
-}; // namespace android::uirenderer
+} // namespace android::uirenderer
diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
index b3e8931..081386a 100644
--- a/libs/hwui/utils/RingBuffer.h
+++ b/libs/hwui/utils/RingBuffer.h
@@ -61,7 +61,7 @@
size_t mCount = 0;
};
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
#endif /* RINGBUFFER_H_ */
diff --git a/libs/hwui/utils/StringUtils.cpp b/libs/hwui/utils/StringUtils.cpp
index 5304b76..304982e 100644
--- a/libs/hwui/utils/StringUtils.cpp
+++ b/libs/hwui/utils/StringUtils.cpp
@@ -34,5 +34,5 @@
return set;
}
-}; // namespace uirenderer
-}; // namespace android
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/TypeLogic.h b/libs/hwui/utils/TypeLogic.h
index dbdad33..1689cce 100644
--- a/libs/hwui/utils/TypeLogic.h
+++ b/libs/hwui/utils/TypeLogic.h
@@ -37,4 +37,4 @@
template <typename D, typename S> using same_cv = copy_cv<std::remove_cv_t<D>, S>;
template <typename D, typename S> using same_cv_t = typename same_cv<D, S>::type;
-}; // namespace android::uirenderer
\ No newline at end of file
+} // namespace android::uirenderer
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index f60c338..8de2ab4 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -82,6 +82,11 @@
STATS_LOG_VALUE_TYPE type;
};
+struct WorkChain {
+ std::vector<int32_t> uids;
+ std::vector<std::string> tags;
+};
+
// Represents a parcelable object. Only used to send data from Android OS to statsd.
class StatsLogEventWrapper : public android::Parcelable {
public:
@@ -99,7 +104,9 @@
int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
- std::vector<StatsLogValue> getElements() const { return mElements; }
+ const std::vector<StatsLogValue>& getElements() const { return mElements; }
+
+ const std::vector<WorkChain>& getWorkChains() const { return mWorkChains; }
private:
int mTagId;
@@ -109,6 +116,8 @@
int64_t mWallClockTimeNs;
std::vector<StatsLogValue> mElements;
+
+ std::vector<WorkChain> mWorkChains;
};
} // Namespace os
} // Namespace android
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 04c4629..f6dfdef 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -58,6 +58,31 @@
ALOGE("statsd could not read wall clock time from parcel");
return res;
}
+ int numWorkChain = 0;
+ if ((res = in->readInt32(&numWorkChain)) != OK) {
+ ALOGE("statsd could not read number of work chains from parcel");
+ return res;
+ }
+ if (numWorkChain > 0) {
+ for (int i = 0; i < numWorkChain; i++) {
+ int numNodes = 0;
+ if ((res = in->readInt32(&numNodes)) != OK) {
+ ALOGE(
+ "statsd could not read number of nodes in work chain from parcel");
+ return res;
+ }
+ if (numNodes == 0) {
+ ALOGE("empty work chain");
+ return BAD_VALUE;
+ }
+ WorkChain wc;
+ for (int j = 0; j < numNodes; j++) {
+ wc.uids.push_back(in->readInt32());
+ wc.tags.push_back(std::string(String8(in->readString16()).string()));
+ }
+ mWorkChains.push_back(wc);
+ }
+ }
int dataSize = 0;
if ((res = in->readInt32(&dataSize)) != OK) {
ALOGE("statsd could not read data size from parcel");
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index ff2fad4..ae87998 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -121,18 +121,4 @@
// used by gts tests to verify throttling whitelist
String[] getBackgroundThrottlingWhitelist();
-
- /**
- * Allow the {@link android.location.LocationManager#getNetworkProviderPackage location
- * provider} to start the UI to modify the location permission for a package.
- *
- * <p>Can only be called by the location provider.
- *
- * @param packageName The package the permission belongs to
- * @param permission The (individual) permission to switch
- *
- * @return A pending intent that starts the permission management UI or {@code null} if the
- * intent cannot be created
- */
- PendingIntent createManageLocationPermissionIntent(in String packageName, in String permission);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index b66ceef..3bf98b3 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,7 +22,6 @@
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import android.Manifest;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -2401,29 +2400,4 @@
return null;
}
}
-
- /**
- * Allow the {@link android.location.LocationManager#getNetworkProviderPackage location
- * provider} to start the UI to modify the location permission for a package.
- *
- * <p>Can only be called by the location provider.
- *
- * @param packageName The package the permission belongs to
- * @param permission The (individual) location permission to switch
- *
- * @return A one-shot pending intent that starts the permission management UI or {@code null} if
- * the intent cannot be created
- *
- * @hide
- */
- @SystemApi
- public @Nullable PendingIntent createManageLocationPermissionIntent(@NonNull String packageName,
- @NonNull String permission) {
- try {
- return mService.createManageLocationPermissionIntent(packageName, permission);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
- }
- }
}
diff --git a/media/OWNERS b/media/OWNERS
index 0abf9ae..03b751c 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,12 +1,13 @@
+chz@google.com
+dwkang@google.com
elaurent@google.com
etalvala@google.com
gkasten@google.com
hunga@google.com
+jaewan@google.com
jmtrivi@google.com
+jsharkey@android.com
lajos@google.com
marcone@google.com
sungsoo@google.com
wjia@google.com
-jaewan@google.com
-chz@google.com
-dwkang@google.com
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index ff1bdd4..d10900e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -541,6 +541,7 @@
* Adjusting the volume due to a hardware key press.
* @hide
*/
+ @SystemApi
public static final int FLAG_FROM_KEY = 1 << 12;
private static final String[] FLAG_NAMES = {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 2a575b6..4b2353c 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,16 +16,6 @@
package android.media;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.ArrayList;
-import java.util.List;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -39,13 +29,21 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
/**
* The AudioRecord class manages the audio resources for Java applications
* to record audio from the audio input hardware of the platform. This is
@@ -1807,6 +1805,8 @@
private native final int native_get_active_microphones(
ArrayList<MicrophoneInfo> activeMicrophones);
+ private native int native_getPortId();
+
//---------------------------------------------------------
// Utility methods
//------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 67cc456..082a375 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -75,6 +75,12 @@
*/
public static final int NUM_STREAMS = 5;
+ /** Maximum value for AudioTrack channel count
+ * @hide public for MediaCode only, do not un-hide or change to a numeric literal
+ */
+ public static final int OUT_CHANNEL_COUNT_MAX = native_get_FCC_8();
+ private static native int native_get_FCC_8();
+
// Expose only the getter method publicly so we can change it in the future
private static final int NUM_STREAM_TYPES = 11;
@UnsupportedAppUsage
@@ -918,6 +924,15 @@
public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
+ /**
+ * Communicate UID of active assistant to audio policy service.
+ */
+ public static native int setAssistantUid(int uid);
+ /**
+ * Communicate UIDs of active accessibility services to audio policy service.
+ */
+ public static native int setA11yServicesUids(int[] uids);
+
// Items shared with audio service
/**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index d37f8ab..2c4ec3a 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -93,11 +93,6 @@
*/
private static final float GAIN_MAX = 1.0f;
- /** Maximum value for AudioTrack channel count
- * @hide public for MediaCode only, do not un-hide or change to a numeric literal
- */
- public static final int CHANNEL_COUNT_MAX = native_get_FCC_8();
-
/** indicates AudioTrack state is stopped */
public static final int PLAYSTATE_STOPPED = 1; // matches SL_PLAYSTATE_STOPPED
/** indicates AudioTrack state is paused */
@@ -1001,7 +996,8 @@
}
// mask of all the positional channels supported, however the allowed combinations
- // are further restricted by the matching left/right rule and CHANNEL_COUNT_MAX
+ // are further restricted by the matching left/right rule and
+ // AudioSystem.OUT_CHANNEL_COUNT_MAX
private static final int SUPPORTED_OUT_CHANNELS =
AudioFormat.CHANNEL_OUT_FRONT_LEFT |
AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
@@ -1124,7 +1120,7 @@
mChannelIndexMask = channelIndexMask;
if (mChannelIndexMask != 0) {
// restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
- final int indexMask = (1 << CHANNEL_COUNT_MAX) - 1;
+ final int indexMask = (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1;
if ((channelIndexMask & ~indexMask) != 0) {
throw new IllegalArgumentException("Unsupported channel index configuration "
+ channelIndexMask);
@@ -1169,9 +1165,9 @@
return false;
}
final int channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
- if (channelCount > CHANNEL_COUNT_MAX) {
+ if (channelCount > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
loge("Channel configuration contains too many channels " +
- channelCount + ">" + CHANNEL_COUNT_MAX);
+ channelCount + ">" + AudioSystem.OUT_CHANNEL_COUNT_MAX);
return false;
}
// check for unsupported multichannel combinations:
@@ -2624,7 +2620,8 @@
* to the audio sink.
* <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
* queuing as much audio data for playback as possible without blocking.
- * @param timestamp The timestamp of the first decodable audio frame in the provided audioData.
+ * @param timestamp The timestamp, in nanoseconds, of the first decodable audio frame in the
+ * provided audioData.
* @return zero or the positive number of bytes that were written, or one of the following
* error codes.
* <ul>
@@ -3418,7 +3415,6 @@
private native final int native_getRoutedDeviceId();
private native final void native_enableDeviceCallback();
private native final void native_disableDeviceCallback();
- static private native int native_get_FCC_8();
private native int native_applyVolumeShaper(
@NonNull VolumeShaper.Configuration configuration,
@@ -3427,6 +3423,8 @@
private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
private native final int native_setPresentation(int presentationId, int programId);
+ private native int native_getPortId();
+
//---------------------------------------------------------
// Utility methods
//------------------
diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java
index a7e168f..0e8e6ce 100644
--- a/media/java/android/media/CallbackDataSourceDesc.java
+++ b/media/java/android/media/CallbackDataSourceDesc.java
@@ -18,31 +18,31 @@
import android.annotation.NonNull;
-import com.android.internal.util.Preconditions;
-
/**
* @hide
- * Structure for file data source descriptor.
+ * Structure of data source descriptor for sources using callback.
*
- * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
* to set data source for playback.
*
* <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}.
*
*/
public class CallbackDataSourceDesc extends DataSourceDesc {
- private Media2DataSource mMedia2DataSource;
+ private DataSourceCallback mDataSourceCallback;
private CallbackDataSourceDesc() {
}
/**
- * Return the Media2DataSource of this data source.
+ * Return the DataSourceCallback of this data source.
* It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}.
- * @return the Media2DataSource of this data source
+ * @return the DataSourceCallback of this data source
*/
- public Media2DataSource getMedia2DataSource() {
- return mMedia2DataSource;
+ public DataSourceCallback getDataSourceCallback() {
+ return mDataSourceCallback;
}
/**
@@ -60,7 +60,7 @@
* </pre>
*/
public static class Builder extends BuilderBase<Builder> {
- private Media2DataSource mMedia2DataSource;
+ private DataSourceCallback mDataSourceCallback;
/**
* Constructs a new Builder with the defaults.
@@ -79,7 +79,7 @@
if (dsd == null) {
return; // use default
}
- mMedia2DataSource = dsd.mMedia2DataSource;
+ mDataSourceCallback = dsd.mDataSourceCallback;
}
/**
@@ -92,21 +92,21 @@
public @NonNull CallbackDataSourceDesc build() {
CallbackDataSourceDesc dsd = new CallbackDataSourceDesc();
super.build(dsd);
- dsd.mMedia2DataSource = mMedia2DataSource;
+ dsd.mDataSourceCallback = mDataSourceCallback;
return dsd;
}
/**
- * Sets the data source (Media2DataSource) to use.
+ * Sets the data source (DataSourceCallback) to use.
*
- * @param m2ds the Media2DataSource for the media to play
+ * @param dscb the DataSourceCallback for the media to play
* @return the same Builder instance.
- * @throws NullPointerException if m2ds is null.
+ * @throws NullPointerException if dscb is null.
*/
- public @NonNull Builder setDataSource(@NonNull Media2DataSource m2ds) {
- Preconditions.checkNotNull(m2ds);
- mMedia2DataSource = m2ds;
+ public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) {
+ Media2Utils.checkArgument(dscb != null, "data source cannot be null.");
+ mDataSourceCallback = dscb;
return this;
}
}
diff --git a/media/java/android/media/CloseGuard.java b/media/java/android/media/CloseGuard.java
new file mode 100644
index 0000000..2014673
--- /dev/null
+++ b/media/java/android/media/CloseGuard.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.util.Log;
+
+/**
+ * Note: This file is copied from dalvik.system package with the following modifications:
+ * - Remove @CorePlatformApi, @IntraCoreApi and @UnsupportedAppUsage annotations.
+ * - Replace System.logW() with android.util.Log.w().
+ * This file should be used only within media mainline module.
+ * TODO: Remove this file and use dalvik.system.CloseGuard once
+ * @CorePlatformApi becomes stable or we have a replacement in SDK API.
+ * b/120419300
+ *
+ * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
+ * resources that should have been cleaned up by explicit close
+ * methods (aka "explicit termination methods" in Effective Java).
+ * <p>
+ * A simple example: <pre> {@code
+ * class Foo {
+ *
+ * {@literal @}ReachabilitySensitive
+ * private final CloseGuard guard = CloseGuard.get();
+ *
+ * ...
+ *
+ * public Foo() {
+ * ...;
+ * guard.open("cleanup");
+ * }
+ *
+ * public void cleanup() {
+ * guard.close();
+ * ...;
+ * }
+ *
+ * protected void finalize() throws Throwable {
+ * try {
+ * // Note that guard could be null if the constructor threw.
+ * if (guard != null) {
+ * guard.warnIfOpen();
+ * }
+ * cleanup();
+ * } finally {
+ * super.finalize();
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * In usage where the resource to be explicitly cleaned up is
+ * allocated after object construction, CloseGuard protection can
+ * be deferred. For example: <pre> {@code
+ * class Bar {
+ *
+ * {@literal @}ReachabilitySensitive
+ * private final CloseGuard guard = CloseGuard.get();
+ *
+ * ...
+ *
+ * public Bar() {
+ * ...;
+ * }
+ *
+ * public void connect() {
+ * ...;
+ * guard.open("cleanup");
+ * }
+ *
+ * public void cleanup() {
+ * guard.close();
+ * ...;
+ * }
+ *
+ * protected void finalize() throws Throwable {
+ * try {
+ * // Note that guard could be null if the constructor threw.
+ * if (guard != null) {
+ * guard.warnIfOpen();
+ * }
+ * cleanup();
+ * } finally {
+ * super.finalize();
+ * }
+ * }
+ * }
+ * }</pre>
+ *
+ * When used in a constructor, calls to {@code open} should occur at
+ * the end of the constructor since an exception that would cause
+ * abrupt termination of the constructor will mean that the user will
+ * not have a reference to the object to cleanup explicitly. When used
+ * in a method, the call to {@code open} should occur just after
+ * resource acquisition.
+ *
+ * The @ReachabilitySensitive annotation ensures that finalize() cannot be
+ * called during the explicit call to cleanup(), prior to the guard.close call.
+ * There is an extremely small chance that, for code that neglects to call
+ * cleanup(), finalize() and thus cleanup() will be called while a method on
+ * the object is still active, but the "this" reference is no longer required.
+ * If missing cleanup() calls are expected, additional @ReachabilitySensitive
+ * annotations or reachabilityFence() calls may be required.
+ *
+ * @hide
+ */
+final class CloseGuard {
+
+ /**
+ * True if collection of call-site information (the expensive operation
+ * here) and tracking via a Tracker (see below) are enabled.
+ * Enabled by default so we can diagnose issues early in VM startup.
+ * Note, however, that Android disables this early in its startup,
+ * but enables it with DropBoxing for system apps on debug builds.
+ */
+ private static volatile boolean stackAndTrackingEnabled = true;
+
+ /**
+ * Hook for customizing how CloseGuard issues are reported.
+ * Bypassed if stackAndTrackingEnabled was false when open was called.
+ */
+ private static volatile Reporter reporter = new DefaultReporter();
+
+ /**
+ * Hook for customizing how CloseGuard issues are tracked.
+ */
+ private static volatile Tracker currentTracker = null; // Disabled by default.
+
+ /**
+ * Returns a CloseGuard instance. {@code #open(String)} can be used to set
+ * up the instance to warn on failure to close.
+ */
+ public static CloseGuard get() {
+ return new CloseGuard();
+ }
+
+ /**
+ * Enables/disables stack capture and tracking. A call stack is captured
+ * during open(), and open/close events are reported to the Tracker, only
+ * if enabled is true. If a stack trace was captured, the {@link
+ * #getReporter() reporter} is informed of unclosed resources; otherwise a
+ * one-line warning is logged.
+ */
+ public static void setEnabled(boolean enabled) {
+ CloseGuard.stackAndTrackingEnabled = enabled;
+ }
+
+ /**
+ * True if CloseGuard stack capture and tracking are enabled.
+ */
+ public static boolean isEnabled() {
+ return stackAndTrackingEnabled;
+ }
+
+ /**
+ * Used to replace default Reporter used to warn of CloseGuard
+ * violations when stack tracking is enabled. Must be non-null.
+ */
+ public static void setReporter(Reporter rep) {
+ if (rep == null) {
+ throw new NullPointerException("reporter == null");
+ }
+ CloseGuard.reporter = rep;
+ }
+
+ /**
+ * Returns non-null CloseGuard.Reporter.
+ */
+ public static Reporter getReporter() {
+ return reporter;
+ }
+
+ /**
+ * Sets the {@link Tracker} that is notified when resources are allocated and released.
+ * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()}
+ * was called. A null argument disables tracking.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ */
+ public static void setTracker(Tracker tracker) {
+ currentTracker = tracker;
+ }
+
+ /**
+ * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate
+ * there is none.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ */
+ public static Tracker getTracker() {
+ return currentTracker;
+ }
+
+ private CloseGuard() {}
+
+ /**
+ * {@code open} initializes the instance with a warning that the caller
+ * should have explicitly called the {@code closer} method instead of
+ * relying on finalization.
+ *
+ * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
+ * @throws NullPointerException if closer is null.
+ */
+ public void open(String closer) {
+ // always perform the check for valid API usage...
+ if (closer == null) {
+ throw new NullPointerException("closer == null");
+ }
+ // ...but avoid allocating an allocation stack if "disabled"
+ if (!stackAndTrackingEnabled) {
+ closerNameOrAllocationInfo = closer;
+ return;
+ }
+ String message = "Explicit termination method '" + closer + "' not called";
+ Throwable stack = new Throwable(message);
+ closerNameOrAllocationInfo = stack;
+ Tracker tracker = currentTracker;
+ if (tracker != null) {
+ tracker.open(stack);
+ }
+ }
+
+ // We keep either an allocation stack containing the closer String or, when
+ // in disabled state, just the closer String.
+ // We keep them in a single field only to minimize overhead.
+ private Object /* String or Throwable */ closerNameOrAllocationInfo;
+
+ /**
+ * Marks this CloseGuard instance as closed to avoid warnings on
+ * finalization.
+ */
+ public void close() {
+ Tracker tracker = currentTracker;
+ if (tracker != null && closerNameOrAllocationInfo instanceof Throwable) {
+ // Invoke tracker on close only if we invoked it on open. Tracker may have changed.
+ tracker.close((Throwable) closerNameOrAllocationInfo);
+ }
+ closerNameOrAllocationInfo = null;
+ }
+
+ /**
+ * Logs a warning if the caller did not properly cleanup by calling an
+ * explicit close method before finalization. If CloseGuard was enabled
+ * when the CloseGuard was created, passes the stacktrace associated with
+ * the allocation to the current reporter. If it was not enabled, it just
+ * directly logs a brief message.
+ */
+ public void warnIfOpen() {
+ if (closerNameOrAllocationInfo != null) {
+ if (closerNameOrAllocationInfo instanceof String) {
+ Log.w("CloseGuard", "A resource failed to call "
+ + (String) closerNameOrAllocationInfo + ". ");
+ } else {
+ String message =
+ "A resource was acquired at attached stack trace but never released. ";
+ message += "See java.io.Closeable for information on avoiding resource leaks.";
+ Throwable stack = (Throwable) closerNameOrAllocationInfo;
+ reporter.report(message, stack);
+ }
+ }
+ }
+
+ /**
+ * Interface to allow customization of tracking behaviour.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ */
+ public interface Tracker {
+ void open(Throwable allocationSite);
+ void close(Throwable allocationSite);
+ }
+
+ /**
+ * Interface to allow customization of reporting behavior.
+ * @hide
+ */
+ public interface Reporter {
+ void report(String message, Throwable allocationSite);
+ }
+
+ /**
+ * Default Reporter which reports CloseGuard violations to the log.
+ */
+ private static final class DefaultReporter implements Reporter {
+ private DefaultReporter() {}
+
+ @Override public void report (String message, Throwable allocationSite) {
+ Log.w("CloseGuard", message, allocationSite);
+ }
+ }
+}
diff --git a/media/java/android/media/Media2DataSource.java b/media/java/android/media/DataSourceCallback.java
similarity index 89%
rename from media/java/android/media/Media2DataSource.java
rename to media/java/android/media/DataSourceCallback.java
index 08df632..9b27baf 100644
--- a/media/java/android/media/Media2DataSource.java
+++ b/media/java/android/media/DataSourceCallback.java
@@ -27,12 +27,12 @@
*
* <p class="note">Methods of this interface may be called on multiple different
* threads. There will be a thread synchronization point between each call to ensure that
- * modifications to the state of your Media2DataSource are visible to future calls. This means
+ * modifications to the state of your DataSourceCallback are visible to future calls. This means
* you don't need to do your own synchronization unless you're modifying the
- * Media2DataSource from another thread while it's being used by the framework.</p>
+ * DataSourceCallback from another thread while it's being used by the framework.</p>
*
*/
-public abstract class Media2DataSource implements Closeable {
+public abstract class DataSourceCallback implements Closeable {
/**
* Called to request data from the given position.
*
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index aed3f84..702034e9 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -18,13 +18,13 @@
import android.annotation.NonNull;
-import com.android.internal.util.Preconditions;
-
/**
* @hide
* Base class of data source descriptor.
*
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
* to set data source for playback.
*
* <p>Users should use subclasses' builder to change {@link DataSourceDesc}.
@@ -32,7 +32,7 @@
*/
public class DataSourceDesc {
// intentionally less than long.MAX_VALUE
- public static final long LONG_MAX = 0x7ffffffffffffffL;
+ static final long LONG_MAX = 0x7ffffffffffffffL;
// keep consistent with native code
public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
@@ -48,6 +48,19 @@
}
/**
+ * Releases the resources held by this {@code DataSourceDesc} object.
+ */
+ void close() {
+ }
+
+ // Have to declare protected for finalize() since it is protected
+ // in the base class Object.
+ @Override
+ protected void finalize() throws Throwable {
+ close();
+ }
+
+ /**
* Return the media Id of data source.
* @return the media Id of data source
*/
@@ -118,7 +131,7 @@
* @return the same instance of subclass of {@link DataSourceDesc}
*/
void build(@NonNull DataSourceDesc dsd) {
- Preconditions.checkNotNull(dsd);
+ Media2Utils.checkArgument(dsd != null, "dsd cannot be null.");
if (mStartPositionMs > mEndPositionMs) {
throw new IllegalStateException("Illegal start/end position: "
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index b96a585..32c4643 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -16,7 +16,10 @@
package android.media;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
@@ -26,12 +29,14 @@
import android.system.OsConstants;
import android.util.Log;
import android.util.Pair;
-import android.annotation.IntDef;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
import java.io.DataInput;
+import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
@@ -42,14 +47,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -58,11 +63,6 @@
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
/**
* This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
@@ -583,11 +583,19 @@
private static class ExifAttribute {
public final int format;
public final int numberOfComponents;
+ public final long bytesOffset;
public final byte[] bytes;
+ public static final long BYTES_OFFSET_UNKNOWN = -1;
+
private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
+ this(format, numberOfComponents, BYTES_OFFSET_UNKNOWN, bytes);
+ }
+
+ private ExifAttribute(int format, int numberOfComponents, long bytesOffset, byte[] bytes) {
this.format = format;
this.numberOfComponents = numberOfComponents;
+ this.bytesOffset = bytesOffset;
this.bytes = bytes;
}
@@ -1318,6 +1326,7 @@
private int mOrfThumbnailLength;
private int mRw2JpgFromRawOffset;
private boolean mIsSupportedFile;
+ private boolean mModified;
// Pattern to check non zero timestamp
private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
@@ -1328,7 +1337,14 @@
/**
* Reads Exif tags from the specified image file.
*/
- public ExifInterface(String filename) throws IOException {
+ public ExifInterface(@NonNull File file) throws IOException {
+ this(file.getAbsolutePath());
+ }
+
+ /**
+ * Reads Exif tags from the specified image file.
+ */
+ public ExifInterface(@NonNull String filename) throws IOException {
if (filename == null) {
throw new IllegalArgumentException("filename cannot be null");
}
@@ -1354,7 +1370,7 @@
* for writable and seekable file descriptors only. This constructor will not rewind the offset
* of the given file descriptor. Developers should close the file descriptor after use.
*/
- public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
+ public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException {
if (fileDescriptor == null) {
throw new IllegalArgumentException("fileDescriptor cannot be null");
}
@@ -1388,7 +1404,7 @@
* for input streams. The given input stream will proceed its current position. Developers
* should close the input stream after use.
*/
- public ExifInterface(InputStream inputStream) throws IOException {
+ public ExifInterface(@NonNull InputStream inputStream) throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream cannot be null");
}
@@ -1414,7 +1430,7 @@
*
* @param tag the name of the tag.
*/
- private ExifAttribute getExifAttribute(String tag) {
+ private @Nullable ExifAttribute getExifAttribute(@NonNull String tag) {
// Retrieves all tag groups. The value from primary image tag group has a higher priority
// than the value from the thumbnail tag group if there are more than one candidates.
for (int i = 0; i < EXIF_TAGS.length; ++i) {
@@ -1432,7 +1448,7 @@
*
* @param tag the name of the tag.
*/
- public String getAttribute(String tag) {
+ public @Nullable String getAttribute(@NonNull String tag) {
ExifAttribute attribute = getExifAttribute(tag);
if (attribute != null) {
if (!sTagSetForCompatibility.contains(tag)) {
@@ -1470,7 +1486,7 @@
* @param tag the name of the tag.
* @param defaultValue the value to return if the tag is not available.
*/
- public int getAttributeInt(String tag, int defaultValue) {
+ public int getAttributeInt(@NonNull String tag, int defaultValue) {
ExifAttribute exifAttribute = getExifAttribute(tag);
if (exifAttribute == null) {
return defaultValue;
@@ -1491,7 +1507,7 @@
* @param tag the name of the tag.
* @param defaultValue the value to return if the tag is not available.
*/
- public double getAttributeDouble(String tag, double defaultValue) {
+ public double getAttributeDouble(@NonNull String tag, double defaultValue) {
ExifAttribute exifAttribute = getExifAttribute(tag);
if (exifAttribute == null) {
return defaultValue;
@@ -1510,7 +1526,7 @@
* @param tag the name of the tag.
* @param value the value of the tag.
*/
- public void setAttribute(String tag, String value) {
+ public void setAttribute(@NonNull String tag, @Nullable String value) {
// Convert the given value to rational values for backwards compatibility.
if (value != null && sTagSetForCompatibility.contains(tag)) {
if (tag.equals(TAG_GPS_TIMESTAMP)) {
@@ -1772,12 +1788,18 @@
}
/**
- * Save the tag data into the original image file. This is expensive because it involves
- * copying all the data from one file to another and deleting the old file and renaming the
- * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
- * and make a single call rather than multiple calls for each attribute.
+ * Save the tag data into the original image file. This is expensive because
+ * it involves copying all the data from one file to another and deleting
+ * the old file and renaming the other. It's best to use
+ * {@link #setAttribute(String,String)} to set all attributes to write and
+ * make a single call rather than multiple calls for each attribute.
* <p>
* This method is only supported for JPEG files.
+ * <p class="note">
+ * Note: after calling this method, any attempts to obtain range information
+ * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
+ * will throw {@link IllegalStateException}, since the offsets may have
+ * changed in the newly written file.
* </p>
*/
public void saveAttributes() throws IOException {
@@ -1789,6 +1811,10 @@
"ExifInterface does not support saving attributes for the current input.");
}
+ // Remember the fact that we've changed the file on disk from what was
+ // originally parsed, meaning we can't answer range questions
+ mModified = true;
+
// Keep the thumbnail in memory
mThumbnailBytes = getThumbnail();
@@ -1849,6 +1875,15 @@
}
/**
+ * Returns true if the image file has the given attribute defined.
+ *
+ * @param tag the name of the tag.
+ */
+ public boolean hasAttribute(String tag) {
+ return (getExifAttribute(tag) != null);
+ }
+
+ /**
* Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
* JPEG compressed thumbnail.
* The returned data can be decoded using
@@ -1968,17 +2003,45 @@
*
* @return two-element array, the offset in the first value, and length in
* the second, or {@code null} if no thumbnail was found.
+ * @throws IllegalStateException if {@link #saveAttributes()} has been
+ * called since the underlying file was initially parsed, since
+ * that means offsets may have changed.
*/
- public long[] getThumbnailRange() {
- if (!mHasThumbnail) {
- return null;
+ public @Nullable long[] getThumbnailRange() {
+ if (mModified) {
+ throw new IllegalStateException(
+ "The underlying file has been modified since being parsed");
}
- long[] range = new long[2];
- range[0] = mThumbnailOffset;
- range[1] = mThumbnailLength;
+ if (mHasThumbnail) {
+ return new long[] { mThumbnailOffset, mThumbnailLength };
+ } else {
+ return null;
+ }
+ }
- return range;
+ /**
+ * Returns the offset and length of the requested tag inside the image file,
+ * or {@code null} if the tag is not contained.
+ *
+ * @return two-element array, the offset in the first value, and length in
+ * the second, or {@code null} if no tag was found.
+ * @throws IllegalStateException if {@link #saveAttributes()} has been
+ * called since the underlying file was initially parsed, since
+ * that means offsets may have changed.
+ */
+ public @Nullable long[] getAttributeRange(@NonNull String tag) {
+ if (mModified) {
+ throw new IllegalStateException(
+ "The underlying file has been modified since being parsed");
+ }
+
+ final ExifAttribute attribute = getExifAttribute(tag);
+ if (attribute != null) {
+ return new long[] { attribute.bytesOffset, attribute.bytes.length };
+ } else {
+ return null;
+ }
}
/**
@@ -2023,13 +2086,41 @@
}
/**
- * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
- * Returns -1 if the date time information if not available.
+ * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid.
+ *
* @hide
*/
@UnsupportedAppUsage
- public long getDateTime() {
- String dateTimeString = getAttribute(TAG_DATETIME);
+ public @CurrentTimeMillisLong long getDateTime() {
+ return parseDateTime(getAttribute(TAG_DATETIME),
+ getAttribute(TAG_SUBSEC_TIME));
+ }
+
+ /**
+ * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or
+ * invalid.
+ *
+ * @hide
+ */
+ public @CurrentTimeMillisLong long getDateTimeDigitized() {
+ return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED),
+ getAttribute(TAG_SUBSEC_TIME_DIGITIZED));
+ }
+
+ /**
+ * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or
+ * invalid.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public @CurrentTimeMillisLong long getDateTimeOriginal() {
+ return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL),
+ getAttribute(TAG_SUBSEC_TIME_ORIGINAL));
+ }
+
+ private static @CurrentTimeMillisLong long parseDateTime(@Nullable String dateTimeString,
+ @Nullable String subSecs) {
if (dateTimeString == null
|| !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
@@ -2041,7 +2132,6 @@
if (datetime == null) return -1;
long msecs = datetime.getTime();
- String subSecs = getAttribute(TAG_SUBSEC_TIME);
if (subSecs != null) {
try {
long sub = Long.parseLong(subSecs);
@@ -2545,13 +2635,18 @@
if (size == 0) {
return 0;
}
- // We don't allow read positions after the available bytes,
- // the input stream won't be able to seek back then.
- if (position < 0 || position >= in.available()) {
+ if (position < 0) {
return -1;
}
try {
if (mPosition != position) {
+ // We don't allow seek to positions after the available bytes,
+ // the input stream won't be able to seek back then.
+ // However, if we hit an exception before (mPosition set to -1),
+ // let it try the seek in hope it might recover.
+ if (mPosition >= 0 && position >= mPosition + in.available()) {
+ return -1;
+ }
in.seek(position);
mPosition = position;
}
@@ -2559,8 +2654,8 @@
// If the read will cause us to go over the available bytes,
// reduce the size so that we stay in the available range.
// Otherwise the input stream may not be able to seek back.
- if (mPosition + size > in.available()) {
- size = in.available() - (int)mPosition;
+ if (size > in.available()) {
+ size = in.available();
}
int bytesRead = in.read(buffer, offset, size);
@@ -3125,9 +3220,11 @@
continue;
}
- byte[] bytes = new byte[(int) byteCount];
+ final int bytesOffset = dataInputStream.peek();
+ final byte[] bytes = new byte[(int) byteCount];
dataInputStream.readFully(bytes);
- ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
+ ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents,
+ bytesOffset, bytes);
mAttributes[ifdType].put(tag.name, attribute);
// DNG files have a DNG Version tag specifying the version of specifications that the
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index 5d8ff49..763a81f 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -17,22 +17,26 @@
package android.media;
import android.annotation.NonNull;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
-import com.android.internal.util.Preconditions;
-
-import java.io.FileDescriptor;
+import java.io.IOException;
/**
* @hide
- * Structure for data source descriptor.
+ * Structure of data source descriptor for sources using file descriptor.
*
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
* to set data source for playback.
*
* <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}.
*
*/
public class FileDataSourceDesc extends DataSourceDesc {
+ private static final String TAG = "FileDataSourceDesc";
+
/**
* Used when the length of file descriptor is unknown.
*
@@ -40,34 +44,61 @@
*/
public static final long FD_LENGTH_UNKNOWN = LONG_MAX;
- private FileDescriptor mFD;
+ private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
private FileDataSourceDesc() {
+ super();
}
/**
- * Return the FileDescriptor of this data source.
- * @return the FileDescriptor of this data source
+ * Releases the resources held by this {@code FileDataSourceDesc} object.
*/
- public FileDescriptor getFileDescriptor() {
- return mFD;
+ @Override
+ void close() {
+ super.close();
+ closeFD();
}
/**
- * Return the offset associated with the FileDescriptor of this data source.
+ * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+ */
+ void closeFD() {
+ synchronized (this) {
+ if (mPFD != null) {
+ try {
+ mPFD.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close pfd: " + e);
+ }
+
+ mPFD = null;
+ }
+ }
+ }
+
+ /**
+ * Return the ParcelFileDescriptor of this data source.
+ * @return the ParcelFileDescriptor of this data source
+ */
+ public ParcelFileDescriptor getParcelFileDescriptor() {
+ return mPFD;
+ }
+
+ /**
+ * Return the offset associated with the ParcelFileDescriptor of this data source.
* It's meaningful only when it has been set by the {@link Builder}.
- * @return the offset associated with the FileDescriptor of this data source
+ * @return the offset associated with the ParcelFileDescriptor of this data source
*/
public long getOffset() {
return mOffset;
}
/**
- * Return the content length associated with the FileDescriptor of this data source.
+ * Return the content length associated with the ParcelFileDescriptor of this data source.
* {@link #FD_LENGTH_UNKNOWN} means same as the length of source content.
- * @return the content length associated with the FileDescriptor of this data source
+ * @return the content length associated with the ParcelFileDescriptor of this data source
*/
public long getLength() {
return mLength;
@@ -80,7 +111,7 @@
*
* <pre class="prettyprint">
* FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder()
- * .setDataSource(fd, 0, srcLength)
+ * .setDataSource(pfd, 0, srcLength)
* .setStartPosition(1000)
* .setEndPosition(15000)
* .build();
@@ -88,7 +119,7 @@
* </pre>
*/
public static class Builder extends BuilderBase<Builder> {
- private FileDescriptor mFD;
+ private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
@@ -109,7 +140,7 @@
if (dsd == null) {
return; // use default
}
- mFD = dsd.mFD;
+ mPFD = dsd.mPFD;
mOffset = dsd.mOffset;
mLength = dsd.mLength;
}
@@ -124,7 +155,7 @@
public @NonNull FileDataSourceDesc build() {
FileDataSourceDesc dsd = new FileDataSourceDesc();
super.build(dsd);
- dsd.mFD = mFD;
+ dsd.mPFD = mPFD;
dsd.mOffset = mOffset;
dsd.mLength = mLength;
@@ -132,38 +163,46 @@
}
/**
- * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
- * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
- * to close the file descriptor after the source has been used.
+ * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
+ * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
+ * created by this builder is passed to {@link MediaPlayer2} via
+ * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
+ * close the ParcelFileDescriptor.
*
- * @param fd the FileDescriptor for the file to play
+ * @param pfd the ParcelFileDescriptor for the file to play
* @return the same Builder instance.
- * @throws NullPointerException if fd is null.
+ * @throws NullPointerException if pfd is null.
*/
- public @NonNull Builder setDataSource(@NonNull FileDescriptor fd) {
- Preconditions.checkNotNull(fd);
+ public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) {
+ Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
resetDataSource();
- mFD = fd;
+ mPFD = pfd;
return this;
}
/**
- * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
- * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
- * to close the file descriptor after the source has been used.
+ * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
+ * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
+ * created by this builder is passed to {@link MediaPlayer2} via
+ * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
+ * close the ParcelFileDescriptor.
*
* Any negative number for offset is treated as 0.
* Any negative number for length is treated as maximum length of the data source.
*
- * @param fd the FileDescriptor for the file to play
+ * @param pfd the ParcelFileDescriptor for the file to play
* @param offset the offset into the file where the data to be played starts, in bytes
* @param length the length in bytes of the data to be played
* @return the same Builder instance.
- * @throws NullPointerException if fd is null.
+ * @throws NullPointerException if pfd is null.
*/
public @NonNull Builder setDataSource(
- @NonNull FileDescriptor fd, long offset, long length) {
- Preconditions.checkNotNull(fd);
+ @NonNull ParcelFileDescriptor pfd, long offset, long length) {
+ Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
if (offset < 0) {
offset = 0;
}
@@ -171,14 +210,14 @@
length = FD_LENGTH_UNKNOWN;
}
resetDataSource();
- mFD = fd;
+ mPFD = pfd;
mOffset = offset;
mLength = length;
return this;
}
private void resetDataSource() {
- mFD = null;
+ mPFD = null;
mOffset = 0;
mLength = FD_LENGTH_UNKNOWN;
}
diff --git a/media/java/android/media/Media2HTTPConnection.java b/media/java/android/media/Media2HTTPConnection.java
index 0d7825a..a369a62 100644
--- a/media/java/android/media/Media2HTTPConnection.java
+++ b/media/java/android/media/Media2HTTPConnection.java
@@ -16,27 +16,27 @@
package android.media;
-import android.net.NetworkUtils;
+import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
+
import android.os.StrictMode;
import android.util.Log;
import java.io.BufferedInputStream;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.Proxy;
-import java.net.URL;
import java.net.HttpURLConnection;
+import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.UnknownHostException;
import java.net.UnknownServiceException;
import java.util.HashMap;
import java.util.Map;
-import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
-
/** @hide */
public class Media2HTTPConnection {
private static final String TAG = "Media2HTTPConnection";
@@ -161,10 +161,10 @@
if (host.equalsIgnoreCase("localhost")) {
return true;
}
- if (NetworkUtils.numericToInetAddress(host).isLoopbackAddress()) {
+ if (InetAddress.getByName(host).isLoopbackAddress()) {
return true;
}
- } catch (IllegalArgumentException iex) {
+ } catch (IllegalArgumentException | UnknownHostException e) {
}
return false;
}
diff --git a/media/java/android/media/Media2Utils.java b/media/java/android/media/Media2Utils.java
index 066233d..5fd6191 100644
--- a/media/java/android/media/Media2Utils.java
+++ b/media/java/android/media/Media2Utils.java
@@ -31,6 +31,20 @@
private Media2Utils() {
}
+ /**
+ * Ensures that an expression checking an argument is true.
+ *
+ * @param expression the expression to check
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
+ public static void checkArgument(boolean expression, String errorMessage) {
+ if (!expression) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+
public static synchronized void storeCookies(List<HttpCookie> cookies) {
CookieHandler cookieHandler = CookieHandler.getDefault();
if (cookieHandler == null) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index dfe29e9..6301993 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -16,6 +16,9 @@
package android.media;
+import static android.media.Utils.intersectSortedDistinctRanges;
+import static android.media.Utils.sortDistinctRanges;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
@@ -32,9 +35,6 @@
import java.util.Map;
import java.util.Set;
-import static android.media.Utils.intersectSortedDistinctRanges;
-import static android.media.Utils.sortDistinctRanges;
-
/**
* Provides information about a given media codec available on the device. You can
* iterate through all codecs available by querying {@link MediaCodecList}. For example,
@@ -1117,7 +1117,7 @@
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
sampleRateRange = Range.create(1, 96000);
bitRates = Range.create(1, 10000000);
- maxChannels = AudioTrack.CHANNEL_COUNT_MAX;
+ maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
sampleRateRange = Range.create(1, 655350);
// lossless codec, so bitrate is ignored
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 284e422..b7743c9 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -17,20 +17,29 @@
package android.media;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.util.AbstractSet;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
- * Encapsulates the information describing the format of media data,
- * be it audio or video.
- *
- * The format of the media data is specified as string/value pairs.
- *
+ * Encapsulates the information describing the format of media data, be it audio or video, as
+ * well as optional feature metadata.
+ * <p>
+ * The format of the media data is specified as key/value pairs. Keys are strings. Values can
+ * be integer, long, float, String or ByteBuffer.
+ * <p>
+ * The feature metadata is specificed as string/boolean pairs.
+ * <p>
* Keys common to all audio/video formats, <b>all keys not marked optional are mandatory</b>:
*
* <table>
@@ -938,7 +947,6 @@
*/
public static final String KEY_CA_SESSION_ID = "ca-session-id";
-
/**
* A key describing the private data in the CA_descriptor associated with a media track.
* <p>
@@ -950,7 +958,7 @@
*/
public static final String KEY_CA_PRIVATE_DATA = "ca-private-data";
- /* package private */ MediaFormat(Map<String, Object> map) {
+ /* package private */ MediaFormat(@NonNull Map<String, Object> map) {
mMap = map;
}
@@ -969,11 +977,58 @@
/**
* Returns true iff a key of the given name exists in the format.
*/
- public final boolean containsKey(String name) {
+ public final boolean containsKey(@NonNull String name) {
return mMap.containsKey(name);
}
/**
+ * Returns true iff a feature of the given name exists in the format.
+ */
+ public final boolean containsFeature(@NonNull String name) {
+ return mMap.containsKey(KEY_FEATURE_ + name);
+ }
+
+ public static final int TYPE_NULL = 0;
+ public static final int TYPE_INTEGER = 1;
+ public static final int TYPE_LONG = 2;
+ public static final int TYPE_FLOAT = 3;
+ public static final int TYPE_STRING = 4;
+ public static final int TYPE_BYTE_BUFFER = 5;
+
+ /** @hide */
+ @IntDef({
+ TYPE_NULL,
+ TYPE_INTEGER,
+ TYPE_LONG,
+ TYPE_FLOAT,
+ TYPE_STRING,
+ TYPE_BYTE_BUFFER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ /**
+ * Returns the value type for a key. If the key does not exist, it returns TYPE_NULL.
+ */
+ public final @Type int getValueTypeForKey(@NonNull String name) {
+ Object value = mMap.get(name);
+ if (value == null) {
+ return TYPE_NULL;
+ } else if (value instanceof Integer) {
+ return TYPE_INTEGER;
+ } else if (value instanceof Long) {
+ return TYPE_LONG;
+ } else if (value instanceof Float) {
+ return TYPE_FLOAT;
+ } else if (value instanceof String) {
+ return TYPE_STRING;
+ } else if (value instanceof ByteBuffer) {
+ return TYPE_BYTE_BUFFER;
+ }
+ throw new RuntimeException("invalid value for key");
+ }
+
+ /**
* A key prefix used together with a {@link MediaCodecInfo.CodecCapabilities}
* feature name describing a required or optional feature for a codec capabilities
* query.
@@ -989,64 +1044,165 @@
public static final String KEY_FEATURE_ = "feature-";
/**
- * Returns the value of an integer key.
+ * Returns the value of a numeric key. This is provided as a convenience method for keys
+ * that may take multiple numeric types, such as {@link #KEY_FRAME_RATE}, or {@link
+ * #KEY_I_FRAME_INTERVAL}.
+ *
+ * @return null if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is ByteBuffer or String
*/
- public final int getInteger(String name) {
+ public final @Nullable Number getNumber(@NonNull String name) {
+ return ((Number)mMap.get(name));
+ }
+
+ /**
+ * Returns the value of a numeric key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is ByteBuffer or String
+ */
+ public final @NonNull Number getNumber(@NonNull String name, @NonNull Number defaultValue) {
+ Number ret = getNumber(name);
+ return ret == null ? defaultValue : ret;
+ }
+
+ /**
+ * Returns the value of an integer key.
+ *
+ * @throws NullPointerException if the key does not exist or the stored value for the key is
+ * null
+ * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or
+ * String
+ */
+ public final int getInteger(@NonNull String name) {
return ((Integer)mMap.get(name)).intValue();
}
/**
- * Returns the value of an integer key, or the default value if the
- * key is missing or is for another type value.
- * @hide
+ * Returns the value of an integer key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or
+ * String
*/
- public final int getInteger(String name, int defaultValue) {
+ public final int getInteger(@NonNull String name, int defaultValue) {
try {
return getInteger(name);
+ } catch (NullPointerException e) {
+ /* no such field or field is null */
+ return defaultValue;
}
- catch (NullPointerException e) { /* no such field */ }
- catch (ClassCastException e) { /* field of different type */ }
- return defaultValue;
}
/**
* Returns the value of a long key.
+ *
+ * @throws NullPointerException if the key does not exist or the stored value for the key is
+ * null
+ * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or
+ * String
*/
- public final long getLong(String name) {
+ public final long getLong(@NonNull String name) {
return ((Long)mMap.get(name)).longValue();
}
/**
- * Returns the value of a float key.
+ * Returns the value of an long key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or
+ * String
*/
- public final float getFloat(String name) {
+ public final long getLong(@NonNull String name, long defaultValue) {
+ try {
+ return getLong(name);
+ } catch (NullPointerException e) {
+ /* no such field or field is null */
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a float key.
+ *
+ * @throws NullPointerException if the key does not exist or the stored value for the key is
+ * null
+ * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or
+ * String
+ */
+ public final float getFloat(@NonNull String name) {
return ((Float)mMap.get(name)).floatValue();
}
/**
- * Returns the value of a string key.
+ * Returns the value of an float key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or
+ * String
*/
- public final String getString(String name) {
+ public final float getFloat(@NonNull String name, float defaultValue) {
+ try {
+ return getFloat(name);
+ } catch (NullPointerException e) {
+ /* no such field or field is null */
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a string key.
+ *
+ * @return null if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer
+ */
+ public final @Nullable String getString(@NonNull String name) {
return (String)mMap.get(name);
}
/**
- * Returns the value of a ByteBuffer key.
+ * Returns the value of an string key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer
*/
- public final ByteBuffer getByteBuffer(String name) {
+ public final @NonNull String getString(@NonNull String name, @NonNull String defaultValue) {
+ String ret = getString(name);
+ return ret == null ? defaultValue : ret;
+ }
+
+ /**
+ * Returns the value of a ByteBuffer key.
+ *
+ * @return null if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, float or String
+ */
+ public final @Nullable ByteBuffer getByteBuffer(@NonNull String name) {
return (ByteBuffer)mMap.get(name);
}
/**
+ * Returns the value of a ByteBuffer key, or the default value if the key is missing.
+ *
+ * @return defaultValue if the key does not exist or the stored value for the key is null
+ * @throws ClassCastException if the stored value for the key is int, long, float or String
+ */
+ public final @NonNull ByteBuffer getByteBuffer(
+ @NonNull String name, @NonNull ByteBuffer defaultValue) {
+ ByteBuffer ret = getByteBuffer(name);
+ return ret == null ? defaultValue : ret;
+ }
+
+ /**
* Returns whether a feature is to be enabled ({@code true}) or disabled
* ({@code false}).
*
* @param feature the name of a {@link MediaCodecInfo.CodecCapabilities} feature.
*
* @throws IllegalArgumentException if the feature was neither set to be enabled
- * nor to be disabled.
+ * nor to be disabled.
*/
- public boolean getFeatureEnabled(String feature) {
+ public boolean getFeatureEnabled(@NonNull String feature) {
Integer enabled = (Integer)mMap.get(KEY_FEATURE_ + feature);
if (enabled == null) {
throw new IllegalArgumentException("feature is not specified");
@@ -1057,39 +1213,239 @@
/**
* Sets the value of an integer key.
*/
- public final void setInteger(String name, int value) {
+ public final void setInteger(@NonNull String name, int value) {
mMap.put(name, Integer.valueOf(value));
}
/**
* Sets the value of a long key.
*/
- public final void setLong(String name, long value) {
+ public final void setLong(@NonNull String name, long value) {
mMap.put(name, Long.valueOf(value));
}
/**
* Sets the value of a float key.
*/
- public final void setFloat(String name, float value) {
+ public final void setFloat(@NonNull String name, float value) {
mMap.put(name, new Float(value));
}
/**
* Sets the value of a string key.
+ * <p>
+ * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
+ * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+ * remove a key.
*/
- public final void setString(String name, String value) {
+ public final void setString(@NonNull String name, @Nullable String value) {
mMap.put(name, value);
}
/**
* Sets the value of a ByteBuffer key.
+ * <p>
+ * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
+ * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+ * remove a key.
*/
- public final void setByteBuffer(String name, ByteBuffer bytes) {
+ public final void setByteBuffer(@NonNull String name, @Nullable ByteBuffer bytes) {
mMap.put(name, bytes);
}
/**
+ * Removes a value of a given key if present. Has no effect if the key is not present.
+ */
+ public final void removeKey(@NonNull String name) {
+ // exclude feature mappings
+ if (!name.startsWith(KEY_FEATURE_)) {
+ mMap.remove(name);
+ }
+ }
+
+ /**
+ * Removes a given feature setting if present. Has no effect if the feature setting is not
+ * present.
+ */
+ public final void removeFeature(@NonNull String name) {
+ mMap.remove(KEY_FEATURE_ + name);
+ }
+
+ /**
+ * A Partial set view for a portion of the keys in a MediaFormat object.
+ *
+ * This class is needed as we want to return a portion of the actual format keys in getKeys()
+ * and another portion of the keys in getFeatures(), and still allow the view properties.
+ */
+ private abstract class FilteredMappedKeySet extends AbstractSet<String> {
+ private Set<String> mKeys;
+
+ // Returns true if this set should include this key
+ abstract protected boolean keepKey(String key);
+
+ // Maps a key from the underlying key set into its new value in this key set
+ abstract protected String mapKeyToItem(String key);
+
+ // Maps a key from this key set into its original value in the underlying key set
+ abstract protected String mapItemToKey(String item);
+
+ public FilteredMappedKeySet() {
+ mKeys = mMap.keySet();
+ }
+
+ // speed up contains and remove from abstract implementation (that would iterate
+ // over each element)
+ @Override
+ public boolean contains(Object o) {
+ if (o instanceof String) {
+ String key = mapItemToKey((String)o);
+ return keepKey(key) && mKeys.contains(key);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (o instanceof String) {
+ String key = mapItemToKey((String)o);
+ if (keepKey(key) && mKeys.remove(key)) {
+ mMap.remove(key);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class KeyIterator implements Iterator<String> {
+ Iterator<String> mIterator;
+ String mLast;
+
+ public KeyIterator() {
+ // We must create a copy of the filtered stream, as remove operation has to modify
+ // the underlying data structure (mMap), so the iterator's operation is undefined.
+ // Use a list as it is likely less memory consuming than the other alternative: set.
+ mIterator =
+ mKeys.stream().filter(k -> keepKey(k)).collect(Collectors.toList()).iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return mIterator.hasNext();
+ }
+
+ @Override
+ public String next() {
+ mLast = mIterator.next();
+ return mapKeyToItem(mLast);
+ }
+
+ @Override
+ public void remove() {
+ mIterator.remove();
+ mMap.remove(mLast);
+ }
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return new KeyIterator();
+ }
+
+ @Override
+ public int size() {
+ return (int)mKeys.stream().filter(k -> keepKey(k)).count();
+ }
+ }
+
+ /**
+ * A Partial set view for a portion of the keys in a MediaFormat object for keys that
+ * don't start with a prefix, such as "feature-"
+ */
+ private class UnprefixedKeySet extends FilteredMappedKeySet {
+ private String mPrefix;
+
+ public UnprefixedKeySet(String prefix) {
+ super();
+ mPrefix = prefix;
+ }
+
+ protected boolean keepKey(String key) {
+ return !key.startsWith(mPrefix);
+ }
+
+ protected String mapKeyToItem(String key) {
+ return key;
+ }
+
+ protected String mapItemToKey(String item) {
+ return item;
+ }
+ }
+
+ /**
+ * A Partial set view for a portion of the keys in a MediaFormat object for keys that
+ * start with a prefix, such as "feature-", with the prefix removed
+ */
+ private class PrefixedKeySetWithPrefixRemoved extends FilteredMappedKeySet {
+ private String mPrefix;
+ private int mPrefixLength;
+
+ public PrefixedKeySetWithPrefixRemoved(String prefix) {
+ super();
+ mPrefix = prefix;
+ mPrefixLength = prefix.length();
+ }
+
+ protected boolean keepKey(String key) {
+ return key.startsWith(mPrefix);
+ }
+
+ protected String mapKeyToItem(String key) {
+ return key.substring(mPrefixLength);
+ }
+
+ protected String mapItemToKey(String item) {
+ return mPrefix + item;
+ }
+ }
+
+
+ /**
+ * Returns a {@link java.util.Set Set} view of the keys contained in this MediaFormat.
+ *
+ * The set is backed by the MediaFormat object, so changes to the format are reflected in the
+ * set, and vice-versa. If the format is modified while an iteration over the set is in progress
+ * (except through the iterator's own remove operation), the results of the iteration are
+ * undefined. The set supports element removal, which removes the corresponding mapping from the
+ * format, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
+ * It does not support the add or addAll operations.
+ */
+ public final @NonNull java.util.Set<String> getKeys() {
+ return new UnprefixedKeySet(KEY_FEATURE_);
+ }
+
+ /**
+ * Returns a {@link java.util.Set Set} view of the features contained in this MediaFormat.
+ *
+ * The set is backed by the MediaFormat object, so changes to the format are reflected in the
+ * set, and vice-versa. If the format is modified while an iteration over the set is in progress
+ * (except through the iterator's own remove operation), the results of the iteration are
+ * undefined. The set supports element removal, which removes the corresponding mapping from the
+ * format, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
+ * It does not support the add or addAll operations.
+ */
+ public final @NonNull java.util.Set<String> getFeatures() {
+ return new PrefixedKeySetWithPrefixRemoved(KEY_FEATURE_);
+ }
+
+ /**
+ * Create a copy of a media format object.
+ */
+ public MediaFormat(@NonNull MediaFormat other) {
+ mMap.putAll(other.mMap);
+ }
+
+ /**
* Sets whether a feature is to be enabled ({@code true}) or disabled
* ({@code false}).
*
@@ -1102,7 +1458,7 @@
* @see MediaCodecList#findEncoderForFormat
* @see MediaCodecInfo.CodecCapabilities#isFormatSupported
*/
- public void setFeatureEnabled(String feature, boolean enabled) {
+ public void setFeatureEnabled(@NonNull String feature, boolean enabled) {
setInteger(KEY_FEATURE_ + feature, enabled ? 1 : 0);
}
@@ -1112,8 +1468,8 @@
* @param sampleRate The sampling rate of the content.
* @param channelCount The number of audio channels in the content.
*/
- public static final MediaFormat createAudioFormat(
- String mime,
+ public static final @NonNull MediaFormat createAudioFormat(
+ @NonNull String mime,
int sampleRate,
int channelCount) {
MediaFormat format = new MediaFormat();
@@ -1132,8 +1488,8 @@
* in the content. (This will also work if there are multiple language
* tracks in the content.)
*/
- public static final MediaFormat createSubtitleFormat(
- String mime,
+ public static final @NonNull MediaFormat createSubtitleFormat(
+ @NonNull String mime,
String language) {
MediaFormat format = new MediaFormat();
format.setString(KEY_MIME, mime);
@@ -1148,8 +1504,8 @@
* @param width The width of the content (in pixels)
* @param height The height of the content (in pixels)
*/
- public static final MediaFormat createVideoFormat(
- String mime,
+ public static final @NonNull MediaFormat createVideoFormat(
+ @NonNull String mime,
int width,
int height) {
MediaFormat format = new MediaFormat();
@@ -1161,7 +1517,7 @@
}
@Override
- public String toString() {
+ public @NonNull String toString() {
return mMap.toString();
}
}
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 8288976..a10b212 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
@@ -28,6 +29,7 @@
import android.content.res.AssetFileDescriptor;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer2.DrmInfo;
import android.media.MediaPlayer2Proto.PlayerMessage;
import android.media.MediaPlayer2Proto.Value;
import android.net.Uri;
@@ -35,6 +37,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.util.Log;
@@ -45,8 +48,6 @@
import com.android.framework.protobuf.InvalidProtocolBufferException;
import com.android.internal.annotations.GuardedBy;
-import dalvik.system.CloseGuard;
-
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
@@ -70,7 +71,11 @@
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -298,21 +303,7 @@
private volatile float mVolume = 1.0f;
private VideoSize mVideoSize = new VideoSize(0, 0);
- // TODO: create per-source drm fields in SourceInfo
- // Modular DRM
- private final Object mDrmLock = new Object();
- //--- guarded by |mDrmLock| start
- private UUID mDrmUUID;
- private DrmInfo mDrmInfo;
- private MediaDrm mDrmObj;
- private byte[] mDrmSessionId;
- private boolean mDrmInfoResolved;
- private boolean mActiveDrmScheme;
- private boolean mDrmConfigAllowed;
- private boolean mDrmProvisioningInProgress;
- private boolean mPrepareDrmInProgress;
- private ProvisioningThread mDrmProvisioningThread;
- //--- guarded by |mDrmLock| end
+ private ExecutorService mDrmThreadPool = Executors.newCachedThreadPool();
// Creating a dummy audio track, used for keeping session id alive
private final Object mSessionIdLock = new Object();
@@ -326,6 +317,7 @@
private final List<Task> mPendingTasks = new LinkedList<>();
@GuardedBy("mTaskLock")
private Task mCurrentTask;
+ private final AtomicLong mTaskIdGenerator = new AtomicLong(0);
@GuardedBy("mTaskLock")
boolean mIsPreviousCommandSeekTo = false;
@@ -411,12 +403,13 @@
mHandlerThread = null;
}
+ clearSourceInfos();
+
// Modular DRM clean up
mOnDrmConfigHelper = null;
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.clear();
}
- resetDrmState();
native_release();
@@ -456,15 +449,8 @@
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.clear();
}
- synchronized (mSrcLock) {
- mCurrentSourceInfo = null;
- mNextSourceInfos.clear();
- }
- synchronized (mTaskLock) {
- mPendingTasks.clear();
- mIsPreviousCommandSeekTo = false;
- }
+ clearSourceInfos();
stayAwake(false);
native_reset();
@@ -478,7 +464,6 @@
mTaskHandler.removeCallbacksAndMessages(null);
}
- resetDrmState();
}
private native void native_reset();
@@ -684,6 +669,8 @@
/**
* Sets the data source as described by a DataSourceDesc.
+ * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+ * in the {@link FileDataSourceDesc} will be closed by the player.
*
* @param dsd the descriptor of data source you want to play
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -693,23 +680,30 @@
return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
void process() throws IOException {
- checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
int state = getState();
- if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
- throw new IllegalStateException("called in wrong state " + state);
- }
+ try {
+ if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
+ throw new IllegalStateException("called in wrong state " + state);
+ }
- synchronized (mSrcLock) {
- mCurrentSourceInfo = new SourceInfo(dsd);
- handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+ synchronized (mSrcLock) {
+ setCurrentSourceInfo_l(new SourceInfo(dsd));
+ handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+ }
+ } finally {
+ dsd.close();
}
}
+
});
}
/**
* Sets a single data source as described by a DataSourceDesc which will be played
* after current data source is finished.
+ * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+ * in the {@link FileDataSourceDesc} will be closed by the player.
*
* @param dsd the descriptor of data source you want to play after current one
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -719,9 +713,9 @@
return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
synchronized (mSrcLock) {
- mNextSourceInfos.clear();
+ clearNextSourceInfos_l();
mNextSourceInfos.add(new SourceInfo(dsd));
}
prepareNextDataSource();
@@ -731,6 +725,8 @@
/**
* Sets a list of data sources to be played sequentially after current data source is done.
+ * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+ * in the {@link FileDataSourceDesc} will be closed by the player.
*
* @param dsds the list of data sources you want to play after current one
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -743,17 +739,15 @@
if (dsds == null || dsds.size() == 0) {
throw new IllegalArgumentException("data source list cannot be null or empty.");
}
- for (DataSourceDesc dsd : dsds) {
- if (dsd == null) {
- throw new IllegalArgumentException(
- "DataSourceDesc in the source list cannot be null.");
- }
- }
synchronized (mSrcLock) {
- mNextSourceInfos.clear();
+ clearNextSourceInfos_l();
for (DataSourceDesc dsd : dsds) {
- mNextSourceInfos.add(new SourceInfo(dsd));
+ if (dsd != null) {
+ mNextSourceInfos.add(new SourceInfo(dsd));
+ } else {
+ Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
+ }
}
}
prepareNextDataSource();
@@ -770,7 +764,9 @@
return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
@Override
void process() {
- mNextSourceInfos.clear();
+ synchronized (mSrcLock) {
+ clearNextSourceInfos_l();
+ }
}
});
}
@@ -788,20 +784,20 @@
private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
throws IOException {
- checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
if (dsd instanceof CallbackDataSourceDesc) {
CallbackDataSourceDesc cbDSD = (CallbackDataSourceDesc) dsd;
handleDataSource(isCurrent,
srcId,
- cbDSD.getMedia2DataSource(),
+ cbDSD.getDataSourceCallback(),
cbDSD.getStartPosition(),
cbDSD.getEndPosition());
} else if (dsd instanceof FileDataSourceDesc) {
FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd;
handleDataSource(isCurrent,
srcId,
- fileDSD.getFileDescriptor(),
+ fileDSD.getParcelFileDescriptor(),
fileDSD.getOffset(),
fileDSD.getLength(),
fileDSD.getStartPosition(),
@@ -885,7 +881,7 @@
if (afd.getDeclaredLength() < 0) {
handleDataSource(isCurrent,
srcId,
- afd.getFileDescriptor(),
+ ParcelFileDescriptor.dup(afd.getFileDescriptor()),
0,
DataSourceDesc.LONG_MAX,
startPos,
@@ -893,7 +889,7 @@
} else {
handleDataSource(isCurrent,
srcId,
- afd.getFileDescriptor(),
+ ParcelFileDescriptor.dup(afd.getFileDescriptor()),
afd.getStartOffset(),
afd.getDeclaredLength(),
startPos,
@@ -959,7 +955,8 @@
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
FileDescriptor fd = is.getFD();
- handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos);
+ handleDataSource(isCurrent, srcId, ParcelFileDescriptor.dup(fd),
+ 0, DataSourceDesc.LONG_MAX, startPos, endPos);
is.close();
} else {
throw new IOException("handleDataSource failed.");
@@ -983,9 +980,10 @@
*/
private void handleDataSource(
boolean isCurrent, long srcId,
- FileDescriptor fd, long offset, long length,
+ ParcelFileDescriptor pfd, long offset, long length,
long startPos, long endPos) throws IOException {
- nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos);
+ nativeHandleDataSourceFD(isCurrent, srcId, pfd.getFileDescriptor(), offset, length,
+ startPos, endPos);
}
private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
@@ -994,15 +992,15 @@
/**
* @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
+ * @throws IllegalArgumentException if dataSource is not a valid DataSourceCallback
*/
- private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource,
+ private void handleDataSource(boolean isCurrent, long srcId, DataSourceCallback dataSource,
long startPos, long endPos) {
nativeHandleDataSourceCallback(isCurrent, srcId, dataSource, startPos, endPos);
}
private native void nativeHandleDataSourceCallback(
- boolean isCurrent, long srcId, Media2DataSource dataSource,
+ boolean isCurrent, long srcId, DataSourceCallback dataSource,
long startPos, long endPos);
// return true if there is a next data source, false otherwise.
@@ -1036,7 +1034,10 @@
MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
mTaskHandler.handleMessage(msg, nextSource.mId);
- mNextSourceInfos.poll();
+ SourceInfo nextSourceInfo = mNextSourceInfos.poll();
+ if (nextSource != null) {
+ nextSourceInfo.close();
+ }
return prepareNextDataSource();
}
}
@@ -1057,7 +1058,7 @@
SourceInfo nextSourceInfo = mNextSourceInfos.peek();
if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
// Switch to next source only when it has been prepared.
- mCurrentSourceInfo = mNextSourceInfos.poll();
+ setCurrentSourceInfo_l(mNextSourceInfos.poll());
long srcId = mCurrentSourceInfo.mId;
try {
@@ -1513,7 +1514,7 @@
return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
@Override
void process() {
- checkArgument(params != null, "the BufferingParams cannot be null");
+ Media2Utils.checkArgument(params != null, "the BufferingParams cannot be null");
native_setBufferingParams(params);
}
});
@@ -1536,7 +1537,7 @@
return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
@Override
void process() {
- checkArgument(params != null, "the PlaybackParams cannot be null");
+ Media2Utils.checkArgument(params != null, "the PlaybackParams cannot be null");
native_setPlaybackParams(params);
}
});
@@ -1564,7 +1565,7 @@
return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
@Override
void process() {
- checkArgument(params != null, "the SyncParams cannot be null");
+ Media2Utils.checkArgument(params != null, "the SyncParams cannot be null");
native_setSyncParams(params);
}
});
@@ -2157,7 +2158,7 @@
final int what = msg.arg1;
final int extra = msg.arg2;
- final SourceInfo sourceInfo = getSourceInfoById(srcId);
+ final SourceInfo sourceInfo = getSourceInfo(srcId);
if (sourceInfo == null) {
return;
}
@@ -2211,11 +2212,11 @@
Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
} else if (msg.obj instanceof byte[]) {
// The PlayerMessage was parsed already in postEventFromNative
- final DrmInfo drmInfo;
- synchronized (mDrmLock) {
- if (mDrmInfo != null) {
- drmInfo = mDrmInfo.makeCopy();
+ final DrmInfo drmInfo;
+ synchronized (sourceInfo) {
+ if (sourceInfo.mDrmInfo != null) {
+ drmInfo = sourceInfo.mDrmInfo.makeCopy();
} else {
drmInfo = null;
}
@@ -2287,7 +2288,7 @@
}
});
- SourceInfo src = getSourceInfoById(srcId);
+ SourceInfo src = getSourceInfo(srcId);
if (src != null) {
src.mBufferedPercentage.set(percent);
}
@@ -2488,6 +2489,7 @@
return;
}
+ final SourceInfo sourceInfo = mp.getSourceInfo(srcId);
switch (what) {
case MEDIA_DRM_INFO:
// We need to derive mDrmInfo before prepare() returns so processing it here
@@ -2495,7 +2497,7 @@
// notification looper so its handleMessage might process the event after prepare()
// has returned.
Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
- if (obj != null) {
+ if (obj != null && sourceInfo != null) {
PlayerMessage playerMsg;
try {
playerMsg = PlayerMessage.parseFrom(obj);
@@ -2504,11 +2506,12 @@
break;
}
DrmInfo drmInfo = new DrmInfo(playerMsg);
- synchronized (mp.mDrmLock) {
- mp.mDrmInfo = drmInfo;
+ synchronized (sourceInfo) {
+ sourceInfo.mDrmInfo = drmInfo;
}
} else {
- Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj);
+ Log.w(TAG, "MEDIA_DRM_INFO sourceInfo " + sourceInfo
+ + " msg.obj of unexpected type " + obj);
}
break;
@@ -2517,8 +2520,10 @@
// mainly for prepare() use case. For prepare(), this still can run to a race
// condition b/c MediaPlayerNative releases the prepare() lock before calling notify
// so we also set mDrmInfoResolved in prepare().
- synchronized (mp.mDrmLock) {
- mp.mDrmInfoResolved = true;
+ if (sourceInfo != null) {
+ synchronized (sourceInfo) {
+ sourceInfo.mDrmInfoResolved = true;
+ }
}
break;
}
@@ -2690,12 +2695,6 @@
}
}
- private static void checkArgument(boolean expression, String errorMessage) {
- if (!expression) {
- throw new IllegalArgumentException(errorMessage);
- }
- }
-
private void sendEvent(final EventNotifier notifier) {
synchronized (mEventCbLock) {
try {
@@ -3201,9 +3200,7 @@
*/
// This is a synchronous call.
public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
- synchronized (mDrmLock) {
- mOnDrmConfigHelper = listener;
- }
+ mOnDrmConfigHelper = listener;
}
private OnDrmConfigHelper mOnDrmConfigHelper;
@@ -3321,6 +3318,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface PrepareDrmStatusCode {}
+ /** @hide */
+ @IntDef({
+ MediaDrm.KEY_TYPE_STREAMING,
+ MediaDrm.KEY_TYPE_OFFLINE,
+ MediaDrm.KEY_TYPE_RELEASE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaDrmKeyType {}
+
+ /** @hide */
+ @StringDef({
+ MediaDrm.PROPERTY_VENDOR,
+ MediaDrm.PROPERTY_VERSION,
+ MediaDrm.PROPERTY_DESCRIPTION,
+ MediaDrm.PROPERTY_ALGORITHMS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaDrmStringProperty {}
+
/**
* Retrieves the DRM Info associated with the given source
*
@@ -3329,24 +3345,27 @@
* @throws IllegalStateException if called before being prepared
*/
public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) {
- // TODO: this implementation only works when dsd is the only data source
- DrmInfo drmInfo = null;
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ DrmInfo drmInfo = null;
- // there is not much point if the app calls getDrmInfo within an OnDrmInfoListenet;
- // regardless below returns drmInfo anyway instead of raising an exception
- synchronized (mDrmLock) {
- if (!mDrmInfoResolved && mDrmInfo == null) {
- final String msg = "The Player has not been prepared yet";
- Log.v(TAG, msg);
- throw new IllegalStateException(msg);
- }
+ // there is not much point if the app calls getDrmInfo within an OnDrmInfoListener;
+ // regardless below returns drmInfo anyway instead of raising an exception
+ synchronized (sourceInfo) {
+ if (!sourceInfo.mDrmInfoResolved && sourceInfo.mDrmInfo == null) {
+ final String msg = "The Player has not been prepared yet";
+ Log.v(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
- if (mDrmInfo != null) {
- drmInfo = mDrmInfo.makeCopy();
- }
- } // synchronized
+ if (sourceInfo.mDrmInfo != null) {
+ drmInfo = sourceInfo.mDrmInfo.makeCopy();
+ }
+ } // synchronized
- return drmInfo;
+ return drmInfo;
+ }
+ return null;
}
/**
@@ -3382,15 +3401,28 @@
*/
// This is an asynchronous call.
public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
- // TODO: this implementation only works when dsd is the only data source
return addTask(new Task(CALL_COMPLETED_PREPARE_DRM, true) {
@Override
void process() {
- int status = PREPARE_DRM_STATUS_SUCCESS;
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
boolean sendEvent = true;
+ if (sourceInfo == null) {
+ Log.e(TAG, "prepareDrm(): DataSource not found.");
+ } else if (sourceInfo.mDrmInfo == null) {
+ // only allowing if tied to a protected source;
+ // might relax for releasing offline keys
+ Log.e(TAG, "prepareDrm(): Wrong usage: The player must be prepared and "
+ + "DRM info be retrieved before this call.");
+ } else {
+ status = PREPARE_DRM_STATUS_SUCCESS;
+ }
+
try {
- doPrepareDrm(dsd, uuid);
+ if (status == PREPARE_DRM_STATUS_SUCCESS) {
+ sourceInfo.mDrmHandle.prepare(uuid);
+ }
} catch (ResourceBusyException e) {
status = PREPARE_DRM_STATUS_RESOURCE_BUSY;
} catch (UnsupportedSchemeException e) {
@@ -3399,14 +3431,14 @@
Log.w(TAG, "prepareDrm: NotProvisionedException");
// handle provisioning internally; it'll reset mPrepareDrmInProgress
- status = handleProvisioninig(dsd, uuid);
+ status = sourceInfo.mDrmHandle.handleProvisioninig(uuid, mTaskId);
if (status == PREPARE_DRM_STATUS_SUCCESS) {
// DrmEventCallback will be fired in provisioning
sendEvent = false;
} else {
- synchronized (mDrmLock) {
- cleanDrmObj();
+ synchronized (sourceInfo.mDrmHandle) {
+ sourceInfo.mDrmHandle.cleanDrmObj();
}
switch (status) {
@@ -3449,95 +3481,6 @@
});
}
- private void doPrepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid)
- throws UnsupportedSchemeException, ResourceBusyException,
- NotProvisionedException {
- Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
-
- synchronized (mDrmLock) {
- // only allowing if tied to a protected source; might relax for releasing offline keys
- if (mDrmInfo == null) {
- final String msg = "prepareDrm(): Wrong usage: The player must be prepared and "
- + "DRM info be retrieved before this call.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mActiveDrmScheme) {
- final String msg = "prepareDrm(): Wrong usage: There is already "
- + "an active DRM scheme with " + mDrmUUID;
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mPrepareDrmInProgress) {
- final String msg = "prepareDrm(): Wrong usage: There is already "
- + "a pending prepareDrm call.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mDrmProvisioningInProgress) {
- final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- // shouldn't need this; just for safeguard
- cleanDrmObj();
-
- mPrepareDrmInProgress = true;
-
- try {
- // only creating the DRM object to allow pre-openSession configuration
- prepareDrm_createDrmStep(uuid);
- } catch (Exception e) {
- Log.w(TAG, "prepareDrm(): Exception ", e);
- mPrepareDrmInProgress = false;
- throw e;
- }
-
- mDrmConfigAllowed = true;
- } // synchronized
-
- // call the callback outside the lock
- if (mOnDrmConfigHelper != null) {
- mOnDrmConfigHelper.onDrmConfig(this, dsd);
- }
-
- synchronized (mDrmLock) {
- mDrmConfigAllowed = false;
- boolean earlyExit = false;
-
- try {
- prepareDrm_openSessionStep(uuid);
-
- mDrmUUID = uuid;
- mActiveDrmScheme = true;
- mPrepareDrmInProgress = false;
- } catch (IllegalStateException e) {
- final String msg = "prepareDrm(): Wrong usage: The player must be "
- + "in the prepared state to call prepareDrm().";
- Log.e(TAG, msg);
- earlyExit = true;
- mPrepareDrmInProgress = false;
- throw new IllegalStateException(msg);
- } catch (NotProvisionedException e) {
- Log.w(TAG, "prepareDrm: NotProvisionedException", e);
- throw e;
- } catch (Exception e) {
- Log.e(TAG, "prepareDrm: Exception " + e);
- earlyExit = true;
- mPrepareDrmInProgress = false;
- throw e;
- } finally {
- if (earlyExit) { // clean up object if didn't succeed
- cleanDrmObj();
- }
- } // finally
- } // synchronized
- }
-
/**
* Releases the DRM session for the given data source
* <p>
@@ -3552,35 +3495,10 @@
// This is a synchronous call.
public void releaseDrm(@NonNull DataSourceDesc dsd)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
- synchronized (mDrmLock) {
- Log.v(TAG, "releaseDrm:");
-
- if (!mActiveDrmScheme) {
- Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
- throw new NoDrmSchemeException(
- "releaseDrm: No active DRM scheme to release.");
- }
-
- try {
- // we don't have the player's state in this layer. The below call raises
- // exception if we're in a non-stopped/prepared state.
-
- // for cleaning native/mediaserver crypto object
- native_releaseDrm();
-
- // for cleaning client-side MediaDrm object; only called if above has succeeded
- cleanDrmObj();
-
- mActiveDrmScheme = false;
- } catch (IllegalStateException e) {
- Log.w(TAG, "releaseDrm: Exception ", e);
- throw new IllegalStateException(
- "releaseDrm: The player is not in a valid state.");
- } catch (Exception e) {
- Log.e(TAG, "releaseDrm: Exception ", e);
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.release();
+ }
}
private native void native_releaseDrm();
@@ -3624,51 +3542,22 @@
*
* @throws NoDrmSchemeException if there is no active DRM session
*/
- @NonNull
public MediaDrm.KeyRequest getDrmKeyRequest(
@NonNull DataSourceDesc dsd,
@Nullable byte[] keySetId, @Nullable byte[] initData,
- @Nullable String mimeType, @MediaDrm.KeyType int keyType,
+ @Nullable String mimeType, @MediaDrmKeyType int keyType,
@Nullable Map<String, String> optionalParameters)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
- Log.v(TAG, "getDrmKeyRequest: "
- + " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType
- + " keyType: " + keyType + " optionalParameters: " + optionalParameters);
+ Log.v(TAG, "getDrmKeyRequest: " +
+ " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
+ " keyType: " + keyType + " optionalParameters: " + optionalParameters);
- synchronized (mDrmLock) {
- if (!mActiveDrmScheme) {
- Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmKeyRequest: Has to set a DRM scheme first.");
- }
-
- try {
- byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE)
- ? mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- keySetId; // keySetId for KEY_TYPE_RELEASE
-
- HashMap<String, String> hmapOptionalParameters =
- (optionalParameters != null)
- ? new HashMap<String, String>(optionalParameters) :
- null;
-
- MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
- keyType, hmapOptionalParameters);
- Log.v(TAG, "getDrmKeyRequest: --> request: " + request);
-
- return request;
-
- } catch (NotProvisionedException e) {
- Log.w(TAG, "getDrmKeyRequest NotProvisionedException: "
- + "Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("getDrmKeyRequest: Unexpected provisioning error.");
- } catch (Exception e) {
- Log.w(TAG, "getDrmKeyRequest Exception " + e);
- throw e;
- }
-
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.getDrmKeyRequest(
+ keySetId, initData, mimeType, keyType, optionalParameters);
+ }
+ return null;
}
/**
@@ -3698,40 +3587,13 @@
@NonNull DataSourceDesc dsd,
@Nullable byte[] keySetId, @NonNull byte[] response)
throws NoDrmSchemeException, DeniedByServerException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response);
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme) {
- Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmKeyRequest: Has to set a DRM scheme first.");
- }
-
- try {
- byte[] scope = (keySetId == null)
- ? mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- keySetId; // keySetId for KEY_TYPE_RELEASE
-
- byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
-
- Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response
- + " --> " + keySetResult);
-
-
- return keySetResult;
-
- } catch (NotProvisionedException e) {
- Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: "
- + "Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("provideDrmKeyResponse: "
- + "Unexpected provisioning error.");
- } catch (Exception e) {
- Log.w(TAG, "provideDrmKeyResponse Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.provideDrmKeyResponse(keySetId, response);
+ }
+ return null;
}
/**
@@ -3750,23 +3612,12 @@
@NonNull DataSourceDesc dsd,
@NonNull byte[] keySetId)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
- synchronized (mDrmLock) {
- if (!mActiveDrmScheme) {
- Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "restoreDrmKeys: Has to set a DRM scheme first.");
- }
-
- try {
- mDrmObj.restoreKeys(mDrmSessionId, keySetId);
- } catch (Exception e) {
- Log.w(TAG, "restoreKeys Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.restoreDrmKeys(keySetId);
+ }
}
/**
@@ -3783,34 +3634,17 @@
*
* @throws NoDrmSchemeException if there is no active DRM session
*/
- @NonNull
public String getDrmPropertyString(
@NonNull DataSourceDesc dsd,
- @NonNull @MediaDrm.StringProperty String propertyName)
+ @NonNull @MediaDrmStringProperty String propertyName)
throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName);
- String value;
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme && !mDrmConfigAllowed) {
- Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmPropertyString: Has to prepareDrm() first.");
- }
-
- try {
- value = mDrmObj.getPropertyString(propertyName);
- } catch (Exception e) {
- Log.w(TAG, "getDrmPropertyString Exception " + e);
- throw e;
- }
- } // synchronized
-
- Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value);
-
- return value;
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ return sourceInfo.mDrmHandle.getDrmPropertyString(propertyName);
+ }
+ return null;
}
/**
@@ -3829,26 +3663,15 @@
// This is a synchronous call.
public void setDrmPropertyString(
@NonNull DataSourceDesc dsd,
- @NonNull @MediaDrm.StringProperty String propertyName, @NonNull String value)
+ @NonNull @MediaDrmStringProperty String propertyName, @NonNull String value)
throws NoDrmSchemeException {
// TODO: this implementation only works when dsd is the only data source
Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value);
- synchronized (mDrmLock) {
-
- if (!mActiveDrmScheme && !mDrmConfigAllowed) {
- Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "setDrmPropertyString: Has to prepareDrm() first.");
- }
-
- try {
- mDrmObj.setPropertyString(propertyName, value);
- } catch (Exception e) {
- Log.w(TAG, "setDrmPropertyString Exception " + e);
- throw e;
- }
- } // synchronized
+ final SourceInfo sourceInfo = getSourceInfo(dsd);
+ if (sourceInfo != null) {
+ sourceInfo.mDrmHandle.setDrmPropertyString(propertyName, value);
+ }
}
/**
@@ -4000,43 +3823,6 @@
private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
- // Modular DRM helpers
-
- private void prepareDrm_createDrmStep(@NonNull UUID uuid)
- throws UnsupportedSchemeException {
- Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
-
- try {
- mDrmObj = new MediaDrm(uuid);
- Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
- } catch (Exception e) { // UnsupportedSchemeException
- Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
- throw e;
- }
- }
-
- private void prepareDrm_openSessionStep(@NonNull UUID uuid)
- throws NotProvisionedException, ResourceBusyException {
- Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
-
- // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do
- // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
- // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
- try {
- mDrmSessionId = mDrmObj.openSession();
- Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
-
- // Sending it down to native/mediaserver to create the crypto object
- // This call could simply fail due to bad player state, e.g., after play().
- native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
- Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
-
- } catch (Exception e) { //ResourceBusyException, NotProvisionedException
- Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
- throw e;
- }
- }
-
// Instantiated from the native side
@SuppressWarnings("unused")
private static class StreamEventCallback extends AudioTrack.StreamEventCallback {
@@ -4068,227 +3854,28 @@
}
}
- private class ProvisioningThread extends Thread {
- public static final int TIMEOUT_MS = 60000;
-
- private final DataSourceDesc mDSD;
- private UUID mUuid;
- private String mUrlStr;
- private Object mDrmLock;
- private MediaPlayer2 mMediaPlayer;
- private int mStatus;
- public int status() {
- return mStatus;
- }
-
- public ProvisioningThread(MediaDrm.ProvisionRequest request,
- DataSourceDesc dsd,
- UUID uuid, MediaPlayer2 mediaPlayer) {
- // lock is held by the caller
- mDSD = dsd;
- mDrmLock = mediaPlayer.mDrmLock;
- mMediaPlayer = mediaPlayer;
-
- mUrlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
- mUuid = uuid;
-
- mStatus = PREPARE_DRM_STATUS_PREPARATION_ERROR;
-
- Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + mUrlStr);
- }
-
- public void run() {
-
- byte[] response = null;
- boolean provisioningSucceeded = false;
- try {
- URL url = new URL(mUrlStr);
- final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- try {
- connection.setRequestMethod("POST");
- connection.setDoOutput(false);
- connection.setDoInput(true);
- connection.setConnectTimeout(TIMEOUT_MS);
- connection.setReadTimeout(TIMEOUT_MS);
-
- connection.connect();
- response = readInputStreamFully(connection.getInputStream());
-
- Log.v(TAG, "handleProvisioninig: Thread run: response "
- + response.length + " " + response);
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
- } finally {
- connection.disconnect();
- }
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
- }
-
- if (response != null) {
- try {
- mDrmObj.provideProvisionResponse(response);
- Log.v(TAG, "handleProvisioninig: Thread run: "
- + "provideProvisionResponse SUCCEEDED!");
-
- provisioningSucceeded = true;
- } catch (Exception e) {
- mStatus = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: "
- + "provideProvisionResponse " + e);
- }
- }
-
- boolean succeeded = false;
-
- synchronized (mDrmLock) {
- // continuing with prepareDrm
- if (provisioningSucceeded) {
- succeeded = mMediaPlayer.resumePrepareDrm(mUuid);
- mStatus = (succeeded)
- ? PREPARE_DRM_STATUS_SUCCESS :
- PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
- mMediaPlayer.mDrmProvisioningInProgress = false;
- mMediaPlayer.mPrepareDrmInProgress = false;
- if (!succeeded) {
- cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock
- }
- } // synchronized
-
- // calling the callback outside the lock
- sendDrmEvent(new DrmEventNotifier() {
- @Override
- public void notify(DrmEventCallback callback) {
- callback.onDrmPrepared(
- mMediaPlayer, mDSD, mStatus);
- }
- });
-
- synchronized (mTaskLock) {
- if (mCurrentTask != null
- && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
- && mCurrentTask.mNeedToWaitForEventToComplete) {
- mCurrentTask = null;
- processPendingTask_l();
- }
- }
- }
-
- /**
- * Returns a byte[] containing the remainder of 'in', closing it when done.
- */
- private byte[] readInputStreamFully(InputStream in) throws IOException {
- try {
- return readInputStreamFullyNoClose(in);
- } finally {
- in.close();
- }
- }
-
- /**
- * Returns a byte[] containing the remainder of 'in'.
- */
- private byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int count;
- while ((count = in.read(buffer)) != -1) {
- bytes.write(buffer, 0, count);
- }
- return bytes.toByteArray();
- }
- } // ProvisioningThread
-
- private int handleProvisioninig(DataSourceDesc dsd, UUID uuid) {
- synchronized (mDrmLock) {
- if (mDrmProvisioningInProgress) {
- Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
- return PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
- if (provReq == null) {
- Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
- return PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- Log.v(TAG, "handleProvisioninig provReq "
- + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
-
- // networking in a background thread
- mDrmProvisioningInProgress = true;
-
- mDrmProvisioningThread = new ProvisioningThread(provReq, dsd, uuid, this);
- mDrmProvisioningThread.start();
-
- return PREPARE_DRM_STATUS_SUCCESS;
- }
- }
-
- private boolean resumePrepareDrm(UUID uuid) {
- Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
-
- // mDrmLock is guaranteed to be held
- boolean success = false;
+ /**
+ * Returns a byte[] containing the remainder of 'in', closing it when done.
+ */
+ private static byte[] readInputStreamFully(InputStream in) throws IOException {
try {
- // resuming
- prepareDrm_openSessionStep(uuid);
-
- mDrmUUID = uuid;
- mActiveDrmScheme = true;
-
- success = true;
- } catch (Exception e) {
- Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed with " + e);
- // mDrmObj clean up is done by the caller
+ return readInputStreamFullyNoClose(in);
+ } finally {
+ in.close();
}
-
- return success;
}
- private void resetDrmState() {
- synchronized (mDrmLock) {
- Log.v(TAG, "resetDrmState:"
- + " mDrmInfo=" + mDrmInfo
- + " mDrmProvisioningThread=" + mDrmProvisioningThread
- + " mPrepareDrmInProgress=" + mPrepareDrmInProgress
- + " mActiveDrmScheme=" + mActiveDrmScheme);
-
- mDrmInfoResolved = false;
- mDrmInfo = null;
-
- if (mDrmProvisioningThread != null) {
- // timeout; relying on HttpUrlConnection
- try {
- mDrmProvisioningThread.join();
- } catch (InterruptedException e) {
- Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
- }
- mDrmProvisioningThread = null;
- }
-
- mPrepareDrmInProgress = false;
- mActiveDrmScheme = false;
-
- cleanDrmObj();
- } // synchronized
- }
-
- private void cleanDrmObj() {
- // the caller holds mDrmLock
- Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
-
- if (mDrmSessionId != null) {
- mDrmObj.closeSession(mDrmSessionId);
- mDrmSessionId = null;
+ /**
+ * Returns a byte[] containing the remainder of 'in'.
+ */
+ private static byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
}
- if (mDrmObj != null) {
- mDrmObj.release();
- mDrmObj = null;
- }
+ return bytes.toByteArray();
}
private static byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
@@ -4304,8 +3891,6 @@
return uuidBytes;
}
- // Modular DRM end
-
private static class TimedTextUtil {
// These keys must be in sync with the keys in TextDescription2.h
private static final int KEY_START_TIME = 7; // int
@@ -4381,6 +3966,7 @@
}
private abstract class Task implements Runnable {
+ final long mTaskId = mTaskIdGenerator.getAndIncrement();
private final int mMediaCallType;
private final boolean mNeedToWaitForEventToComplete;
private DataSourceDesc mDSD;
@@ -4472,10 +4058,507 @@
}
};
- private final class SourceInfo {
+ // Modular DRM
+ final class DrmHandle {
+
+ static final int PROVISION_TIMEOUT_MS = 60000;
+
+ final DataSourceDesc mDSD;
+
+ //--- guarded by |this| start
+ MediaDrm mDrmObj;
+ byte[] mDrmSessionId;
+ UUID mActiveDrmUUID;
+ boolean mDrmConfigAllowed;
+ boolean mDrmProvisioningInProgress;
+ boolean mPrepareDrmInProgress;
+ Future<?> mProvisionResult;
+ //--- guarded by |this| end
+
+ DrmHandle(DataSourceDesc dsd) {
+ mDSD = dsd;
+ }
+
+ void prepare(UUID uuid) throws UnsupportedSchemeException,
+ ResourceBusyException, NotProvisionedException {
+ final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper;
+ Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper);
+
+ synchronized (this) {
+ if (mActiveDrmUUID != null) {
+ final String msg = "prepareDrm(): Wrong usage: There is already "
+ + "an active DRM scheme with " + uuid;
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ if (mPrepareDrmInProgress) {
+ final String msg = "prepareDrm(): Wrong usage: There is already "
+ + "a pending prepareDrm call.";
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ if (mDrmProvisioningInProgress) {
+ final String msg = "prepareDrm(): Unexpectd: Provisioning already in progress";
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
+
+ // shouldn't need this; just for safeguard
+ cleanDrmObj();
+
+ mPrepareDrmInProgress = true;
+
+ try {
+ // only creating the DRM object to allow pre-openSession configuration
+ prepareDrm_createDrmStep(uuid);
+ } catch (Exception e) {
+ Log.w(TAG, "prepareDrm(): Exception ", e);
+ mPrepareDrmInProgress = false;
+ throw e;
+ }
+
+ mDrmConfigAllowed = true;
+ } // synchronized
+
+ // call the callback outside the lock
+ if (onDrmConfigHelper != null) {
+ onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD);
+ }
+
+ synchronized (this) {
+ mDrmConfigAllowed = false;
+ boolean earlyExit = false;
+
+ try {
+ prepareDrm_openSessionStep(uuid);
+
+ this.mActiveDrmUUID = uuid;
+ mPrepareDrmInProgress = false;
+ } catch (IllegalStateException e) {
+ final String msg = "prepareDrm(): Wrong usage: The player must be "
+ + "in the prepared state to call prepareDrm().";
+ Log.e(TAG, msg);
+ earlyExit = true;
+ mPrepareDrmInProgress = false;
+ throw new IllegalStateException(msg);
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "prepareDrm: NotProvisionedException", e);
+ throw e;
+ } catch (Exception e) {
+ Log.e(TAG, "prepareDrm: Exception " + e);
+ earlyExit = true;
+ mPrepareDrmInProgress = false;
+ throw e;
+ } finally {
+ if (earlyExit) { // clean up object if didn't succeed
+ cleanDrmObj();
+ }
+ } // finally
+ } // synchronized
+ }
+
+ void prepareDrm_createDrmStep(UUID uuid)
+ throws UnsupportedSchemeException {
+ Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
+
+ try {
+ mDrmObj = new MediaDrm(uuid);
+ Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
+ } catch (Exception e) { // UnsupportedSchemeException
+ Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
+ throw e;
+ }
+ }
+
+ void prepareDrm_openSessionStep(UUID uuid)
+ throws NotProvisionedException, ResourceBusyException {
+ Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
+
+ // TODO:
+ // don't need an open session for a future specialKeyReleaseDrm mode but we should do
+ // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
+ // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
+ try {
+ mDrmSessionId = mDrmObj.openSession();
+ Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
+
+ // Sending it down to native/mediaserver to create the crypto object
+ // This call could simply fail due to bad player state, e.g., after play().
+ MediaPlayer2.this.native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
+ Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
+
+ } catch (Exception e) { //ResourceBusyException, NotProvisionedException
+ Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
+ throw e;
+ }
+
+ }
+
+ int handleProvisioninig(UUID uuid, long taskId) {
+ synchronized (this) {
+ if (mDrmProvisioningInProgress) {
+ Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
+ return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+
+ MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+ if (provReq == null) {
+ Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
+ return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+
+ Log.v(TAG, "handleProvisioninig provReq "
+ + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
+
+ // networking in a background thread
+ mDrmProvisioningInProgress = true;
+
+ mProvisionResult = mDrmThreadPool.submit(newProvisioningTask(uuid, taskId));
+
+ return PREPARE_DRM_STATUS_SUCCESS;
+ }
+ }
+
+ void provision(UUID uuid, long taskId) {
+
+ MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+ String urlStr = provReq.getDefaultUrl();
+ urlStr += "&signedRequest=" + new String(provReq.getData());
+ Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + urlStr);
+
+ byte[] response = null;
+ boolean provisioningSucceeded = false;
+ int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ try {
+ URL url = new URL(urlStr);
+ final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ try {
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(false);
+ connection.setDoInput(true);
+ connection.setConnectTimeout(PROVISION_TIMEOUT_MS);
+ connection.setReadTimeout(PROVISION_TIMEOUT_MS);
+
+ connection.connect();
+ response = readInputStreamFully(connection.getInputStream());
+
+ Log.v(TAG, "handleProvisioninig: Thread run: response " +
+ response.length + " " + response);
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
+ } finally {
+ connection.disconnect();
+ }
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
+ }
+
+ if (response != null) {
+ try {
+ mDrmObj.provideProvisionResponse(response);
+ Log.v(TAG, "handleProvisioninig: Thread run: " +
+ "provideProvisionResponse SUCCEEDED!");
+
+ provisioningSucceeded = true;
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
+ Log.w(TAG, "handleProvisioninig: Thread run: " +
+ "provideProvisionResponse " + e);
+ }
+ }
+
+ boolean succeeded = false;
+
+ synchronized (this) {
+ // continuing with prepareDrm
+ if (provisioningSucceeded) {
+ succeeded = resumePrepare(uuid);
+ status = (succeeded) ?
+ PREPARE_DRM_STATUS_SUCCESS :
+ PREPARE_DRM_STATUS_PREPARATION_ERROR;
+ }
+ mDrmProvisioningInProgress = false;
+ mPrepareDrmInProgress = false;
+ if (!succeeded) {
+ cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock
+ }
+ } // synchronized
+
+ // calling the callback outside the lock
+ final int finalStatus = status;
+ sendDrmEvent(new DrmEventNotifier() {
+ @Override
+ public void notify(DrmEventCallback callback) {
+ callback.onDrmPrepared(
+ MediaPlayer2.this, mDSD, finalStatus);
+ }
+ });
+
+ synchronized (mTaskLock) {
+ if (mCurrentTask != null
+ && mCurrentTask.mTaskId == taskId
+ && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
+ && mCurrentTask.mNeedToWaitForEventToComplete) {
+ mCurrentTask = null;
+ processPendingTask_l();
+ }
+ }
+ }
+
+ Runnable newProvisioningTask(UUID uuid, long taskId) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ provision(uuid, taskId);
+ }
+ };
+ }
+
+ boolean resumePrepare(UUID uuid) {
+ Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
+
+ // mDrmLock is guaranteed to be held
+ boolean success = false;
+ try {
+ // resuming
+ prepareDrm_openSessionStep(uuid);
+
+ this.mActiveDrmUUID = uuid;
+
+ success = true;
+ } catch (Exception e) {
+ Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed:" + e);
+ // mDrmObj clean up is done by the caller
+ }
+
+ return success;
+ }
+
+ void cleanDrmObj() {
+ // the caller holds mDrmLock
+ Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
+
+ if (mDrmSessionId != null) {
+ mDrmObj.closeSession(mDrmSessionId);
+ mDrmSessionId = null;
+ }
+ if (mDrmObj != null) {
+ mDrmObj.close();
+ mDrmObj = null;
+ }
+ }
+
+ void release() throws NoDrmSchemeException {
+ synchronized (this) {
+ Log.v(TAG, "releaseDrm:");
+
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
+ throw new NoDrmSchemeException(
+ "releaseDrm: No active DRM scheme to release.");
+ }
+
+ try {
+ // we don't have the player's state in this layer. The below call raises
+ // exception if we're in a non-stopped/prepared state.
+
+ // for cleaning native/mediaserver crypto object
+ native_releaseDrm();
+
+ // for cleaning client-side MediaDrm object; only called if above has succeeded
+ cleanDrmObj();
+
+ this.mActiveDrmUUID = null;
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "releaseDrm: Exception ", e);
+ throw new IllegalStateException(
+ "releaseDrm: The player is not in a valid state.");
+ } catch (Exception e) {
+ Log.e(TAG, "releaseDrm: Exception ", e);
+ }
+ } // synchronized
+ }
+
+ void cleanup() {
+ synchronized (this) {
+ Log.v(TAG, "cleanupDrm: " +
+ " mProvisioningTask=" + mProvisionResult +
+ " mPrepareDrmInProgress=" + mPrepareDrmInProgress +
+ " mActiveDrmScheme=" + mActiveDrmUUID);
+
+ if (mProvisionResult != null) {
+ // timeout; relying on HttpUrlConnection
+ try {
+ mProvisionResult.get();
+ }
+ catch (InterruptedException | ExecutionException e) {
+ Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
+ }
+ }
+
+ // set to false to avoid duplicate release calls
+ this.mActiveDrmUUID = null;
+
+ cleanDrmObj();
+ } // synchronized
+ }
+
+ Runnable newCleanupTask() {
+ return new Runnable() {
+ @Override
+ public void run() {
+ cleanup();
+ }
+ };
+ }
+
+ MediaDrm.KeyRequest getDrmKeyRequest(
+ byte[] keySetId, byte[] initData,
+ String mimeType, int keyType,
+ Map<String, String> optionalParameters)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmKeyRequest: Has to set a DRM scheme first.");
+ }
+
+ try {
+ byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
+ mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+ keySetId; // keySetId for KEY_TYPE_RELEASE
+
+ HashMap<String, String> hmapOptionalParameters =
+ (optionalParameters != null)
+ ? new HashMap<String, String>(optionalParameters)
+ : null;
+
+ MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(
+ scope, initData, mimeType, keyType, hmapOptionalParameters);
+ Log.v(TAG, "getDrmKeyRequest: --> request: " + request);
+
+ return request;
+
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " +
+ "Unexpected. Shouldn't have reached here.");
+ throw new IllegalStateException("getDrmKeyRequest: provisioning error.");
+ } catch (Exception e) {
+ Log.w(TAG, "getDrmKeyRequest Exception " + e);
+ throw e;
+ }
+
+ }
+ }
+
+ byte[] provideDrmKeyResponse(byte[] keySetId, byte[] response)
+ throws NoDrmSchemeException, DeniedByServerException {
+ synchronized (this) {
+
+ if (mActiveDrmUUID == null) {
+ Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmKeyRequest: Has to set a DRM scheme first.");
+ }
+
+ try {
+ byte[] scope = (keySetId == null) ?
+ mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+ keySetId; // keySetId for KEY_TYPE_RELEASE
+
+ byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
+
+ Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId
+ + " response: " + response + " --> " + keySetResult);
+
+
+ return keySetResult;
+
+ } catch (NotProvisionedException e) {
+ Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " +
+ "Unexpected. Shouldn't have reached here.");
+ throw new IllegalStateException("provideDrmKeyResponse: " +
+ "Unexpected provisioning error.");
+ } catch (Exception e) {
+ Log.w(TAG, "provideDrmKeyResponse Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ void restoreDrmKeys(byte[] keySetId)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+ if (mActiveDrmUUID == null) {
+ Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "restoreDrmKeys: Has to set a DRM scheme first.");
+ }
+
+ try {
+ mDrmObj.restoreKeys(mDrmSessionId, keySetId);
+ } catch (Exception e) {
+ Log.w(TAG, "restoreKeys Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ String getDrmPropertyString(String propertyName)
+ throws NoDrmSchemeException {
+ String v;
+ synchronized (this) {
+
+ if (mActiveDrmUUID == null && !mDrmConfigAllowed) {
+ Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "getDrmPropertyString: Has to prepareDrm() first.");
+ }
+
+ try {
+ v = mDrmObj.getPropertyString(propertyName);
+ } catch (Exception e) {
+ Log.w(TAG, "getDrmPropertyString Exception " + e);
+ throw e;
+ }
+ } // synchronized
+
+ Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + v);
+
+ return v;
+ }
+
+ void setDrmPropertyString(String propertyName, String value)
+ throws NoDrmSchemeException {
+ synchronized (this) {
+
+ if ( mActiveDrmUUID == null && !mDrmConfigAllowed ) {
+ Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
+ throw new NoDrmSchemeException(
+ "setDrmPropertyString: Has to prepareDrm() first.");
+ }
+
+ try {
+ mDrmObj.setPropertyString(propertyName, value);
+ } catch ( Exception e ) {
+ Log.w(TAG, "setDrmPropertyString Exception " + e);
+ throw e;
+ }
+ }
+ }
+
+ }
+
+ final class SourceInfo {
final DataSourceDesc mDSD;
final long mId = mSrcIdGenerator.getAndIncrement();
AtomicInteger mBufferedPercentage = new AtomicInteger(0);
+ boolean mClosed = false;
// m*AsNextSource (below) only applies to pending data sources in the playlist;
// the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
@@ -4483,8 +4566,25 @@
int mStateAsNextSource = NEXT_SOURCE_STATE_INIT;
boolean mPlayPendingAsNextSource = false;
+ // Modular DRM
+ final DrmHandle mDrmHandle;
+ DrmInfo mDrmInfo;
+ boolean mDrmInfoResolved;
+
SourceInfo(DataSourceDesc dsd) {
this.mDSD = dsd;
+ mDrmHandle = new DrmHandle(dsd);
+ }
+
+ void close() {
+ synchronized (this) {
+ if (!mClosed) {
+ if (mDSD != null) {
+ mDSD.close();
+ }
+ mClosed = true;
+ }
+ }
}
@Override
@@ -4494,7 +4594,7 @@
}
- private SourceInfo getSourceInfoById(long srcId) {
+ private SourceInfo getSourceInfo(long srcId) {
synchronized (mSrcLock) {
if (isCurrentSource(srcId)) {
return mCurrentSourceInfo;
@@ -4506,17 +4606,68 @@
return null;
}
+ private SourceInfo getSourceInfo(DataSourceDesc dsd) {
+ synchronized (mSrcLock) {
+ if (isCurrentSource(dsd)) {
+ return mCurrentSourceInfo;
+ }
+ if (isNextSource(dsd)) {
+ return mNextSourceInfos.peek();
+ }
+ }
+ return null;
+ }
+
private boolean isCurrentSource(long srcId) {
synchronized (mSrcLock) {
return mCurrentSourceInfo != null && mCurrentSourceInfo.mId == srcId;
}
}
+ private boolean isCurrentSource(DataSourceDesc dsd) {
+ synchronized (mSrcLock) {
+ return mCurrentSourceInfo != null && mCurrentSourceInfo.mDSD == dsd;
+ }
+ }
+
private boolean isNextSource(long srcId) {
SourceInfo nextSourceInfo = mNextSourceInfos.peek();
return nextSourceInfo != null && nextSourceInfo.mId == srcId;
}
+ private boolean isNextSource(DataSourceDesc dsd) {
+ SourceInfo nextSourceInfo = mNextSourceInfos.peek();
+ return nextSourceInfo != null && nextSourceInfo.mDSD == dsd;
+ }
+
+ @GuardedBy("mSrcLock")
+ private void setCurrentSourceInfo_l(SourceInfo sourceInfo) {
+ cleanupSourceInfo(mCurrentSourceInfo);
+ mCurrentSourceInfo = sourceInfo;
+ }
+
+ @GuardedBy("mSrcLock")
+ private void clearNextSourceInfos_l() {
+ while (!mNextSourceInfos.isEmpty()) {
+ cleanupSourceInfo(mNextSourceInfos.poll());
+ }
+ }
+
+ private void cleanupSourceInfo(SourceInfo sourceInfo) {
+ if (sourceInfo != null) {
+ sourceInfo.close();
+ Runnable task = sourceInfo.mDrmHandle.newCleanupTask();
+ mDrmThreadPool.submit(task);
+ }
+ }
+
+ private void clearSourceInfos() {
+ synchronized (mSrcLock) {
+ setCurrentSourceInfo_l(null);
+ clearNextSourceInfos_l();
+ }
+ }
+
public static final class MetricsConstants {
private MetricsConstants() {}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 3a64f43..90cfc53 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -156,7 +156,8 @@
private static final String NOTIFICATIONS_DIR = "/notifications/";
private static final String ALARMS_DIR = "/alarms/";
private static final String MUSIC_DIR = "/music/";
- private static final String PODCAST_DIR = "/podcasts/";
+ private static final String PODCASTS_DIR = "/podcasts/";
+ private static final String AUDIOBOOKS_DIR = "/audiobooks/";
public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
@@ -654,7 +655,7 @@
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
if (noMedia) {
- result = endFile(entry, false, false, false, false, false);
+ result = endFile(entry, false, false, false, false, false, false);
} else {
boolean isaudio = MediaFile.isAudioMimeType(mMimeType);
boolean isvideo = MediaFile.isVideoMimeType(mMimeType);
@@ -679,11 +680,13 @@
boolean notifications = mScanSuccess &&
(lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
- boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCAST_DIR) > 0);
+ boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCASTS_DIR) > 0);
+ boolean audiobooks = mScanSuccess && (lowpath.indexOf(AUDIOBOOKS_DIR) > 0);
boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
- (!ringtones && !notifications && !alarms && !podcasts));
+ (!ringtones && !notifications && !alarms && !podcasts && !audiobooks));
- result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
+ result = endFile(entry, ringtones, notifications, alarms, podcasts,
+ audiobooks, music);
}
}
} catch (RemoteException e) {
@@ -957,7 +960,7 @@
@UnsupportedAppUsage
private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
- boolean alarms, boolean music, boolean podcasts)
+ boolean alarms, boolean podcasts, boolean audiobooks, boolean music)
throws RemoteException {
// update database
@@ -1003,6 +1006,7 @@
values.put(Audio.Media.IS_ALARM, alarms);
values.put(Audio.Media.IS_MUSIC, music);
values.put(Audio.Media.IS_PODCAST, podcasts);
+ values.put(Audio.Media.IS_AUDIOBOOK, audiobooks);
} else if (MediaFile.isExifMimeType(mMimeType) && !mNoMedia) {
ExifInterface exif = null;
try {
@@ -1011,12 +1015,6 @@
// exif is null
}
if (exif != null) {
- float[] latlng = new float[2];
- if (exif.getLatLong(latlng)) {
- values.put(Images.Media.LATITUDE, latlng[0]);
- values.put(Images.Media.LONGITUDE, latlng[1]);
- }
-
long time = exif.getGpsDateTime();
if (time != -1) {
values.put(Images.Media.DATE_TAKEN, time);
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index b6e3276..5e9eed7 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -20,7 +20,6 @@
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Bitmap;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -36,7 +35,6 @@
import android.util.Log;
import android.view.KeyEvent;
-import java.lang.ref.WeakReference;
import java.util.List;
/**
@@ -250,7 +248,7 @@
* @throws IllegalArgumentException
*/
public boolean sendMediaKeyEvent(KeyEvent keyEvent) throws IllegalArgumentException {
- if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+ if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
throw new IllegalArgumentException("not a media key event");
}
synchronized (mInfoLock) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 436897f..874f21e 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -1029,7 +1030,7 @@
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #getDefaultUri
*/
- public static AssetFileDescriptor openDefaultRingtoneUri(
+ public static @Nullable AssetFileDescriptor openDefaultRingtoneUri(
@NonNull Context context, @NonNull Uri uri) throws FileNotFoundException {
// Try cached ringtone first since the actual provider may not be
// encryption aware, or it may be stored on CE media storage
diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java
index e6f39e0..6a83dab 100644
--- a/media/java/android/media/UriDataSourceDesc.java
+++ b/media/java/android/media/UriDataSourceDesc.java
@@ -21,8 +21,6 @@
import android.content.Context;
import android.net.Uri;
-import com.android.internal.util.Preconditions;
-
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;
@@ -33,9 +31,11 @@
/**
* @hide
- * Structure for data source descriptor.
+ * Structure of data source descriptor for sources using URI.
*
- * Used by {@link MediaPlayer2#setDataSource(UriDataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
* to set data source for playback.
*
* <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}.
@@ -158,8 +158,8 @@
* @throws NullPointerException if context or uri is null.
*/
public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri) {
- Preconditions.checkNotNull(context, "context cannot be null");
- Preconditions.checkNotNull(uri, "uri cannot be null");
+ Media2Utils.checkArgument(context != null, "context cannot be null");
+ Media2Utils.checkArgument(uri != null, "uri cannot be null");
resetDataSource();
mUri = uri;
mContext = context;
@@ -195,8 +195,8 @@
*/
public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri,
@Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) {
- Preconditions.checkNotNull(context, "context cannot be null");
- Preconditions.checkNotNull(uri);
+ Media2Utils.checkArgument(context != null, "context cannot be null");
+ Media2Utils.checkArgument(uri != null, "uri cannot be null");
if (cookies != null) {
CookieHandler cookieHandler = CookieHandler.getDefault();
if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index a7bdf4f..89a509f 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -18,11 +18,12 @@
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
-import android.util.Log;
-import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
/**
* The Visualizer class enables application to retrieve part of the currently playing audio for
@@ -455,7 +456,7 @@
* <li> Rfk, Ifk are respectively the real and imaginary parts of the kth frequency
* component</li>
* <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is:
- * (k*Fs)/(n/2) </li>
+ * k * Fs / n </li>
* </ul>
* <table border="0" cellspacing="0" cellpadding="0">
* <tr><td>Index </p></td>
@@ -476,9 +477,23 @@
* <td>Rf2 </p></td>
* <td>If2 </p></td>
* <td>... </p></td>
- * <td>Rf(n-1)/2 </p></td>
- * <td>If(n-1)/2 </p></td></tr>
+ * <td>Rf(n/2-1) </p></td>
+ * <td>If(n/2-1) </p></td></tr>
* </table>
+ * <p>In order to obtain magnitude and phase values the following code can
+ * be used:
+ * <pre class="prettyprint">
+ * int n = fft.size();
+ * float[] magnitudes = new float[n / 2 + 1];
+ * float[] phases = new float[n / 2 + 1];
+ * magnitudes[0] = (float)Math.abs(fft[0]); // DC
+ * magnitudes[n / 2] = (float)Math.abs(fft[1]); // Nyquist
+ * phases[0] = phases[n / 2] = 0;
+ * for (int k = 1; k < n / 2; k++) {
+ * int i = k * 2;
+ * magnitudes[k] = (float)Math.hypot(fft[i], fft[i + 1]);
+ * phases[k] = (float)Math.atan2(fft[i + 1], fft[i]);
+ * }</pre>
* @param fft array of bytes where the FFT should be returned
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
@@ -561,25 +576,11 @@
* <p>Data in the fft buffer is valid only within the scope of the callback.
* Applications which need access to the fft data after returning from the callback
* should make a copy of the data instead of holding a reference.
+ * <p>For the explanation of the fft data array layout, and the example
+ * code for processing it, please see the documentation for {@link #getFft(byte[])} method.
*
- * <p>In order to obtain magnitude and phase values the following formulas can
- * be used:
- * <pre class="prettyprint">
- * for (int i = 0; i < fft.size(); i += 2) {
- * float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
- * float phase = (float)Math.atan2(fft[i + 1], fft[i]);
- * }</pre>
* @param visualizer Visualizer object on which the listener is registered.
* @param fft array of bytes containing the frequency representation.
- * The fft array only contains the first half of the actual
- * FFT spectrum (frequencies up to Nyquist frequency), exploiting
- * the symmetry of the spectrum. For each frequencies bin <code>i</code>:
- * <ul>
- * <li>the element at index <code>2*i</code> in the array contains
- * the real part of a complex number,</li>
- * <li>the element at index <code>2*i+1</code> contains the imaginary
- * part of the complex number.</li>
- * </ul>
* @param samplingRate sampling rate of the visualized audio.
*/
void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
index 511f6cd..5411e66 100644
--- a/media/java/android/media/midi/MidiOutputPort.java
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -59,6 +59,8 @@
// read next event
int count = mInputStream.read(buffer);
if (count < 0) {
+ // This is the exit condition as read() returning <0 indicates
+ // that the pipe has been closed.
break;
// FIXME - inform receivers here?
}
@@ -81,10 +83,15 @@
Log.e(TAG, "Unknown packet type " + packetType);
break;
}
- }
+ } // while (true)
} catch (IOException e) {
// FIXME report I/O failure?
- Log.e(TAG, "read failed", e);
+ // TODO: The comment above about the exit condition is not currently working
+ // as intended. The read from the closed pipe is throwing an error rather than
+ // returning <0, so this becomes (probably) not an error, but the exit case.
+ // This warrants further investigation;
+ // Silence the (probably) spurious error message.
+ // Log.e(TAG, "read failed", e);
} finally {
IoUtils.closeQuietly(mInputStream);
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index c4b82c3..b457357 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -149,7 +149,7 @@
if (keyEvent == null) {
throw new IllegalArgumentException("KeyEvent may not be null");
}
- if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+ if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
return false;
}
try {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8215779..d91cf87 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -96,9 +96,15 @@
* @return The binder object from the system
* @hide
*/
+ @SystemApi
public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
- @NonNull String tag, int userId) throws RemoteException {
- return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
+ @NonNull String tag, int userId) {
+ try {
+ return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return null;
}
/**
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index d9017b4..ff69779 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -59,9 +59,7 @@
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* The TvInputService class represents a TV input or source such as HDMI or built-in tuner which
@@ -1400,7 +1398,7 @@
// ViewRootImpl always consumes the keys. In this case, the application loses
// a chance to handle media keys. Therefore, media keys are not dispatched to
// ViewRootImpl.
- skipDispatchToOverlayView = KeyEvent.isMediaKey(keyEvent.getKeyCode())
+ skipDispatchToOverlayView = KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())
|| keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK;
} else if (event instanceof MotionEvent) {
MotionEvent motionEvent = (MotionEvent) event;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index faf4301..e25e6a5 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,7 +88,7 @@
name: "libmedia2_jni",
srcs: [
- "android_media_Media2DataSource.cpp",
+ "android_media_DataSourceCallback.cpp",
"android_media_MediaMetricsJNI.cpp",
"android_media_MediaPlayer2.cpp",
"android_media_SyncParams.cpp",
diff --git a/media/jni/android_media_Media2DataSource.cpp b/media/jni/android_media_DataSourceCallback.cpp
similarity index 79%
rename from media/jni/android_media_Media2DataSource.cpp
rename to media/jni/android_media_DataSourceCallback.cpp
index b3434e9..c91d409 100644
--- a/media/jni/android_media_Media2DataSource.cpp
+++ b/media/jni/android_media_DataSourceCallback.cpp
@@ -15,10 +15,10 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "JMedia2DataSource-JNI"
+#define LOG_TAG "JDataSourceCallback-JNI"
#include <utils/Log.h>
-#include "android_media_Media2DataSource.h"
+#include "android_media_DataSourceCallback.h"
#include "log/log.h"
#include "jni.h"
@@ -33,14 +33,14 @@
static const size_t kBufferSize = 64 * 1024;
-JMedia2DataSource::JMedia2DataSource(JNIEnv* env, jobject source)
+JDataSourceCallback::JDataSourceCallback(JNIEnv* env, jobject source)
: mJavaObjStatus(OK),
mSizeIsCached(false),
mCachedSize(0) {
- mMedia2DataSourceObj = env->NewGlobalRef(source);
- CHECK(mMedia2DataSourceObj != NULL);
+ mDataSourceCallbackObj = env->NewGlobalRef(source);
+ CHECK(mDataSourceCallbackObj != NULL);
- ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mMedia2DataSourceObj));
+ ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mDataSourceCallbackObj));
CHECK(media2DataSourceClass.get() != NULL);
mReadAtMethod = env->GetMethodID(media2DataSourceClass.get(), "readAt", "(J[BII)I");
@@ -55,17 +55,17 @@
CHECK(mByteArrayObj != NULL);
}
-JMedia2DataSource::~JMedia2DataSource() {
+JDataSourceCallback::~JDataSourceCallback() {
JNIEnv* env = JavaVMHelper::getJNIEnv();
- env->DeleteGlobalRef(mMedia2DataSourceObj);
+ env->DeleteGlobalRef(mDataSourceCallbackObj);
env->DeleteGlobalRef(mByteArrayObj);
}
-status_t JMedia2DataSource::initCheck() const {
+status_t JDataSourceCallback::initCheck() const {
return OK;
}
-ssize_t JMedia2DataSource::readAt(off64_t offset, void *data, size_t size) {
+ssize_t JDataSourceCallback::readAt(off64_t offset, void *data, size_t size) {
Mutex::Autolock lock(mLock);
if (mJavaObjStatus != OK) {
@@ -76,7 +76,7 @@
}
JNIEnv* env = JavaVMHelper::getJNIEnv();
- jint numread = env->CallIntMethod(mMedia2DataSourceObj, mReadAtMethod,
+ jint numread = env->CallIntMethod(mDataSourceCallbackObj, mReadAtMethod,
(jlong)offset, mByteArrayObj, (jint)0, (jint)size);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred in readAt()");
@@ -106,7 +106,7 @@
return numread;
}
-status_t JMedia2DataSource::getSize(off64_t* size) {
+status_t JDataSourceCallback::getSize(off64_t* size) {
Mutex::Autolock lock(mLock);
if (mJavaObjStatus != OK) {
@@ -118,7 +118,7 @@
}
JNIEnv* env = JavaVMHelper::getJNIEnv();
- *size = env->CallLongMethod(mMedia2DataSourceObj, mGetSizeMethod);
+ *size = env->CallLongMethod(mDataSourceCallbackObj, mGetSizeMethod);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred in getSize()");
jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
@@ -139,20 +139,20 @@
return OK;
}
-void JMedia2DataSource::close() {
+void JDataSourceCallback::close() {
Mutex::Autolock lock(mLock);
JNIEnv* env = JavaVMHelper::getJNIEnv();
- env->CallVoidMethod(mMedia2DataSourceObj, mCloseMethod);
+ env->CallVoidMethod(mDataSourceCallbackObj, mCloseMethod);
// The closed state is effectively the same as an error state.
mJavaObjStatus = UNKNOWN_ERROR;
}
-String8 JMedia2DataSource::toString() {
- return String8::format("JMedia2DataSource(pid %d, uid %d)", getpid(), getuid());
+String8 JDataSourceCallback::toString() {
+ return String8::format("JDataSourceCallback(pid %d, uid %d)", getpid(), getuid());
}
-String8 JMedia2DataSource::getMIMEType() const {
+String8 JDataSourceCallback::getMIMEType() const {
return String8("application/octet-stream");
}
diff --git a/media/jni/android_media_Media2DataSource.h b/media/jni/android_media_DataSourceCallback.h
similarity index 80%
rename from media/jni/android_media_Media2DataSource.h
rename to media/jni/android_media_DataSourceCallback.h
index dc085f3..5bde682 100644
--- a/media/jni/android_media_Media2DataSource.h
+++ b/media/jni/android_media_DataSourceCallback.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
-#define _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
+#ifndef _ANDROID_MEDIA_DATASOURCECALLBACK_H_
+#define _ANDROID_MEDIA_DATASOURCECALLBACK_H_
#include "jni.h"
@@ -26,16 +26,16 @@
namespace android {
-// The native counterpart to a Java android.media.Media2DataSource. It inherits from
+// The native counterpart to a Java android.media.DataSourceCallback. It inherits from
// DataSource.
//
// If the java DataSource returns an error or throws an exception it
// will be considered to be in a broken state, and the only further call this
// will make is to close().
-class JMedia2DataSource : public DataSource {
+class JDataSourceCallback : public DataSource {
public:
- JMedia2DataSource(JNIEnv *env, jobject source);
- virtual ~JMedia2DataSource();
+ JDataSourceCallback(JNIEnv *env, jobject source);
+ virtual ~JDataSourceCallback();
virtual status_t initCheck() const override;
virtual ssize_t readAt(off64_t offset, void *data, size_t size) override;
@@ -56,15 +56,15 @@
bool mSizeIsCached;
off64_t mCachedSize;
- jobject mMedia2DataSourceObj;
+ jobject mDataSourceCallbackObj;
jmethodID mReadAtMethod;
jmethodID mGetSizeMethod;
jmethodID mCloseMethod;
jbyteArray mByteArrayObj;
- DISALLOW_EVIL_CONSTRUCTORS(JMedia2DataSource);
+ DISALLOW_EVIL_CONSTRUCTORS(JDataSourceCallback);
};
} // namespace android
-#endif // _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
+#endif // _ANDROID_MEDIA_DATASOURCECALLBACK_H_
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index f7de2e7..4567492 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -46,7 +46,7 @@
#include "utils/KeyedVector.h"
#include "utils/String8.h"
#include "android_media_BufferingParams.h"
-#include "android_media_Media2DataSource.h"
+#include "android_media_DataSourceCallback.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_PlaybackParams.h"
#include "android_media_SyncParams.h"
@@ -423,7 +423,7 @@
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
- sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource);
+ sp<DataSource> callbackDataSource = new JDataSourceCallback(env, dataSource);
sp<DataSourceDesc> dsd = new DataSourceDesc();
dsd->mId = srcId;
dsd->mType = DataSourceDesc::TYPE_CALLBACK;
@@ -1390,7 +1390,7 @@
},
{
"nativeHandleDataSourceCallback",
- "(ZJLandroid/media/Media2DataSource;JJ)V",
+ "(ZJLandroid/media/DataSourceCallback;JJ)V",
(void *)android_media_MediaPlayer2_handleDataSourceCallback
},
{"nativePlayNextDataSource", "(J)V", (void *)android_media_MediaPlayer2_playNextDataSource},
diff --git a/media/proto/Android.bp b/media/proto/Android.bp
index 50d44c3..74fd525 100644
--- a/media/proto/Android.bp
+++ b/media/proto/Android.bp
@@ -7,6 +7,7 @@
srcs: ["mediaplayer2.proto"],
no_framework_libs: true,
jarjar_rules: "jarjar-rules.txt",
+ sdk_version: "28",
}
cc_library_static {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
new file mode 100644
index 0000000..f578e46
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.ImageReader;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Surface;
+
+import junit.framework.Assert;
+
+public class SurfaceUtilsTest extends junit.framework.TestCase {
+
+ @SmallTest
+ public void testInvalidSurfaceException() {
+ ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 1);
+ Surface surface = reader.getSurface();
+ surface.release();
+
+ try {
+ SurfaceUtils.isFlexibleConsumer(surface);
+ Assert.fail("unreachable");
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ reader.close();
+ }
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index e7e8384..207508e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -144,6 +144,7 @@
AHardwareBuffer_describe; # introduced=26
AHardwareBuffer_fromHardwareBuffer; # introduced=26
AHardwareBuffer_getNativeHandle; # introduced=26
+ AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock; # introduced=26
AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26
AHardwareBuffer_release; # introduced=26
diff --git a/native/webview/plat_support/draw_gl.h b/native/webview/plat_support/draw_gl.h
index c8434b6..de13ed0 100644
--- a/native/webview/plat_support/draw_gl.h
+++ b/native/webview/plat_support/draw_gl.h
@@ -43,9 +43,9 @@
// Input: tells the draw function what action to perform.
enum Mode {
kModeDraw = 0,
- kModeProcess,
- kModeProcessNoContext,
- kModeSync,
+ kModeProcess = 1,
+ kModeProcessNoContext = 2,
+ kModeSync = 3,
} mode;
// Input: current clip rect in surface coordinates. Reflects the current state
@@ -93,9 +93,9 @@
AwDrawGLInfo* draw_info,
void* spare);
enum AwMapMode {
- MAP_READ_ONLY,
- MAP_WRITE_ONLY,
- MAP_READ_WRITE,
+ MAP_READ_ONLY = 0,
+ MAP_WRITE_ONLY = 1,
+ MAP_READ_WRITE = 2,
};
// Called to create a GraphicBuffer
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_out.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_out.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_out.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_out.xml
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
index ed637a7..6f12338 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
index 227c981..9f8c12e 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
index 5901ff4..adc1f72 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
index 41cbe4b..dec5c05 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
index 341e7e0..986a9cb 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
index 6ae7413..80b38b3 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
index 06ac9e3..721376c 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
index 4baefb8..246099e 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
index 2d0deb9..9a1c642 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
similarity index 95%
rename from packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
index 3315220..1414b66 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/drawable/car_add_circle_round.xml b/packages/CarSystemUI/res/drawable/car_add_circle_round.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_add_circle_round.xml
rename to packages/CarSystemUI/res/drawable/car_add_circle_round.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_add_white.xml b/packages/CarSystemUI/res/drawable/car_ic_add_white.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_add_white.xml
rename to packages/CarSystemUI/res/drawable/car_ic_add_white.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml
similarity index 94%
rename from packages/SystemUI/res/drawable/car_ic_arrow.xml
rename to packages/CarSystemUI/res/drawable/car_ic_arrow.xml
index d400ed8..cfacbf9 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml
@@ -1,5 +1,5 @@
<!--
- ~ Copyright (C) 2017 The Android Open Source Project
+ ~ 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.
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
similarity index 93%
rename from packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
rename to packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
index 33a512e..81e7262 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
@@ -1,5 +1,5 @@
<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+ ~ 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.
diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_hvac.xml
rename to packages/CarSystemUI/res/drawable/car_ic_hvac.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml b/packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
rename to packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
diff --git a/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml
new file mode 100644
index 0000000..eb501e5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml
@@ -0,0 +1,27 @@
+<?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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners
+ android:bottomLeftRadius="@dimen/car_radius_3"
+ android:topLeftRadius="0dp"
+ android:bottomRightRadius="@dimen/car_radius_3"
+ android:topRightRadius="0dp"
+ />
+</shape>
diff --git a/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml
new file mode 100644
index 0000000..34578fe
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="18.0"
+ android:viewportHeight="18.0">
+ <group
+ android:translateY="0.5"
+ android:translateX="0.5" >
+ <path
+ android:pathData="M9.57,8.5l2.79,-2.78c0.3,-0.3 0.3,-0.8 0,-1.1L9.04,1.29L9.02,1.27C8.7,0.98 8.21,1 7.91,1.31C7.78,1.45 7.71,1.64 7.71,1.84v4.79L4.69,3.61c-0.3,-0.3 -0.79,-0.3 -1.09,0s-0.3,0.79 0,1.09L7.39,8.5L3.6,12.29c-0.3,0.3 -0.3,0.79 0,1.09s0.79,0.3 1.09,0l3.01,-3.01v4.8c0,0.42 0.35,0.77 0.77,0.77c0.19,0 0.39,-0.07 0.53,-0.21l0.04,-0.04l3.32,-3.32c0.3,-0.3 0.3,-0.8 0,-1.1L9.57,8.5zM9.19,6.77v-3.2l1.6,1.6L9.19,6.77zM9.19,13.42v-3.2l1.6,1.6L9.19,13.42zM4.03,9.29c-0.44,0.44 -1.15,0.44 -1.58,0C2.02,8.86 2.02,8.16 2.45,7.72l0.01,-0.01C2.89,7.27 3.59,7.27 4.02,7.7l0.01,0.01C4.47,8.15 4.47,8.85 4.03,9.29zM14.44,7.71c0.44,0.44 0.44,1.15 0,1.58c-0.44,0.44 -1.15,0.44 -1.58,0c-0.44,-0.43 -0.44,-1.13 -0.01,-1.57l0.01,-0.01C13.3,7.28 14,7.27 14.43,7.7C14.44,7.7 14.44,7.71 14.44,7.71z"
+ android:fillColor="#FFFFFF"/>
+ </group>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
index f5a91b5..e1e389d 100644
--- a/packages/CarSystemUI/res/drawable/ic_mic_white.xml
+++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
@@ -1,3 +1,19 @@
+<?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
+ -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/CarSystemUI/res/layout/car_facet_button.xml
similarity index 97%
rename from packages/SystemUI/res/layout/car_facet_button.xml
rename to packages/CarSystemUI/res/layout/car_facet_button.xml
index ad86049..8e7ebad 100644
--- a/packages/SystemUI/res/layout/car_facet_button.xml
+++ b/packages/CarSystemUI/res/layout/car_facet_button.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
-** Copyright 2017, The Android Open Source Project
+** Copyright 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.
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
rename to packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
index ee8d357..1d67286 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (C) 2015 The Android Open Source Project
+ 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.
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
rename to packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index c9f5148..6cd70d6 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2015 The Android Open Source Project
+ 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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
similarity index 98%
rename from packages/SystemUI/res/layout/car_left_navigation_bar.xml
rename to packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 02be457..141b28a 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
-** Copyright 2016, The Android Open Source Project
+** Copyright 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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
similarity index 100%
rename from packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
rename to packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
similarity index 95%
rename from packages/SystemUI/res/layout/car_navigation_button.xml
rename to packages/CarSystemUI/res/layout/car_navigation_button.xml
index 4062eb8..6d8cca9 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
-** Copyright 2016, The Android Open Source Project
+** Copyright 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.
diff --git a/packages/SystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml
similarity index 98%
rename from packages/SystemUI/res/layout/car_qs_footer.xml
rename to packages/CarSystemUI/res/layout/car_qs_footer.xml
index 3afd4ea..6f19cfc 100644
--- a/packages/SystemUI/res/layout/car_qs_footer.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_qs_panel.xml
rename to packages/CarSystemUI/res/layout/car_qs_panel.xml
index e7413de..dfa48c3 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
similarity index 98%
copy from packages/SystemUI/res/layout/car_left_navigation_bar.xml
copy to packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 02be457..141b28a 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
-** Copyright 2016, The Android Open Source Project
+** Copyright 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.
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
similarity index 100%
rename from packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
rename to packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
diff --git a/packages/SystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml
similarity index 95%
rename from packages/SystemUI/res/values/colors_car.xml
rename to packages/CarSystemUI/res/values/colors_car.xml
index 49bfb25..2f720f5 100644
--- a/packages/SystemUI/res/values/colors_car.xml
+++ b/packages/CarSystemUI/res/values/colors_car.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
- * Copyright 2017, The Android Open Source Project
+ * Copyright 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.
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/CarSystemUI/res/values/dimens_car.xml
similarity index 97%
rename from packages/SystemUI/res/values/dimens_car.xml
rename to packages/CarSystemUI/res/values/dimens_car.xml
index afbe176..c027f81 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/CarSystemUI/res/values/dimens_car.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- * Copyright (c) 2016, The Android Open Source Project
+ * 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.
diff --git a/packages/SystemUI/res/values/ids_car.xml b/packages/CarSystemUI/res/values/ids_car.xml
similarity index 100%
rename from packages/SystemUI/res/values/ids_car.xml
rename to packages/CarSystemUI/res/values/ids_car.xml
diff --git a/packages/SystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
similarity index 95%
rename from packages/SystemUI/res/values/integers_car.xml
rename to packages/CarSystemUI/res/values/integers_car.xml
index fc3623c..472c957 100644
--- a/packages/SystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2017, The Android Open Source Project
+ 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.
diff --git a/packages/SystemUI/res/values/strings_car.xml b/packages/CarSystemUI/res/values/strings_car.xml
similarity index 96%
rename from packages/SystemUI/res/values/strings_car.xml
rename to packages/CarSystemUI/res/values/strings_car.xml
index 2890cf2..83e91c5 100644
--- a/packages/SystemUI/res/values/strings_car.xml
+++ b/packages/CarSystemUI/res/values/strings_car.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/**
- * Copyright (c) 2016, The Android Open Source Project
+ * 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.
diff --git a/packages/CarSystemUI/res/xml/car_volume_items.xml b/packages/CarSystemUI/res/xml/car_volume_items.xml
index 8715946..922b1a7 100644
--- a/packages/CarSystemUI/res/xml/car_volume_items.xml
+++ b/packages/CarSystemUI/res/xml/car_volume_items.xml
@@ -23,8 +23,8 @@
car:icon="@drawable/car_ic_music"/>
<item car:usage="media"
car:icon="@drawable/car_ic_music"/>
- <item car:usage="voice_communication"
- car:icon="@*android:drawable/ic_audio_ring_notif"/>
+ <item car:usage="assistance_navigation_guidance"
+ car:icon="@drawable/car_ic_navigation"/>
<item car:usage="voice_communication_signalling"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="alarm"
@@ -43,8 +43,8 @@
car:icon="@drawable/car_ic_notification"/>
<item car:usage="assistance_accessibility"
car:icon="@drawable/car_ic_notification"/>
- <item car:usage="assistance_navigation_guidance"
- car:icon="@drawable/car_ic_navigation"/>
+ <item car:usage="voice_communication"
+ car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="assistance_sonification"
car:icon="@drawable/car_ic_notification"/>
<item car:usage="game"
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index dfe5704..f57f26d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -28,6 +28,8 @@
import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.volume.CarVolumeDialogComponent;
+import com.android.systemui.volume.VolumeDialogComponent;
/**
* Class factory to provide car specific SystemUI components.
@@ -39,6 +41,10 @@
return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
}
+ public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
+ return new CarVolumeDialogComponent(systemUi, context);
+ }
+
@Override
public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
rename to packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 50fefe9..0563418 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -11,8 +11,9 @@
* 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
+ * limitations under the License.
*/
+
package com.android.systemui.car;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 0389030..b74f199 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -1,16 +1,19 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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.
+ * 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.car;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
index 3e82c54..41c37d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
@@ -1,16 +1,19 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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.
+ * 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.car;
import android.animation.Animator;
@@ -223,7 +226,7 @@
private void animateHeightChange(boolean opening) {
// Animation in progress; cancel it to avoid contention.
- if (mAnimatorSet != null){
+ if (mAnimatorSet != null) {
mAnimatorSet.cancel();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index 083a747..d5dd3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -1,16 +1,19 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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.
+ * 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.car;
import android.content.Context;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
new file mode 100644
index 0000000..5bf30ca
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
+
+/**
+ * AssitantButton is a ui component that will trigger the Voice Interaction Service.
+ */
+public class AssitantButton extends CarFacetButton {
+
+ private static final String TAG = "CarFacetButton";
+ private IVoiceInteractionSessionShowCallback mShowCallback =
+ new IVoiceInteractionSessionShowCallback.Stub() {
+ @Override
+ public void onFailed() {
+ Log.w(TAG, "Failed to show VoiceInteractionSession");
+ }
+
+ @Override
+ public void onShown() {
+ Log.d(TAG, "IVoiceInteractionSessionShowCallback onShown()");
+ }
+ };
+
+ private static final String EXTRA_CAR_PUSH_TO_TALK =
+ "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK";
+ private final AssistUtils mAssistUtils;
+
+ public AssitantButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mAssistUtils = new AssistUtils(context);
+ setOnClickListener(v -> {
+ showAssistant();
+ });
+ }
+
+ private void showAssistant() {
+ final Bundle args = new Bundle();
+ args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true);
+ mAssistUtils.showSessionForActiveService(args,
+ SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null);
+ }
+
+ @Override
+ protected void setupIntents(TypedArray typedArray){
+ // left blank because for the assistant button Intent will not be passed from the layout.
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index fc39648..58f80a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -58,28 +58,31 @@
private final Context mContext;
private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
- private BluetoothHeadsetClient mBluetoothHeadsetClient;
-
private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private BluetoothHeadsetClient mBluetoothHeadsetClient;
+ private final ServiceListener mHfpServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+ }
+ }
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = null;
+ }
+ }
+ };
private int mLevel;
-
- /**
- * An interface indicating the container of a View that will display what the information
- * in the {@link CarBatteryController}.
- */
- public interface BatteryViewHandler {
- void hideBatteryView();
- void showBatteryView();
- }
-
private BatteryViewHandler mBatteryViewHandler;
public CarBatteryController(Context context) {
mContext = context;
if (mAdapter == null) {
- return;
+ return;
}
mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
@@ -159,7 +162,7 @@
}
BluetoothDevice device =
- (BluetoothDevice)intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
+ (BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
updateBatteryIcon(device, newState);
}
}
@@ -261,20 +264,14 @@
}
}
- private final ServiceListener mHfpServiceListener = new ServiceListener() {
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (profile == BluetoothProfile.HEADSET_CLIENT) {
- mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
- }
- }
+ /**
+ * An interface indicating the container of a View that will display what the information
+ * in the {@link CarBatteryController}.
+ */
+ public interface BatteryViewHandler {
+ void hideBatteryView();
- @Override
- public void onServiceDisconnected(int profile) {
- if (profile == BluetoothProfile.HEADSET_CLIENT) {
- mBluetoothHeadsetClient = null;
- }
- }
- };
+ void showBatteryView();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index bd32856..cea4ab0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
package com.android.systemui.statusbar.car;
import android.content.Context;
@@ -31,6 +47,7 @@
private static final String EXTRA_FACET_PACKAGES = "packages";
private static final String EXTRA_FACET_ID = "filter_id";
private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
+ private static final String TAG = "CarFacetButton";
private Context mContext;
private AlphaOptimizedImageButton mIcon;
@@ -51,12 +68,10 @@
private float mSelectedAlpha = 1f;
private float mUnselectedAlpha = 1f;
-
public CarFacetButton(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
View.inflate(context, R.layout.car_facet_button, this);
-
// extract custom attributes
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
setupIntents(typedArray);
@@ -64,13 +79,12 @@
CarFacetButtonController carFacetButtonController = Dependency.get(
CarFacetButtonController.class);
carFacetButtonController.addFacetButton(this);
-
}
/**
* Reads the custom attributes to setup click handlers for this component.
*/
- private void setupIntents(TypedArray typedArray) {
+ protected void setupIntents(TypedArray typedArray) {
String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
@@ -111,7 +125,6 @@
}
}
-
private void setupIcons(TypedArray styledAttributes) {
mSelectedAlpha = styledAttributes.getFloat(
R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
@@ -153,6 +166,9 @@
return mFacetPackages;
}
+ /**
+ * @return The list of component names.
+ */
public String[] getComponentName() {
if (mComponentNames == null) {
return new String[0];
@@ -162,6 +178,7 @@
/**
* Updates the alpha of the icons to "selected" and shows the "More icon"
+ *
* @param selected true if the view must be selected, false otherwise
*/
public void setSelected(boolean selected) {
@@ -171,7 +188,8 @@
/**
* Updates the visual state to let the user know if it's been selected.
- * @param selected true if should update the alpha of the icon to selected, false otherwise
+ *
+ * @param selected true if should update the alpha of the icon to selected, false otherwise
* @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
* is ignored if the attribute useMoreIcon is set to false
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 20986ad..56db242 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
package com.android.systemui.statusbar.car;
import android.app.ActivityManager;
@@ -34,7 +50,6 @@
* Add facet button to this controller. The expected use is for the facet button
* to get a reference to this controller via {@link com.android.systemui.Dependency}
* and self add.
- * @param facetButton
*/
public void addFacetButton(CarFacetButton facetButton) {
String[] categories = facetButton.getCategories();
@@ -70,15 +85,16 @@
* They will then be compared with the supplied StackInfo list.
* The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
* for consideration if it has the same displayId as the CarFacetButtons.
- * @param taskInfo of the currently running application
+ *
+ * @param stackInfoList of the currently running application
*/
public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
int displayId = getDisplayId();
ActivityManager.StackInfo validStackInfo = null;
- for (ActivityManager.StackInfo stackInfo :stackInfoList) {
+ for (ActivityManager.StackInfo stackInfo : stackInfoList) {
// If the display id is unknown or it matches the stack, it's valid for use
- if ((displayId == -1 || displayId == stackInfo.displayId) &&
- stackInfo.topActivity != null) {
+ if ((displayId == -1 || displayId == stackInfo.displayId)
+ && stackInfo.topActivity != null) {
validStackInfo = stackInfo;
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 084c136..e640baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
package com.android.systemui.statusbar.car;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 4bff5ba..2d90f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -35,6 +35,8 @@
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.StatusBarState;
@@ -252,6 +254,11 @@
addTemperatureViewToController(mStatusBarWindow);
}
+ @Override
+ protected QS createDefaultQSFragment() {
+ return new CarQSFragment();
+ }
+
private BatteryController createBatteryController() {
mCarBatteryController = new CarBatteryController(mContext);
mCarBatteryController.addBatteryViewHandler(this);
@@ -549,7 +556,7 @@
*/
public void dismissKeyguard() {
executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
- true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+ true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
index d0f0629..8c6b9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
package com.android.systemui.statusbar.car;
import android.content.Context;
@@ -16,13 +32,13 @@
ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils) {
super(context, callback, lockPatternUtils);
- mShouldHideNavBar =context.getResources()
+ mShouldHideNavBar = context.getResources()
.getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
}
@Override
protected void updateNavigationBarVisibility(boolean navBarVisible) {
- if(!mShouldHideNavBar) {
+ if (!mShouldHideNavBar) {
return;
}
CarStatusBar statusBar = (CarStatusBar) mStatusBar;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
index f2923f7..3288927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
package com.android.systemui.statusbar.car;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
@@ -29,7 +45,7 @@
*/
public class ConnectedDeviceSignalController extends BroadcastReceiver implements
BluetoothController.Callback {
- private final static String TAG = "DeviceSignalCtlr";
+ private static final String TAG = "DeviceSignalCtlr";
/**
* The value that indicates if a network is unavailable. This value is according ot the
@@ -70,6 +86,21 @@
private final SignalDrawable mSignalDrawable;
private BluetoothHeadsetClient mBluetoothHeadsetClient;
+ private final ServiceListener mHfpServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HEADSET_CLIENT) {
+ mBluetoothHeadsetClient = null;
+ }
+ }
+ };
public ConnectedDeviceSignalController(Context context, View signalsView) {
mContext = context;
@@ -87,7 +118,7 @@
new ScalingDrawableWrapper(mSignalDrawable, mIconScaleFactor));
if (mAdapter == null) {
- return;
+ return;
}
mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
@@ -236,20 +267,4 @@
mSignalsView.setVisibility(View.GONE);
}
}
-
- private final ServiceListener mHfpServiceListener = new ServiceListener() {
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (profile == BluetoothProfile.HEADSET_CLIENT) {
- mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
- }
- }
-
- @Override
- public void onServiceDisconnected(int profile) {
- if (profile == BluetoothProfile.HEADSET_CLIENT) {
- mBluetoothHeadsetClient = null;
- }
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index 47941bf..730c3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -124,4 +124,4 @@
Log.d(TAG, message);
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 2ebf5eb..23fe594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.systemui.statusbar.car;
@@ -44,7 +44,7 @@
// Initialize user grid.
mUserGridView = container.findViewById(R.id.user_grid);
GridLayoutManager layoutManager = new GridLayoutManager(context,
- context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
+ context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
mUserGridView.getRecyclerView().setLayoutManager(layoutManager);
mUserGridView.buildAdapter();
mUserGridView.setUserSelectionListener(this::onUserSelected);
@@ -54,7 +54,7 @@
hide();
mShortAnimDuration = container.getResources()
- .getInteger(android.R.integer.config_shortAnimTime);
+ .getInteger(android.R.integer.config_shortAnimTime);
}
/**
@@ -108,4 +108,4 @@
});
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index d802ed8..fb2b57b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.systemui.statusbar.car;
@@ -210,7 +210,7 @@
public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
UserRecord userRecord = mUsers.get(position);
RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
- getUserRecordIcon(userRecord));
+ getUserRecordIcon(userRecord));
circleIcon.setCircular(true);
holder.mUserAvatarImageView.setImageDrawable(circleIcon);
holder.mUserNameTextView.setText(userRecord.mInfo.name);
@@ -254,13 +254,13 @@
private void showMaxUserLimitReachedDialog() {
AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
- .setTitle(R.string.user_limit_reached_title)
- .setMessage(getResources().getQuantityString(
- R.plurals.user_limit_reached_message,
- mCarUserManagerHelper.getMaxSupportedRealUsers(),
- mCarUserManagerHelper.getMaxSupportedRealUsers()))
- .setPositiveButton(android.R.string.ok, null)
- .create();
+ .setTitle(R.string.user_limit_reached_title)
+ .setMessage(getResources().getQuantityString(
+ R.plurals.user_limit_reached_message,
+ mCarUserManagerHelper.getMaxSupportedRealUsers(),
+ mCarUserManagerHelper.getMaxSupportedRealUsers()))
+ .setPositiveButton(android.R.string.ok, null)
+ .create();
// Sets window flags for the SysUI dialog
SystemUIDialog.applyFlags(maxUsersDialog);
maxUsersDialog.show();
@@ -268,17 +268,17 @@
private void showConfirmAddUserDialog() {
String message = mRes.getString(R.string.user_add_user_message_setup)
- .concat(System.getProperty("line.separator"))
- .concat(System.getProperty("line.separator"))
- .concat(mRes.getString(R.string.user_add_user_message_update));
+ .concat(System.getProperty("line.separator"))
+ .concat(System.getProperty("line.separator"))
+ .concat(mRes.getString(R.string.user_add_user_message_update));
AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
- .setTitle(R.string.user_add_user_title)
- .setMessage(message)
- .setNegativeButton(android.R.string.cancel, this)
- .setPositiveButton(android.R.string.ok, this)
- .setOnCancelListener(this)
- .create();
+ .setTitle(R.string.user_add_user_title)
+ .setMessage(message)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
// Sets window flags for the SysUI dialog
SystemUIDialog.applyFlags(addUserDialog);
addUserDialog.show();
@@ -298,7 +298,7 @@
if (userRecord.mIsAddUser) {
return UserIcons.convertToBitmap(mContext
- .getDrawable(R.drawable.car_add_circle_round));
+ .getDrawable(R.drawable.car_add_circle_round));
}
return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 6c924e3..aec31ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -41,117 +41,13 @@
public class HvacController {
public static final String TAG = "HvacController";
- public final static int BIND_TO_HVAC_RETRY_DELAY = 5000;
+ public static final int BIND_TO_HVAC_RETRY_DELAY = 5000;
private Context mContext;
private Handler mHandler;
private Car mCar;
private CarHvacManager mHvacManager;
private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
-
- public HvacController(Context context) {
- mContext = context;
- }
-
- /**
- * Create connection to the Car service. Note: call backs from the Car service
- * ({@link CarHvacManager}) will happen on the same thread this method was called from.
- */
- public void connectToCarService() {
- mHandler = new Handler();
- mCar = Car.createCar(mContext, mServiceConnection, mHandler);
- if (mCar != null) {
- // note: this connect call handles the retries
- mCar.connect();
- }
- }
-
- /**
- * Registers callbacks and initializes components upon connection.
- */
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- service.linkToDeath(mRestart, 0);
- mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
- mHvacManager.registerCallback(mHardwareCallback);
- initComponents();
- } catch (Exception e) {
- Log.e(TAG, "Failed to correctly connect to HVAC", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyHvacManager();
- }
- };
-
- private void destroyHvacManager() {
- if (mHvacManager != null) {
- mHvacManager.unregisterCallback(mHardwareCallback);
- mHvacManager = null;
- }
- }
-
- /**
- * If the connection to car service goes away then restart it.
- */
- private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- Log.d(TAG, "Death of HVAC triggering a restart");
- if (mCar != null) {
- mCar.disconnect();
- }
- destroyHvacManager();
- mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
- }
- };
-
- /**
- * Add component to list and initialize it if the connection is up.
- * @param temperatureView
- */
- public void addHvacTextView(TemperatureView temperatureView) {
-
- HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
- if (!mTempComponents.containsKey(hvacKey)) {
- mTempComponents.put(hvacKey, new ArrayList<>());
- }
- mTempComponents.get(hvacKey).add(temperatureView);
- initComponent(temperatureView);
- }
-
- private void initComponents() {
- Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
- mTempComponents.entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
- List<TemperatureView> temperatureViews = next.getValue();
- for (TemperatureView view : temperatureViews) {
- initComponent(view);
- }
- }
- }
-
-
- private void initComponent(TemperatureView view) {
- int id = view.getPropertyId();
- int zone = view.getAreaId();
- try {
- if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
- view.setTemp(Float.NaN);
- return;
- }
- view.setTemp(mHvacManager.getFloatProperty(id, zone));
- } catch (Exception e) {
- view.setTemp(Float.NaN);
- Log.e(TAG, "Failed to get value from hvac service", e);
- }
- }
-
/**
* Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
* match.
@@ -179,10 +75,109 @@
@Override
public void onErrorEvent(final int propertyId, final int zone) {
- Log.d(TAG, "HVAC error event, propertyId: " + propertyId +
- " zone: " + zone);
+ Log.d(TAG, "HVAC error event, propertyId: " + propertyId
+ + " zone: " + zone);
}
};
+ /**
+ * If the connection to car service goes away then restart it.
+ */
+ private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Log.d(TAG, "Death of HVAC triggering a restart");
+ if (mCar != null) {
+ mCar.disconnect();
+ }
+ destroyHvacManager();
+ mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
+ }
+ };
+ /**
+ * Registers callbacks and initializes components upon connection.
+ */
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ service.linkToDeath(mRestart, 0);
+ mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+ mHvacManager.registerCallback(mHardwareCallback);
+ initComponents();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to correctly connect to HVAC", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ destroyHvacManager();
+ }
+ };
+
+ public HvacController(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Create connection to the Car service. Note: call backs from the Car service
+ * ({@link CarHvacManager}) will happen on the same thread this method was called from.
+ */
+ public void connectToCarService() {
+ mHandler = new Handler();
+ mCar = Car.createCar(mContext, mServiceConnection, mHandler);
+ if (mCar != null) {
+ // note: this connect call handles the retries
+ mCar.connect();
+ }
+ }
+
+ private void destroyHvacManager() {
+ if (mHvacManager != null) {
+ mHvacManager.unregisterCallback(mHardwareCallback);
+ mHvacManager = null;
+ }
+ }
+
+ /**
+ * Add component to list and initialize it if the connection is up.
+ */
+ public void addHvacTextView(TemperatureView temperatureView) {
+
+ HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
+ if (!mTempComponents.containsKey(hvacKey)) {
+ mTempComponents.put(hvacKey, new ArrayList<>());
+ }
+ mTempComponents.get(hvacKey).add(temperatureView);
+ initComponent(temperatureView);
+ }
+
+ private void initComponents() {
+ Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
+ mTempComponents.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
+ List<TemperatureView> temperatureViews = next.getValue();
+ for (TemperatureView view : temperatureViews) {
+ initComponent(view);
+ }
+ }
+ }
+
+ private void initComponent(TemperatureView view) {
+ int id = view.getPropertyId();
+ int zone = view.getAreaId();
+ try {
+ if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
+ view.setTemp(Float.NaN);
+ return;
+ }
+ view.setTemp(mHvacManager.getFloatProperty(id, zone));
+ } catch (Exception e) {
+ view.setTemp(Float.NaN);
+ Log.e(TAG, "Failed to get value from hvac service", e);
+ }
+ }
/**
* Removes all registered components. This is useful if you need to rebuild the UI since
@@ -200,7 +195,7 @@
int mPropertyId;
int mAreaId;
- public HvacKey(int propertyId, int areaId) {
+ private HvacKey(int propertyId, int areaId) {
mPropertyId = propertyId;
mAreaId = areaId;
}
@@ -210,8 +205,8 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HvacKey hvacKey = (HvacKey) o;
- return mPropertyId == hvacKey.mPropertyId &&
- mAreaId == hvacKey.mAreaId;
+ return mPropertyId == hvacKey.mPropertyId
+ && mAreaId == hvacKey.mAreaId;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
index 4d8ce43..507c60f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
@@ -40,7 +40,7 @@
public TemperatureTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
- mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1);
+ mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1);
mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1);
String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat);
mTempFormat = (format == null) ? "%.1f\u00B0" : format;
@@ -48,6 +48,7 @@
/**
* Formats the float for display
+ *
* @param temp - The current temp or NaN
*/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
new file mode 100644
index 0000000..71cc19b
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.volume;
+
+import android.content.Context;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.plugins.VolumeDialog;
+
+/**
+ * Allows for adding car specific dialog when the volume dialog is created.
+ */
+public class CarVolumeDialogComponent extends VolumeDialogComponent {
+
+ public CarVolumeDialogComponent(SystemUI sysui, Context context) {
+ super(sysui, context);
+ }
+
+ protected VolumeDialog createDefault() {
+ return new CarVolumeDialogImpl(mContext);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
new file mode 100644
index 0000000..12df263
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -0,0 +1,602 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.volume;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.media.CarAudioManager;
+import android.car.media.ICarVolumeCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.ServiceConnection;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemAdapter.BackgroundStyle;
+import androidx.car.widget.ListItemProvider.ListProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.SeekbarListItem;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.VolumeDialog;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Car version of the volume dialog.
+ *
+ * Methods ending in "H" must be called on the (ui) handler.
+ */
+public class CarVolumeDialogImpl implements VolumeDialog {
+
+ private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
+
+ private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
+ private static final String XML_TAG_VOLUME_ITEM = "item";
+ private static final int HOVERING_TIMEOUT = 16000;
+ private static final int NORMAL_TIMEOUT = 3000;
+ private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
+ private static final int DISMISS_DELAY_IN_MILLIS = 50;
+ private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
+
+ private final Context mContext;
+ private final H mHandler = new H();
+ // All the volume items.
+ private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
+ // Available volume items in car audio manager.
+ private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
+ // Volume items in the PagedListView.
+ private final List<ListItem> mVolumeLineItems = new ArrayList<>();
+ private final KeyguardManager mKeyguard;
+ private Window mWindow;
+ private CustomDialog mDialog;
+ private PagedListView mListView;
+ private ListItemAdapter mPagedListAdapter;
+ private Car mCar;
+ private CarAudioManager mCarAudioManager;
+ private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
+ @Override
+ public void onGroupVolumeChanged(int groupId, int flags) {
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ int value = getSeekbarValue(mCarAudioManager, groupId);
+ // Do not update the progress if it is the same as before. When car audio manager sets
+ // its group volume caused by the seekbar progress changed, it also triggers this
+ // callback. Updating the seekbar at the same time could block the continuous seeking.
+ if (value != volumeItem.progress) {
+ volumeItem.listItem.setProgress(value);
+ volumeItem.progress = value;
+ }
+ if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+ mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onMasterMuteChanged(int flags) {
+ // ignored
+ }
+ };
+ private boolean mHovering;
+ private boolean mShowing;
+ private boolean mExpanded;
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ mExpanded = false;
+ mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+ int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+ // Populates volume slider items from volume groups to UI.
+ for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+ VolumeItem volumeItem = getVolumeItemForUsages(
+ mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+ mAvailableVolumeItems.add(volumeItem);
+ // The first one is the default item.
+ if (groupId == 0) {
+ volumeItem.defaultItem = true;
+ addSeekbarListItem(volumeItem, groupId,
+ R.drawable.car_ic_keyboard_arrow_down,
+ new ExpandIconListener());
+ }
+ }
+
+ // If list is already initiated, update its content.
+ if (mPagedListAdapter != null) {
+ mPagedListAdapter.notifyDataSetChanged();
+ }
+ mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ /**
+ * This does not get called when service is properly disconnected.
+ * So we need to also handle cleanups in destroy().
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ cleanupAudioManager();
+ }
+ };
+
+ public CarVolumeDialogImpl(Context context) {
+ mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
+ mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mCar = Car.createCar(mContext, mServiceConnection);
+ }
+
+ private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+ try {
+ return carAudioManager.getGroupVolume(volumeGroupId);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ return 0;
+ }
+
+ private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+ try {
+ return carAudioManager.getGroupMaxVolume(volumeGroupId);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ return 0;
+ }
+
+ /**
+ * Build the volume window and connect to the CarService which registers with car audio
+ * manager.
+ */
+ @Override
+ public void init(int windowType, Callback callback) {
+ initDialog();
+
+ mCar.connect();
+ }
+
+ @Override
+ public void destroy() {
+ mHandler.removeCallbacksAndMessages(null);
+
+ cleanupAudioManager();
+ // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
+ // audio manager beforehand.
+ mCar.disconnect();
+ }
+
+ private void initDialog() {
+ loadAudioUsageItems();
+ mVolumeLineItems.clear();
+ mDialog = new CustomDialog(mContext);
+
+ mHovering = false;
+ mShowing = false;
+ mExpanded = false;
+ mWindow = mDialog.getWindow();
+ mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
+ final WindowManager.LayoutParams lp = mWindow.getAttributes();
+ lp.format = PixelFormat.TRANSLUCENT;
+ lp.setTitle(VolumeDialogImpl.class.getSimpleName());
+ lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ lp.windowAnimations = -1;
+ mWindow.setAttributes(lp);
+ mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ mDialog.setCanceledOnTouchOutside(true);
+ mDialog.setContentView(R.layout.car_volume_dialog);
+ mDialog.setOnShowListener(dialog -> {
+ mListView.setTranslationY(-mListView.getHeight());
+ mListView.setAlpha(0);
+ mListView.animate()
+ .alpha(1)
+ .translationY(0)
+ .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+ .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
+ .start();
+ });
+ mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
+ mListView.setOnHoverListener((v, event) -> {
+ int action = event.getActionMasked();
+ mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
+ || (action == MotionEvent.ACTION_HOVER_MOVE);
+ rescheduleTimeoutH();
+ return true;
+ });
+
+ mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
+ BackgroundStyle.PANEL);
+ mListView.setAdapter(mPagedListAdapter);
+ mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+ }
+
+
+ private void showH(int reason) {
+ if (D.BUG) {
+ Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
+ }
+
+ mHandler.removeMessages(H.SHOW);
+ mHandler.removeMessages(H.DISMISS);
+ rescheduleTimeoutH();
+ // Refresh the data set before showing.
+ mPagedListAdapter.notifyDataSetChanged();
+ if (mShowing) {
+ return;
+ }
+ mShowing = true;
+
+ mDialog.show();
+ Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+ }
+
+ private void rescheduleTimeoutH() {
+ mHandler.removeMessages(H.DISMISS);
+ final int timeout = computeTimeoutH();
+ mHandler.sendMessageDelayed(mHandler
+ .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
+
+ if (D.BUG) {
+ Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
+ }
+ }
+
+ private int computeTimeoutH() {
+ return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
+ }
+
+ private void dismissH(int reason) {
+ if (D.BUG) {
+ Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
+ }
+
+ mHandler.removeMessages(H.DISMISS);
+ mHandler.removeMessages(H.SHOW);
+ if (!mShowing) {
+ return;
+ }
+
+ mListView.animate().cancel();
+
+ mListView.setTranslationY(0);
+ mListView.setAlpha(1);
+ mListView.animate()
+ .alpha(0)
+ .translationY(-mListView.getHeight())
+ .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+ .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+ .withEndAction(() -> mHandler.postDelayed(() -> {
+ if (D.BUG) {
+ Log.d(TAG, "mDialog.dismiss()");
+ }
+ mDialog.dismiss();
+ mShowing = false;
+ }, DISMISS_DELAY_IN_MILLIS))
+ .start();
+
+ Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+ }
+
+ private void loadAudioUsageItems() {
+ try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+ int type;
+ // Traverse to the first start tag
+ while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
+ && type != XmlResourceParser.START_TAG) {
+ // Do Nothing (moving parser to start element)
+ }
+
+ if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
+ throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
+ }
+ int outerDepth = parser.getDepth();
+ int rank = 0;
+ while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
+ && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlResourceParser.END_TAG) {
+ continue;
+ }
+ if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
+ TypedArray item = mContext.getResources().obtainAttributes(
+ attrs, R.styleable.carVolumeItems_item);
+ int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
+ if (usage >= 0) {
+ VolumeItem volumeItem = new VolumeItem();
+ volumeItem.rank = rank;
+ volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon,
+ 0);
+ mVolumeItems.put(usage, volumeItem);
+ rank++;
+ }
+ item.recycle();
+ }
+ }
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Error parsing volume groups configuration", e);
+ }
+ }
+
+ private VolumeItem getVolumeItemForUsages(int[] usages) {
+ int rank = Integer.MAX_VALUE;
+ VolumeItem result = null;
+ for (int usage : usages) {
+ VolumeItem volumeItem = mVolumeItems.get(usage);
+ if (volumeItem.rank < rank) {
+ rank = volumeItem.rank;
+ result = volumeItem;
+ }
+ }
+ return result;
+ }
+
+ private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem,
+ int volumeGroupId,
+ int supplementalIconId,
+ @Nullable View.OnClickListener supplementalIconOnClickListener) {
+ SeekbarListItem listItem = new SeekbarListItem(mContext);
+ listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
+ int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
+ int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
+ listItem.setProgress(progress);
+ listItem.setOnSeekBarChangeListener(new CarVolumeDialogImpl
+ .VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
+ Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
+ primaryIcon.mutate().setTint(color);
+ listItem.setPrimaryActionIcon(primaryIcon);
+ if (supplementalIconId != 0) {
+ Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
+ supplementalIcon.mutate().setTint(color);
+ listItem.setSupplementalIcon(supplementalIcon, true);
+ listItem.setSupplementalIconListener(supplementalIconOnClickListener);
+ } else {
+ listItem.setSupplementalEmptyIcon(true);
+ listItem.setSupplementalIconListener(null);
+ }
+
+ mVolumeLineItems.add(listItem);
+ volumeItem.listItem = listItem;
+ volumeItem.progress = progress;
+ return listItem;
+ }
+
+ private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
+ for (int i = 0; i < mVolumeItems.size(); ++i) {
+ VolumeItem volumeItem = mVolumeItems.valueAt(i);
+ if (volumeItem.listItem == targetItem) {
+ return volumeItem;
+ }
+ }
+ return null;
+ }
+
+ private void cleanupAudioManager() {
+ try {
+ mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ mVolumeLineItems.clear();
+ mCarAudioManager = null;
+ }
+
+ /**
+ * Wrapper class which contains information of each volume group.
+ */
+ private static class VolumeItem {
+
+ private int rank;
+ private boolean defaultItem = false;
+ private @DrawableRes int icon;
+ private SeekbarListItem listItem;
+ private int progress;
+ }
+
+ private final class H extends Handler {
+
+ private static final int SHOW = 1;
+ private static final int DISMISS = 2;
+
+ private H() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW:
+ showH(msg.arg1);
+ break;
+ case DISMISS:
+ dismissH(msg.arg1);
+ break;
+ default:
+ }
+ }
+ }
+
+ private final class CustomDialog extends Dialog implements DialogInterface {
+
+ private CustomDialog(Context context) {
+ super(context, com.android.systemui.R.style.qs_theme);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ rescheduleTimeoutH();
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ protected void onStart() {
+ super.setCanceledOnTouchOutside(true);
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (isShowing()) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ mHandler.obtainMessage(
+ H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private final class ExpandIconListener implements View.OnClickListener {
+
+ @Override
+ public void onClick(final View v) {
+ mExpanded = !mExpanded;
+ Animator inAnimator;
+ if (mExpanded) {
+ for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
+ // Adding the items which are not coming from the default item.
+ VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+ if (volumeItem.defaultItem) {
+ // Set progress here due to the progress of seekbar may not be updated.
+ volumeItem.listItem.setProgress(volumeItem.progress);
+ } else {
+ addSeekbarListItem(volumeItem, groupId, 0, null);
+ }
+ }
+ inAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_in_rotate_up);
+ } else {
+ // Only keeping the default stream if it is not expended.
+ Iterator itr = mVolumeLineItems.iterator();
+ while (itr.hasNext()) {
+ SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
+ VolumeItem volumeItem = findVolumeItem(seekbarListItem);
+ if (!volumeItem.defaultItem) {
+ itr.remove();
+ } else {
+ // Set progress here due to the progress of seekbar may not be updated.
+ seekbarListItem.setProgress(volumeItem.progress);
+ }
+ }
+ inAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_in_rotate_down);
+ }
+
+ Animator outAnimator = AnimatorInflater.loadAnimator(
+ mContext, R.anim.car_arrow_fade_out);
+ inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
+ AnimatorSet animators = new AnimatorSet();
+ animators.playTogether(outAnimator, inAnimator);
+ animators.setTarget(v);
+ animators.start();
+ mPagedListAdapter.notifyDataSetChanged();
+ }
+ }
+
+ private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
+
+ private final int mVolumeGroupId;
+ private final CarAudioManager mCarAudioManager;
+
+ private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
+ mVolumeGroupId = volumeGroupId;
+ mCarAudioManager = carAudioManager;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (!fromUser) {
+ // For instance, if this event is originated from AudioService,
+ // we can ignore it as it has already been handled and doesn't need to be
+ // sent back down again.
+ return;
+ }
+ try {
+ if (mCarAudioManager == null) {
+ Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+ return;
+ }
+ mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+ mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ }
+}
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
index 7057ce6..aef6a3c 100644
--- a/packages/CarrierDefaultApp/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -9,4 +9,5 @@
jminjie@google.com
satk@google.com
shuoq@google.com
-refuhoo@google.com
\ No newline at end of file
+refuhoo@google.com
+nazaninb@google.com
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 22d0e3b..0cad5af 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -19,8 +19,7 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -241,7 +240,7 @@
signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
}
if (Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) == 1) {
+ Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
if (mNotificationCategorizer.shouldSilence(entry)) {
final int importance = entry.getImportance() < IMPORTANCE_LOW
? entry.getImportance() : IMPORTANCE_LOW;
@@ -378,6 +377,14 @@
}
@Override
+ public void onActionClicked(String key, Notification.Action action, int source) {
+ if (DEBUG) {
+ Log.d(TAG, "onActionClicked() called with: key = [" + key + "], action = [" + action.title
+ + "], source = [" + source + "]");
+ }
+ }
+
+ @Override
public void onListenerConnected() {
if (DEBUG) Log.i(TAG, "CONNECTED");
try {
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index b2fc417..892267b 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -48,7 +48,7 @@
| Notification.FLAG_NO_CLEAR;
private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
private static final int MAX_ACTIONS_PER_LINK = 1;
- private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
+ private static final int MAX_SMART_ACTIONS = 3;
private static final int MAX_SUGGESTED_REPLIES = 3;
private static final ConversationActions.TypeConfig TYPE_CONFIG =
@@ -81,12 +81,9 @@
if (tcm == null) {
return EMPTY_ACTION_LIST;
}
- Notification.Action[] actions = entry.getNotification().actions;
- int numOfExistingActions = actions == null ? 0: actions.length;
- int maxSmartActions = MAX_SMART_ACTIONS - numOfExistingActions;
return suggestActionsFromText(
tcm,
- getMostSalientActionText(entry.getNotification()), maxSmartActions);
+ getMostSalientActionText(entry.getNotification()), MAX_SMART_ACTIONS);
}
ArrayList<CharSequence> suggestReplies(
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
deleted file mode 100644
index bc06cab..0000000
--- a/packages/PackageInstaller/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-android_app {
- name: "PackageInstaller",
-
- srcs: ["src/**/*.java"],
-
- static_libs: [
- "androidx.leanback_leanback",
- "xz-java",
- ],
-
- certificate: "platform",
- privileged: true,
- platform_apis: true,
-}
\ No newline at end of file
diff --git a/packages/PackageInstaller/Android.mk b/packages/PackageInstaller/Android.mk
new file mode 100644
index 0000000..ab5483c
--- /dev/null
+++ b/packages/PackageInstaller/Android.mk
@@ -0,0 +1,32 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PackageInstaller
+
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := xz-java
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.leanback_leanback
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 4801f62..eb9ec82 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.packageinstaller">
+ <original-package android:name="com.android.packageinstaller" />
+
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 91e23dd..03eafc4 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -34,15 +34,23 @@
<uses-permission android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.READ_PRINT_SERVICES" />
<uses-permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+ android:dataSentOffDevice="no"
+ android:dataSharedWithThirdParty="no"
+ android:dataUsedForMonetization="no"
+ android:dataRetentionTime="unlimited"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
+ android:dataSentOffDevice="no"
+ android:dataSharedWithThirdParty="no"
+ android:dataUsedForMonetization="no"
+ android:dataRetentionTime="unlimited"/>
+
<application
android:allowClearUserData="true"
android:label="@string/app_label"
- android:allowBackup= "false"
android:supportsRtl="true">
<service
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 0ccf13e2..9e16f5e 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -107,7 +107,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dip"
android:elevation="@dimen/preview_controls_elevation"
- android:tint="?android:attr/textColorPrimaryInverse"
+ android:tint="@android:color/white"
android:background="@drawable/print_button">
</ImageButton>
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
new file mode 100644
index 0000000..e518e0b
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "ActionButtonsPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
new file mode 100644
index 0000000..4f47113
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
@@ -0,0 +1,52 @@
+<?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:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/button4"
+ style="@style/SettingsActionButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
new file mode 100644
index 0000000..efa508d
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<resources>
+ <style name="SettingsActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="android:drawablePadding">4dp</item>
+ <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>
+ <item name="android:layout_marginEnd">8dp</item>
+ <item name="android:paddingTop">20dp</item>
+ <item name="android:paddingBottom">20dp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
new file mode 100644
index 0000000..8b46cc6
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -0,0 +1,393 @@
+/*
+ * 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.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * This preference provides a four buttons layout with Settings style.
+ * It looks like below
+ *
+ * --------------------------------------------------
+ * button1 | button2 | button3 | button4 |
+ * --------------------------------------------------
+ *
+ * User can set title / icon / click listener for each button.
+ *
+ * By default, four buttons are visible.
+ * However, there are two cases which button should be invisible(View.GONE).
+ *
+ * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false)
+ * 2. User doesn't set any title or icon for button.
+ */
+public class ActionButtonsPreference extends Preference {
+
+ private static final String TAG = "ActionButtonPreference";
+ private final ButtonInfo mButton1Info = new ButtonInfo();
+ private final ButtonInfo mButton2Info = new ButtonInfo();
+ private final ButtonInfo mButton3Info = new ButtonInfo();
+ private final ButtonInfo mButton4Info = new ButtonInfo();
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public ActionButtonsPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ setLayoutResource(R.layout.settings_action_buttons);
+ setSelectable(false);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ holder.setDividerAllowedAbove(true);
+ holder.setDividerAllowedBelow(true);
+
+ mButton1Info.mButton = (Button) holder.findViewById(R.id.button1);
+ mButton2Info.mButton = (Button) holder.findViewById(R.id.button2);
+ mButton3Info.mButton = (Button) holder.findViewById(R.id.button3);
+ mButton4Info.mButton = (Button) holder.findViewById(R.id.button4);
+
+ mButton1Info.setUpButton();
+ mButton2Info.setUpButton();
+ mButton3Info.setUpButton();
+ mButton4Info.setUpButton();
+ }
+
+ /**
+ * Set the visibility state of button1.
+ */
+ public ActionButtonsPreference setButton1Visible(boolean isVisible) {
+ if (isVisible != mButton1Info.mIsVisible) {
+ mButton1Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button1.
+ */
+ public ActionButtonsPreference setButton1Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton1Info.mText)) {
+ mButton1Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button1.
+ */
+ public ActionButtonsPreference setButton1Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton1Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button1.
+ */
+ public ActionButtonsPreference setButton1Enabled(boolean isEnabled) {
+ if (isEnabled != mButton1Info.mIsEnabled) {
+ mButton1Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button1 is clicked.
+ */
+ public ActionButtonsPreference setButton1OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton1Info.mListener) {
+ mButton1Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button2.
+ */
+ public ActionButtonsPreference setButton2Visible(boolean isVisible) {
+ if (isVisible != mButton2Info.mIsVisible) {
+ mButton2Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button2.
+ */
+ public ActionButtonsPreference setButton2Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton2Info.mText)) {
+ mButton2Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button2.
+ */
+ public ActionButtonsPreference setButton2Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton2Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button2.
+ */
+ public ActionButtonsPreference setButton2Enabled(boolean isEnabled) {
+ if (isEnabled != mButton2Info.mIsEnabled) {
+ mButton2Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button2 is clicked.
+ */
+ public ActionButtonsPreference setButton2OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton2Info.mListener) {
+ mButton2Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button3.
+ */
+ public ActionButtonsPreference setButton3Visible(boolean isVisible) {
+ if (isVisible != mButton3Info.mIsVisible) {
+ mButton3Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button3.
+ */
+ public ActionButtonsPreference setButton3Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton3Info.mText)) {
+ mButton3Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button3.
+ */
+ public ActionButtonsPreference setButton3Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton3Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button3.
+ */
+ public ActionButtonsPreference setButton3Enabled(boolean isEnabled) {
+ if (isEnabled != mButton3Info.mIsEnabled) {
+ mButton3Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button3 is clicked.
+ */
+ public ActionButtonsPreference setButton3OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton3Info.mListener) {
+ mButton3Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Set the visibility state of button4.
+ */
+ public ActionButtonsPreference setButton4Visible(boolean isVisible) {
+ if (isVisible != mButton4Info.mIsVisible) {
+ mButton4Info.mIsVisible = isVisible;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the text to be displayed in button4.
+ */
+ public ActionButtonsPreference setButton4Text(@StringRes int textResId) {
+ final String newText = getContext().getString(textResId);
+ if (!TextUtils.equals(newText, mButton4Info.mText)) {
+ mButton4Info.mText = newText;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the drawable to be displayed above of text in button4.
+ */
+ public ActionButtonsPreference setButton4Icon(@DrawableRes int iconResId) {
+ if (iconResId == 0) {
+ return this;
+ }
+
+ final Drawable icon;
+ try {
+ icon = getContext().getDrawable(iconResId);
+ mButton4Info.mIcon = icon;
+ notifyChanged();
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ return this;
+ }
+
+ /**
+ * Set the enabled state of button4.
+ */
+ public ActionButtonsPreference setButton4Enabled(boolean isEnabled) {
+ if (isEnabled != mButton4Info.mIsEnabled) {
+ mButton4Info.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked when button4 is clicked.
+ */
+ public ActionButtonsPreference setButton4OnClickListener(
+ View.OnClickListener listener) {
+ if (listener != mButton4Info.mListener) {
+ mButton4Info.mListener = listener;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ static class ButtonInfo {
+ private Button mButton;
+ private CharSequence mText;
+ private Drawable mIcon;
+ private View.OnClickListener mListener;
+ private boolean mIsEnabled = true;
+ private boolean mIsVisible = true;
+
+ void setUpButton() {
+ mButton.setText(mText);
+ mButton.setOnClickListener(mListener);
+ mButton.setEnabled(mIsEnabled);
+ mButton.setCompoundDrawablesWithIntrinsicBounds(
+ null /* left */, mIcon /* top */, null /* right */, null /* bottom */);
+
+ if (shouldBeVisible()) {
+ mButton.setVisibility(View.VISIBLE);
+ } else {
+ mButton.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * By default, four buttons are visible.
+ * However, there are two cases which button should be invisible.
+ *
+ * 1. User set invisible for this button. ex: mIsVisible = false.
+ * 2. User didn't set any title or icon.
+ */
+ private boolean shouldBeVisible() {
+ return mIsVisible && (!TextUtils.isEmpty(mText) || mIcon != null);
+ }
+ }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 444e724..cc17b25 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -17,6 +17,7 @@
"SettingsLibSearchWidget",
"SettingsLibSettingsSpinner",
"SettingsLayoutPreference",
+ "ActionButtonsPreference",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/SearchWidget/res/values-as/strings.xml b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
new file mode 100644
index 0000000..813e764
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="search_menu" msgid="1604061903696928905">"সন্ধান সম্পৰ্কীয় ছেটিংসমূহ"</string>
+</resources>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
index 0678263..01d9c00 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -35,7 +35,7 @@
android:id="@+id/entity_header_icon"
android:layout_width="48dp"
android:layout_height="48dp"
- android:scaleType="fitXY"
+ android:scaleType="fitCenter"
android:antialias="true"/>
<TextView
@@ -72,7 +72,6 @@
</LinearLayout>
<LinearLayout
- android:id="@+id/entity_header_links"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
@@ -85,6 +84,7 @@
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="0dp"
+ android:visibility="gone"
android:minWidth="24dp"
android:src="@null"
android:tint="?android:attr/colorAccent"/>
@@ -95,6 +95,7 @@
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="0dp"
+ android:visibility="gone"
android:minWidth="24dp"
android:src="@null"
android:tint="?android:attr/colorAccent"/>
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
new file mode 100644
index 0000000..1a47afc
--- /dev/null
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -0,0 +1,92 @@
+<?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.
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="@android:color/transparent"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@+id/checkbox_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="56dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <include layout="@layout/preference_widget_checkbox" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="64dp"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index d34820c..1dd7838 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan nie skandeer vir netwerke nie"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gestoor"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Gedeaktiveer"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-opstelling het misluk"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sal op grond van jou gebruik waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meer as <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vra elke keer"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Totdat jy dit afskakel"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Sopas"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aansluitingprogram om opgedateerde grafikadrywer in ontwikkeling te gebruik"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Foonluidspreker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b595e2b..2ce5f44 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ለአውታረመረቦች መቃኘት አይቻልም"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"የለም"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ተቀምጧል"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ተሰናክሏል"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"የአይ.ፒ. ውቅረት መሰናከል"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"በአጠቃቀምዎ መሠረት እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"እስከ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ገደማ ድረስ መቆየት አለበት"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"እስከ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"ከ<xliff:g id="TIME_REMAINING">%1$s</xliff:g> በላይ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ሁልጊዜ ጠይቅ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"እስኪያጠፉት ድረስ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ልክ አሁን"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"በግንባታ ላይ የተዘመነ የግራፊክስ ነጂን ለመጠቀም መተግበሪያን መርጠው ያስገቡ"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"የስልክ ድምጽ ማጉያ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index f8c2ba2..23f3a12 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"لا يمكن فحص الشبكات"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"بدون"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غير مفعّلة"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"تعذّرت تهيئة عنوان IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> حسب استخدامك."</string>
<string name="power_discharge_by" msgid="6453537733650125582">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g>."</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"حتى <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"يتبقى أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -451,4 +453,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"الطلب في كل مرة"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"إلى أن توقف الوضع يدويًا"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"للتو"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"فعِّل التطبيق لاستخدام برنامج تشغيل الرسومات المُحدَّث في تطوير البرامج."</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"مكبر صوت الهاتف"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 72122c2..86d1459 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটৱৰ্ক বিচাৰি স্কেন কৰিব পৰা নাই"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"নাই"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ছেভ কৰি থোৱা নেটৱৰ্কসমূহ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"নিষ্ক্ৰিয় হৈ আছে"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগাৰেশ্বন বিফল হৈছে"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> পৰ্যন্ত"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>তকৈও কম সময় বাকী আছে"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>তকৈও কম সময় বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>তকৈও বেছি সময় বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্ৰতিবাৰতে সোধক"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"আপুনি অফ নকৰা পর্যন্ত"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"এই মাত্ৰ"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"বিকাশকাৰ্য চলি থকা আপডে\'টেড গ্ৰাফিক ড্ৰাইভাৰ ব্যৱহাৰ কৰিবলৈ এপ্ অপ্ট ইন কৰক"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ফ’নৰ স্পীকাৰ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 23008e2..17d0ca6 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Şəbəkə axtarmaq olmur"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Heç biri"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Yadda saxlanılan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiv"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Konfiqurasiya Uğursuzluğu"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"İstifadəyə əsasən təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> olana qədər"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Qalan vaxt <xliff:g id="TIME_REMAINING">%1$s</xliff:g> və daha çoxdur (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Hər dəfə soruşun"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Deaktiv edənə qədər"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"İndicə"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Qrafik drayverdən istifadə etmək üçün tətbiq seçin"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon spikeri"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3ee8589..56052d2 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nije moguće skenirati mreže"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfiguracija je otkazala"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g> na osnovu korišćenja"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uvek pitaj"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogući aplikaciju za korišćenje upravljačkog programa grafičke katice u razvoju"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 64253bf..f2c2046 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не атрымлiваецца выканаць сканаванне для сетак"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Захавана"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Адключана"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Збой канфігурацыі IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g> пры цяперашнім узроўні выкарыстання"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Зараду (<xliff:g id="LEVEL">%2$s</xliff:g>) хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Да <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Засталося менш за <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Узровень зараду батарэі: <xliff:g id="LEVEL">%2$s</xliff:g> (хопіць менш чым на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Узровень зараду батарэі: <xliff:g id="LEVEL">%2$s</xliff:g> (хопіць больш чым на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Заўсёды пытацца"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Пакуль не выключыце"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Зараз"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Выбраная праграма, якая выкарыстоўвае абноўлены драйвер графічнай сістэмы (падчас распрацоўкі)"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Дынамік тэлефона"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 668aa2d..5b8d6b8 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да се сканира за мрежи"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Запазено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Деактивирани"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Неуспешно конфигуриране на IP адреса"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> въз основа на използването"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Следва да издържи до около <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Остава/т повече от <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Да се пита винаги"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"До изключване"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Току-що"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Включване на приложението за използване на актуализирания графичен драйвер в разработка"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Високоговорител на телефона"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index c6eed2c..7efad99 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটওয়ার্কগুলির জন্য স্ক্যান করা যাবে না"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"কোনো কিছুই নয়"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"সংরক্ষিত"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"অক্ষম হয়েছে"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগারেশনের ব্যর্থতা"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"বর্তমান ব্যবহার অনুযায়ী আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> এর থেকেও কম বাকি আছে"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"আর <xliff:g id="THRESHOLD">%1$s</xliff:g>-এর কম চার্জ বাকি আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"আরও <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-এর বেশি চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্রতিবার জিজ্ঞেস করা হবে"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"এখনই"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ডেভলপমেন্টে আপডেট হওয়া গ্রাফিক্স ড্রাইভার ব্যবহার করতে অ্যাপ বেছে নিন"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ফেনের স্পিকার"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 16179fb..92435f7 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ne može skenirati mreže"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Greška u konfiguraciji IP-a"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g> na osnovu vaše upotrebe"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Trebala bi trajati do otprilike <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prijavi aplikaciju za korištenje ažuriranog grafičkog drajvera u razvoju"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index f906ea4..3182f16 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No es poden cercar xarxes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Cap"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Desat"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivat"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuració d\'IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en fas"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Temps restant superior a <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pregunta sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Fins que no ho desactivis"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Ara mateix"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicació activada per utilitzar el controlador de gràfics actualitzat en desenvolupament"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altaveu del telèfon"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index f9c8669..3785433 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nelze hledat sítě"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Žádné"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Uloženo"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuto"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Selhání konfigurace protokolu IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Při vašem obvyklém využití vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zbývá více než <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pokaždé se zeptat"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dokud tuto funkci nevypnete"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Právě teď"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Přihlaste aplikaci k použití vyvíjeného aktualizovaného grafického ovladače"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefonu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 6b2bd98..eeeccee 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Der kan ikke søges efter netværk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gemt"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiveret"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfejl"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> baseret på dit forbrug"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Indtil <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Der er mere end <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spørg hver gang"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Indtil du deaktiverer"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Lige nu"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Tilvælg en app, der skal bruge den opdaterede grafikdriver under udvikling"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonens højttaler"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index ad6f5f2..95f7416 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Netzwerkscan nicht möglich"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Keine"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gespeichert"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiviert"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-Konfigurationsfehler"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sollte basierend auf deiner Nutzung etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Sollte etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Sollte etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Bis <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mehr als <xliff:g id="TIME_REMAINING">%1$s</xliff:g> verbleibend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Jedes Mal fragen"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Bis zur Deaktivierung"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Gerade eben"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App aktivieren, um den aktualisierten Grafiktreiber in der Entwicklung zu verwenden"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon-Lautsprecher"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 5bfbf2c..310b6cf 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Δεν είναι δυνατή η σάρωση για δίκτυα"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Καμία"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Αποθηκευμένο"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Απενεργοποιημένο"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Αποτυχία διαμόρφωσης διεύθυνσης IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου, ανάλογα με τη χρήση σας"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Έως τις <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Απομένουν περισσότερα/ες από <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Να ερωτώμαι κάθε φορά"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Μέχρι την απενεργοποίηση"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Μόλις τώρα"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Επιλέξτε μια εφαρμογή για τη χρήση του ενημερωμένου προγράμματος οδήγησης γραφικών σε ανάπτυξη"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Ηχείο τηλεφώνου"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 6927fda..d7ed49a 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphcis driver in developement"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 675084f..ecec63c 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se pueden buscar las redes."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitada"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> según el uso"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que lo desactives"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Recién"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar app para que use el controlador de gráficos actualizado en el desarrollo"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index b51b847..b8fdf30 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se puede buscar redes."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardado"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> según el uso"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hasta: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Queda menos del <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Queda más del <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que se desactive"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Justo ahora"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar aplicación para usar controlador de gráficos actualizado en desarrollo"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index fb077be..1246626 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Võrke ei saa kontrollida"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Puudub"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvestatud"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Keelatud"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP seadistamise ebaõnnestumine"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Peaks teie kasutuse põhjal kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Jäänud on üle <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Küsi iga kord"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Kuni välja lülitate"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Äsja"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lubage rakendus, et kasutada arenduses olevat värskendatud graafikadraiverit"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoni kõlar"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 3d3b8c3..68b4840 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ezin dira sareak bilatu"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Bat ere ez"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gordeta"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desgaituta"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ezin izan da konfiguratu IP helbidea"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Erabileraren arabera, ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> arte"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen dira"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen da (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> baino gehiago gelditzen da (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Galdetu beti"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Desaktibatu arte"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Oraintxe"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Hautatu zein aplikaziorekin erabili nahi duzun garatze-prozesuan dagoen grafikoen kontrolatzaile eguneratua"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonoaren bozgorailua"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 0af118e..7882b056 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"اسکن شبکهها امکانپذیر نیست"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"هیچکدام"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ذخیرهشده"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غیرفعال شد"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"پیکربندی IP انجام نشد"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"باتوجه به میزان مصرفتان، باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) شارژ داشته باشید"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"تا <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> باقی مانده"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"بیش از <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"هربار پرسیده شود"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"تا زمانیکه آن را خاموش کنید"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"هماکنون"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"برنامه انتخابشده برای استفاده از درایور گرافیک بهروزرسانیشده در برنامهنویس"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"بلندگوی تلفن"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index dde10d0d..1904c4d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Verkkoja ei voi etsiä."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ei mitään"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Tallennettu"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Pois käytöstä"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-kokoonpanovirhe"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Varaus loppuu käyttösi perusteella noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> saakka"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Yli <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Kysy aina"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Kunnes poistat sen käytöstä"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Äsken"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lisää sovellus käyttämään päivitettyä grafiikkaohjainta kehitysvaiheessa"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Puhelimen kaiutin"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 1257ea5..45b2872 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivés"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>, en fonction de votre usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionnez l\'application pour utiliser le pilote graphique mis à jour en mode de conception"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 444a52e..13361d7 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivé"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionner une application pour le développement de laquelle utiliser le pilote graphique mis à jour"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8df2697..e8cf009 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Non se poden explorar redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ningunha"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivadas"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Erro na configuración de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"En función do uso, debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Ata: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo restante inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tempo restante: máis de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Ata a desactivación"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Subscribirse á aplicación para utilizar o controlador de gráficos actualizado en desenvolvemento"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altofalante do teléfono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 4a92f2a..918920d 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"નેટવર્ક્સ માટે સ્કૅન કરી શકતા નથી"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"કોઈ નહીં"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"સાચવેલા"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"અક્ષમ કર્યો"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP કન્ફિગરેશન નિષ્ફળ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"તમારા વપરાશના આધારે લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> સુધી"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> કરતાં વધુ સમય બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"દર વખતે પૂછો"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"તમે બંધ ન કરો ત્યાં સુધી"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"હમણાં જ"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"અપડેટ કરેલ ગ્રાફિક્સ ડ્રાઇવરનો ઉપયોગ કરવા માટે અૅપ પસંદ કરો"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ફોન સ્પીકર"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 6d88c35..890d036 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्क के लिए स्कैन नहीं कर सकता"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"कोई नहीं"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सेव किया गया"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फ़िगरेशन की विफलता"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"आपके इस्तेमाल के हिसाब से बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> तक"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम समय बचा है"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> से ज़्यादा चलने लायक बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"हर बार पूछें"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"जब तक आप इसे बंद नहीं करते"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"अभी-अभी"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"डेवलपमेंट में अपडेट किए गए ग्राफ़िक्स ड्राइवर का इस्तेमाल करने के लिए ऐप्लिकेशन में ऑप्ट इन करें"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फ़ोन स्पीकर"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 32c9a62..1464b91 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Skeniranje mreža nije moguće"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Spremljeno"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-a nije uspjela"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g> na temelju vaše upotrebe"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Upravo sad"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uključi aplikaciju za upotrebu ažuriranog upravljačkog programa u razvoju"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index ed19267..c9fdd45 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nem lehet beolvasni a hálózatokat"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nincs"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Mentve"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Letiltva"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurációs hiba"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"A használat alapján nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Eddig: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Kevesebb mint <xliff:g id="TIME_REMAINING">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Mindig kérdezzen rá"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Kikapcsolásig"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Az imént"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"A frissített, fejlesztés alatt álló grafikus drivert használja a választott alkalmazás"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hangszórója"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 6d516ea..3b13ed1 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Հնարավոր չէ սկանավորել ցանցերը"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ոչ մեկը"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Պահված է"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Անջատված"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP կարգավորման ձախողում"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Լիցքը պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>՝ կախված օգտագործման եղանակից"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Լիցքը (<xliff:g id="LEVEL">%2$s</xliff:g>) պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Լիցքը պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Հարցնել ամեն անգամ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Մինչև չանջատեք"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Հենց նոր"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ընտրված հավելվածը, որը պետք է օգտագործի թարմացված գրաֆիկական սարքավարը մշակման ժամանակ"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Հեռախոսի բարձրախոս"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index a5f2317..4d1b2df 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak dapat memindai jaringan"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Tidak ada"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Nonaktif"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan Anda"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tersisa lebih dari <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Selalu tanya"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Sampai Anda menonaktifkannya"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Baru saja"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ikut sertakan aplikasi untuk menggunakan driver grafis yang diupdate dalam pengembangan"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ponsel"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 6cf09a6..600ffa4 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ekki er hægt að leita að netum"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ekkert"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Vistað"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Óvirkt"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-stillingarvilla"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g> miðað við notkun þína"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Til klukkan <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meira en <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spyrja í hvert skipti"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Þar til þú slekkur"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Rétt í þessu"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velja að nota uppfærðan myndefnisrekil í þróun í forriti"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Símahátalari"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index a1b9b82..068aa91 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossibile cercare reti"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nessuna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvata"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disattivata"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Errore configurazione IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tempo stimato rimanente in base al tuo utilizzo: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Tempo stimato rimanente: <xliff:g id="TIME">%1$s</xliff:g> circa (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Tempo stimato rimanente: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fino alle ore <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Chiedi ogni volta"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Fino alla disattivazione"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Adesso"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Attiva l\'app per utilizzare il driver grafico aggiornato nella versione di sviluppo"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlante telefono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 1a50622..fa38f77 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"לא ניתן לסרוק לאיתור רשתות"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ללא"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"נשמר"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"מושבת"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"כשל בתצורת IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g> על סמך השימוש במכשיר"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"נותרו יותר מ-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"שאל בכל פעם"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"עד הכיבוי"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"הרגע"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"האפליקציה שנבחרה לשימוש במנהל ההתקן המעודכן לגרפיקה שבפיתוח"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"רמקול של טלפון"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4c544af..9927654 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ネットワークをスキャンできません"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"なし"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"保存済み"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"無効"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP設定エラー"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> まで"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"残り時間: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>以上(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"毎回確認"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"OFF にするまで"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"たった今"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"更新したグラフィックス ドライバを開発に使用するオプトイン アプリ"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"スマートフォンのスピーカー"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 92f3049..7ba0153 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ქსელების სკანირება არა არის შესაძლებელი"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"არცერთი"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"დამახსოვრებულია"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"გამორთულია"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP კონფიგურაციის შეფერხება"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>, მოხმარების გათვალისწინებით"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g>-მდე"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები დრო (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"დარჩენილია <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ზე მეტი დრო (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ყოველთვის მკითხეთ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"გამორთვამდე"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ახლახან"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"გააქტიურების აპი, რომელიც გამოიყენებს შემუშავების პროცესში მყოფ, განახლებულ გრაფიკულ დრაივერს"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ტელეფონის დინამიკი"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index b56c6fd..d5b4441 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Желілерді шолу мүмкін емес"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ешқандай"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сақталды"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Өшірілген"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясының қатесі"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Пайдалануға байланысты шамамен <xliff:g id="TIME">%1$s</xliff:g> уақытқа жетеді"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) уақытқа жетеді"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> уақытқа жетеді"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> дейін"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> шамасынан көп уақыт қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Әрдайым сұрау"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Өшірілгенге дейін"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Дәл қазір"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Әзірлеу барысында қолданба жаңартылған графика драйверін пайдаланады"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефон динамигі"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 5860473..d1d1c76 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"មិនអាចវិភាគរកបណ្ដាញ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"គ្មាន"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"បានរក្សាទុក"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"បានបិទ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ការកំណត់រចនាសម្ព័ន្ធ IP បរាជ័យ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"គួរតែអាចប្រើបានរហូតដល់ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g> ដោយផ្អែកលើការប្រើប្រាស់របស់អ្នក"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"គួរតែអាចប្រើបានរហូតដល់ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"គួរតែអាចប្រើបានរហូតដល់ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"រហូតដល់ម៉ោង <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"នៅសល់តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"នៅសល់តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"នៅសល់ច្រើនជាង <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"សួរគ្រប់ពេល"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"រហូតទាល់តែអ្នកបិទ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"អម្បាញ់មិញ"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ភ្ជាប់កម្មវិធី ដើម្បីប្រើដ្រាយវើក្រាហ្វិកដែលបានដំឡើងជំនាន់សម្រាប់ការអភិវឌ្ឍ"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ឧបករណ៍បំពងសំឡេងទូរសព្ទ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 87e18e6..46759f5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ನೆಟ್ವರ್ಕ್ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ಯಾವುದೂ ಇಲ್ಲ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ಉಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ನಿಮ್ಮ ಬಳಕೆ ಆಧರಿಸಿ <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್ ರನ್ ಆಗಬೇಕು"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ಸಮಯದವರೆಗೆ ಫೋನ್ ರನ್ ಆಗಬೇಕು"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್ ರನ್ ಆಗಬೇಕು"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ರವರೆಗೆ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ನಿಮಿಷಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ಕ್ಕಿಂತ ಕಡಿಮೆ (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ಇದೀಗ"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ಅಭಿವೃದ್ಧಿಯಲ್ಲಿ ಅಪ್ಡೇಟ್ ಮಾಡಲಾದ ಗ್ರಾಫಿಕ್ಗಳ ಡ್ರೈವರ್ ಬಳಸಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 3333c0d..067175b 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"네트워크를 검색할 수 없습니다."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"없음"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"저장됨"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"사용 중지됨"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 설정 실패"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"사용량을 기준으로 약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g>까지"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> 이상 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -435,7 +437,7 @@
<string name="cancel" msgid="6859253417269739139">"취소"</string>
<string name="okay" msgid="1997666393121016642">"확인"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"켜기"</string>
- <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"알림 일시중지 사용 설정"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"방해 금지 모드 사용 설정"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"사용 안함"</string>
<string name="zen_interruption_level_priority" msgid="2078370238113347720">"중요 알림만"</string>
<string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"항상 확인"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"사용 중지할 때까지"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"조금 전"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"개발 중인 업데이트된 그래픽 드라이버를 사용할 앱을 선택하세요."</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"휴대전화 스피커"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index cbbc47c..8e994da 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Түйүндөрдү издөө мүмкүн эмес"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Жок"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сакталды"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Өчүрүлгөн"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясы бузулду"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Колдонгонуңузга караганда болжол менен <xliff:g id="TIME">%1$s</xliff:g> кийин өчөт"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) кийин өчөт"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> кийин өчөт"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> чейин"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ашыгыраак убакыт калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ар дайым суралсын"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Бул функция өчүрүлгөнгө чейин"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Азыр эле"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Иштеп чыгууда жаңыртылган графикалык драйверлерди пайдалануу үчүн колдонмону кошуңуз"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефондун динамиги"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 21e4679..7bf46c0 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ບໍ່ສາມາດກວດຫາເຄືອຂ່າຍໄດ້"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ບໍ່ໃຊ້"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ບັນທຶກແລ້ວ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ປິດການນຳໃຊ້"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ການຕັ້ງຄ່າ IP ລົ້ມເຫຼວ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"ຈົນກວ່າຈະຮອດ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"ຍັງເຫຼືອຫຼາຍກວ່າ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ຖາມທຸກເທື່ອ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"ຈົນກວ່າທ່ານຈະປິດ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ຕອນນີ້"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ເຂົ້າຮ່ວມແອັບເພື່ອໃຊ້ໄດຣເວີກຣາຟິກທີ່ອັບເດດແລ້ວໃນການພັດທະນາ"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ລຳໂພງໂທລະສັບ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index cbff9e7..9da3d52 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nepavyksta nuskaityti tinklų"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nėra"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Išsaugotas"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Neleidžiama"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigūracijos triktis"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Pagal tai, kaip naudojama, turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Iki <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Liko daugiau nei <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Klausti kaskart"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Kol išjungsite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Ką tik"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Pasirinkti programą, kuri bus naudojama su atnaujinta kuriama grafikos tvarkykle"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefono garsiakalbis"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d200828..2233468 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nevar skenēt tīklus"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nav"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saglabāts"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Atspējots"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigurācijas kļūme"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Ņemot vērā lietojumu, darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Atlikušais laiks — mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Atlicis mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Atlicis vairāk nekā <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vaicāt katru reizi"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Līdz brīdim, kad izslēgsiet"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Tikko"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Izvēlēties izmantot atjaunināto grafikas dzini šīs lietotnes izstrādē"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Tālruņa skaļrunis"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index db16847..534d154 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да скенира за мрежи"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ниедна"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Зачувано"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Оневозможено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Конфигурирањето ИП не успеа"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g> според вашето користење"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Уште повеќе од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Секогаш прашувај"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Додека не го исклучите"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Неодамнешни"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Прифатете ја апликацијата за да се користи ажурираниот драјвер за графика во програмирање"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефонски звучник"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index c3af968..7a335cd 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"നെറ്റ്വർക്കുകൾക്കായി സ്കാൻ ചെയ്യാനായില്ല"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ഒന്നുമില്ല"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"സംരക്ഷിച്ചു"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP കോൺഫിഗറേഷൻ പരാജയം"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"നിങ്ങളുടെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ് (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> വരെ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ൽ കൂടുതൽ സമയം ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"എപ്പോഴും ചോദിക്കുക"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ഇപ്പോൾ"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"വികസനത്തിൽ, അപ്ഡേറ്റ് ചെയ്ത ഗ്രാഫിക്സ് ഡ്രൈവർ ഉപയോഗിക്കാൻ ഓപ്റ്റ് ഇൻ ചെയ്യുക"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ഫോൺ സ്പീക്കർ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 8627e1b..4816643 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Сүлжээнүүдийг скан хийх боломжгүй"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Байхгүй"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Хадгалагдсан"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Идэвхгүйжүүлсэн"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP тохируулга амжилтгүй"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Таны хэрэглээнд тулгуурлан ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> хүртэл"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-с их хугацаа үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Тухай бүрт асуух"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Таныг унтраах хүртэл"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Дөнгөж сая"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Хөгжүүлэлтэд буй шинэчилсэн график драйверийг ашиглахын тулд аппад нэгдэх"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Утасны чанга яригч"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index cd7f175..8ebb182 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्कसाठी स्कॅन करू शकत नाही"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"काहीही नाही"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सेव्ह केले"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फिगरेशन अयशस्वी"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"तुमच्या वापरावर अवलंबून सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकावी"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकेल (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकावी"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी शिल्लक आहे"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी वेळ शिल्लक आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> पेक्षा जास्त वेळ शिल्लक आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक वेळी विचारा"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"तुम्ही बंद करेपर्यंत"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"आत्ताच"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"अपडेट केलेले ग्राफिक ड्राइव्हर डेव्हलमेंटमध्ये वापरण्यासाठी अॅप निवडा"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनचा स्पीकर"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index d0b2e12..3593882 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak boleh mengimbas untuk rangkaian"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Tiada"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Dinyahdayakan"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan anda"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tinggal kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Lebih daripada <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Tanya setiap kali"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Sehingga anda matikan"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Sebentar tadi"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sertakan apl untuk menggunakan pemacu grafik yang dikemas kini dalam pembangunan"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Pembesar suara telefon"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 5f5957c..7ac3742 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ကွန်ယက်များကို စကင်မလုပ်နိုင်ပါ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"တစ်ခုမျှ မဟုတ်ပါ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"သိမ်းဆည်းပြီး"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ပိတ်ထားသည်"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ပြုပြင်ခြင်း မအောင်မြင်ပါ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"သင်၏ အသုံးပြုမှုအပေါ် အခြေခံ၍ <xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည်"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည်"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> အထိ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ခန့်သာ ကျန်တော့သည်"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> အောက်သာ ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ကျော် ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"အမြဲမေးပါ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"သင်ပိတ်လိုက်သည် အထိ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ယခုလေးတင်"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ဆော့ဖ်ဝဲရေးဆွဲမှုအတွင်း အပ်ဒိတ်လုပ်ထားသော ဂရပ်ဖစ်ဒရိုင်ဗာကို အသုံးပြုရန် အက်ပ်ကို ရွေးချယ်ပါ"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ဖုန်းစပီကာ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 3c240f8..ae9c5f2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan ikke søke etter nettverk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Lagret"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Slått av"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurasjonsfeil"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>, basert på bruken din"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Til <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mer enn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spør hver gang"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Til du slår av"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Nå nettopp"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velg app for å bruke en oppdatert grafikkdriver som er under utvikling"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonhøyttaler"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 1699870..0b4510fd 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"सञ्जालका लागि स्क्यान गर्न सक्दैन"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"कुनै पनि होइन"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सुरक्षित गरियो"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"असक्षम पारियो"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP विन्यास असफल"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"तपाईंको प्रयोगका आधारमा लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"ब्याट्री लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> सम्म"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी छ"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> भन्दा बढी समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक पटक सोध्नुहोस्"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"तपाईंले निष्क्रिय नपार्दासम्म"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"अहिले भर्खरै"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"विकासको क्रममा अद्यावधिक गरिएको ग्राफिक ड्राइभर प्रयोग गर्न अप्ट इन गर्नुहोस्"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनको स्पिकर"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 738df94..f227e1c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan niet zoeken naar netwerken"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Opgeslagen"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Uitgeschakeld"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-configuratie mislukt"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g> op basis van je gebruik"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meer dan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> resterend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Altijd vragen"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Totdat je uitschakelt"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Zojuist"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Meld een app aan om het geüpdatete grafische stuurprogramma in ontwikkeling te gebruiken"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoonluidspreker"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 2d39cc6..bf8493a 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ନେଟ୍ୱର୍କଗୁଡ଼ିକୁ ଖୋଜିପାରୁନାହିଁ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"କିଛି ନାହିଁ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ସେଭ୍ ହୋଇଗଲା"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ଅକ୍ଷମ ହୋଇଛି"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP କନଫିଗରେଶନ ବିଫଳ ହୋଇଛି"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ଆପଣଙ୍କର ବ୍ୟବହାରକୁ ଆଧାର କରି ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"ବ୍ୟାଟେରୀ ପାଖାପାଖି <xliff:g id="TIME">%1$s</xliff:g> ଚାଲିବ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ରୁ କମ୍ ସମୟ ବଳକା ଅଛି"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ରୁ କମ୍ ସମୟ ବଳକା ଅଛି (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ରୁ ଅଧିକ ସମୟ ବଳକା ଅଛି(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ DND ଅନ୍ ରହିବ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ଏହିକ୍ଷଣି"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ଡେଭଲପ୍ମେଣ୍ଟରେ ଅପ୍ଡେଟ୍ ଗ୍ରାଫିକ୍ସ ଡ୍ରାଇଭର୍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍ଟ ଇନ୍ ଅପ୍ଲିକେସନ୍"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ଫୋନ୍ ସ୍ପିକର୍"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 61f0447..f948a7e 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ਨੈਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ਕੋਈ ਨਹੀਂ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ਰੱਖਿਅਤ ਕੀਤਾ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ਅਯੋਗ ਬਣਾਇਆ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ਕੌਂਫਿਗਰੇਸ਼ਨ ਅਸਫਲਤਾ"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ਤੱਕ"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਤੋਂ ਵੱਧ ਸਮਾਂ ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ਹੁਣੇ ਹੀ"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ਅੱਪਡੇਟ ਕੀਤੇ ਵਿਕਾਸ-ਅਧੀਨ ਗ੍ਰਾਫਿਕਸ ਡਰਾਈਵਰ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਐਪ ਦੀ ਚੋਣ ਕਰੋ"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 519f82c..c75a894 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nie można wyszukać sieci."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Brak"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Zapisana"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Wyłączona"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Błąd konfiguracji IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Na podstawie Twojego sposobu korzystania jeszcze około <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Pozostało ponad: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Zawsze pytaj"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dopóki nie wyłączysz"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Przed chwilą"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Wybierz aplikację, która ma używać opracowywanego sterownika grafiki"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Głośnik telefonu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 16844a2..c273f59 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> com base no seu uso"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index f01ddfa..26e4729 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> com base na sua utilização."</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g>."</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até à(s) <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Resta(m) mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Até ser desativado"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Optar pela aplicação para utilizar a placa gráfica atualizada em desenvolvimento"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altifalante do telemóvel"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 16844a2..c273f59 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> com base no seu uso"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 8a7b440..94f4842 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nu se poate scana pentru rețele"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Niciuna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvată"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Dezactivată"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Eroare de configurație IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"În baza utilizării, ar trebui să reziste până la aproximativ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"a mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"A mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"A mai rămas mai mult de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Întreabă de fiecare dată"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Până când dezactivați"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Chiar acum"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicația pentru înscriere pentru a folosi driverul actualizat al plăcii grafice este în dezvoltare"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Difuzorul telefonului"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 31274d1..1c331d8 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не удалось начать поиск сетей."</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Нет"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сохранено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Отключено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ошибка IP-конфигурации"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"При текущем уровне использования заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Заряда (<xliff:g id="LEVEL">%2$s</xliff:g>) хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Осталось менее <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит менее чем на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит более чем на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Всегда спрашивать"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Пока вы не отключите"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Только что"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Приложение будет использовать обновленный драйвер графической системы (на стадии разработки)"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Встроенный динамик"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index b22e068..608ff7f 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ජාල සඳහා පරිලෝකනය කළ නොහැක"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"කිසිවක් නැත"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"සුරකින ලදි"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"අබලයි"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP වින්යාස කිරීම අසාර්ථකයි"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ඔබේ භාවිතය මත පදනම්ව <xliff:g id="TIME">%1$s</xliff:g> පමන වන තෙක් තිබිය යුතුය"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) පමණ වන තෙක් තිබිය යුතුය"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> පමණ වන තෙක් තිබිය යුතුය"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> දක්වා"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිව ඇත"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිය (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ට වඩා වැඩියෙන් ඉතිරිය (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"සෑම විටම ඉල්ලන්න"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"ඔබ ක්රියාවිරහිත කරන තුරු"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"මේ දැන්"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"වැඩිදියුණු වෙමින් ඇති යාවත්කාලීන කළ චිත්රක ධාවකය භාවිත කිරීමට යෙදුමට ඇතුළු වන්න"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"දුරකථන ස්පීකරය"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 43923b8..2f76ef9 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Siete sa nedajú vyhľadávať"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Žiadne"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Uložené"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuté"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Zlyhanie konfigurácie adresy IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g> v závislosti od využitia"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Mal by vydržať približne <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zostáva viac ako <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vždy sa opýtať"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dokiaľ túto funkciu nevypnete"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Teraz"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prihlásiť aplikáciu, ktorá má používať aktualizovaný ovládač grafickej karty vo vývoji"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefónu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 6d392fe..64124cb 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ni mogoče iskati omrežij"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Brez"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Shranjeno"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogočeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-ja ni uspela"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Naprava bi morala glede na način uporabe delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostanek: manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostali čas delovanja: manj kot <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vedno vprašaj"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dokler ne izklopite"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Pravkar"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogočena aplikacija za uporabo posodobljenega grafičnega gonilnika pri razvoju"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvočnik telefona"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index e05a019..25d575e 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nuk mund të skanojë për rrjete"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Asnjë"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"U ruajt"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Të çaktivizuara"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Dështim në konfigurimin e IP-së"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> bazuar në përdorimin tënd"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Deri në <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> të mbetura"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mbeten më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mbeten më shumë se <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pyet çdo herë"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Deri sa ta çaktivizosh"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Pikërisht tani"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prano aplikacionin për të përdorur drejtuesin e përditësuar të grafikës që është në zhvillim"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlanti i telefonit"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 371b909..b58b47c 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Није могуће скенирати мреже"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Нема"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сачувано"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Онемогућено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурација је отказала"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g> на основу коришћења"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Преостало је више од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Увек питај"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Док не искључите"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Управо"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Омогући апликацију за коришћење управљачког програма графичке катице у развоју"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Звучник телефона"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index e6872bb..7198b84 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Det går inte att söka efter nätverk"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sparat"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inaktiverad"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfel"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g> utifrån din användning"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Till kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mer än <xliff:g id="TIME_REMAINING">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Fråga varje gång"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Tills du inaktiverar funktionen"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Nyss"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Välj om appen ska använda den uppdaterade grafikdrivrutinen under utveckling"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Mobilens högtalare"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 23efb91..657b54f 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Haiwezi kutambaza mitandao"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Hamna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Imehifadhiwa"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Imezimwa"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Haikuweza Kusanidi IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Inapaswa kudumu hadi <xliff:g id="TIME">%1$s</xliff:g> kulingana na jinsi unavyoitumia"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Inapaswa kudumu kwa takribani <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Inapaswa kudumu hadi <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hadi <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zimesalia zaidi ya <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uliza kila wakati"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Hadi utakapoizima"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Sasa hivi"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chagua programu itakayotumia kiendeshaji cha michoro kilichosasishwa katika hatua ya kusanidi"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Spika ya simu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 53ba738..59b42d8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"நெட்வொர்க்குகளுக்கு ஸ்கேன் செய்யப்படவில்லை"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ஏதுமில்லை"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"சேமிக்கப்பட்டது"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"முடக்கப்பட்டது"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP உள்ளமைவில் தோல்வி"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"நீங்கள் பயன்படுத்துவதன் அடிப்படையில் <xliff:g id="TIME">%1$s</xliff:g> வரை உபயோகிக்க முடியும்"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> வரை பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> வரை பயன்படுத்த முடியும்"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> வரை"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும்"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>க்கும் மேல் பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ஒவ்வொரு முறையும் கேள்"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"ஆஃப் செய்யும் வரை"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"சற்றுமுன்"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"உருவாக்கத்திலுள்ள புதுப்பிக்கப்பட்ட கிராஃபிக்ஸ் டிரைவரைப் பயன்படுத்த ஆப்ஸைத் தேர்ந்தெடுக்கவும்"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"மொபைல் ஸ்பீக்கர்"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c8c6b4c..af233da 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"నెట్వర్క్ల కోసం స్కాన్ చేయడం సాధ్యపడదు"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ఏదీ లేదు"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"సేవ్ చేయబడింది"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"నిలిపివేయబడింది"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"మీ వినియోగం ఆధారంగా దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> వరకు"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> కంటే ఎక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ప్రతిసారి అడుగు"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"మీరు ఆఫ్ చేసే వరకు"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ఇప్పుడే"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"అభివృద్దిలో అప్డేట్ చేసిన గ్రాఫిక్ డ్రైవర్ను ఉపయోగించడానికి యాప్ని ప్రారంభించండి"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ఫోన్ స్పీకర్"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index d293d59..636c3f2 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ไม่สามารถสแกนหาเครือข่าย"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"ไม่มี"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"บันทึกแล้ว"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ปิดอยู่"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"การกำหนดค่า IP ล้มเหลว"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> เมื่อดูจากการใช้งานของคุณ"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"จนถึง <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"เหลืออีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"เหลือเวลาอีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"เหลือเวลามากกว่า <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ถามทุกครั้ง"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"จนกว่าคุณจะปิด"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"เมื่อสักครู่"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"เลือกใช้แอปเพื่อใช้ไดรเวอร์กราฟิกที่อัปเดตในการพัฒนา"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ลำโพงโทรศัพท์"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 9da4561..1bcc36a 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Hindi makapag-scan ng mga network"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Wala"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Na-save"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Naka-disable"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Pagkabigo ng Configuration ng IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> batay sa iyong paggamit"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Tatagal hanggang mga <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hanggang <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Magtanong palagi"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Hanggang sa i-off mo"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Ngayon lang"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App sa pag-opt in para magamit ang na-update na graphics driver na ginagawa"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ng telepono"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 2d5cd7f..ff669f6 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ağlar taranamıyor"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Yok"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Kaydedildi"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Devre dışı"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Yapılandırması Hatası"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Kullanımınıza göre saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Şu saate kadar: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"En fazla <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"En çok <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"En az <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Her zaman sor"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Siz kapatana kadar"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Az önce"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Güncellenmiş grafik sürücüsünü geliştirme ortamında kullanmak için uygulamayı kaydedin"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hoparlörü"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 512ea86..2ded8c1 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Неможливо здійснити сканування мереж"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Немає"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Збережено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Вимкнено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Помилка конфігурації IP-адреси"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"На основі даних про використання, вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Залишилося понад <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Запитувати щоразу"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Доки ви не вимкнете"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Щойно"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Вибраний додаток, який використовуватиме оновлений графічний драйвер під час розробки"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Динамік телефона"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 84ad3ed..7dc4690 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"نیٹ ورکس کیلئے اسکین نہيں کر سکتے ہیں"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"کوئی نہیں"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"محفوظ کردیا گیا"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غیر فعال"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP کنفیگریشن کی ناکامی"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> تک"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> سے زیادہ باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ہر بار پوچھیں"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"یہاں تک کہ آپ آف کر دیں"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ابھی ابھی"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ڈیولپمنٹ میں اپ ڈیٹ کردہ گرافکس ڈرائیور کو استعمال کرنے کے لیے ایپ آپٹ ان کریں"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"فون اسپیکر"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index af2ada5..5f92c9a 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tarmoqlarni tekshirib chiqishni iloji bo‘lmadi"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Hech qanday"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saqlandi"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"O‘chiq"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP manzilini sozlab bo‘lmadi"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Joriy holatda taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> gacha"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>dan ko‘proq vaqt qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Har safar so‘ralsin"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Bekor qilinmaguncha"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Hozir"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ilova yangilangan grafik drayverdan (hali ishlov jarayonida) foydalanadi"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon karnayi"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index f454127..eb10fb1 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Không thể dò tìm mạng"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Không"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Đã lưu"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Đã tắt"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Lỗi cấu hình IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g> dựa trên mức sử dụng của bạn"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Cho đến <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Còn lại hơn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Luôn hỏi"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Cho đến khi bạn tắt"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Vừa xong"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chọn ứng dụng để sử dụng trình điều khiển đồ họa được cập nhật trong giai đoạn phát triển"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Loa điện thoại"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 10a20be..7b14138 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"无法扫描网络"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"无"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已保存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 配置失败"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根据您的使用情况,估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"目前电量为 <xliff:g id="LEVEL">%2$s</xliff:g>,估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"直到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"电量剩余使用时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"电量剩余使用时间超过 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都询问"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"直到您将其关闭"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"刚刚"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"为应用启用更新后的显卡驱动,以在开发过程中使用"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手机扬声器"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 1510545..48b6959 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃瞄網絡"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根據您的使用情況,電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"還可用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩餘電量時間少於 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"還有少於 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"還有超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"直至您關閉為止"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"請選取應用程式,以在開發階段使用更新的顯示卡驅動程式"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 90c0d80..684569b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃描網路"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根據你的使用情形,預估可持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"目前電量 <xliff:g id="LEVEL">%2$s</xliff:g>,預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"電池可用時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"直到你關閉為止"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"選取要在開發階段使用最新版繪圖驅動程式的應用程式"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e4f9f4d7..d107a5a 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -21,6 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ayikwazi ukuhlola amanethiwekhi"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Lutho"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Kulondoloziwe"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Akusebenzi"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ukwehluleka kokulungiswa kwe-IP"</string>
@@ -374,6 +375,7 @@
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g> kusukela ekusetshenzisweni kwakho"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="107616694963545745">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Kuze kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kusele okungaphansi kunokungu-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Ngaphansi kuka-<xliff:g id="THRESHOLD">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Ngaphezu kuka-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Buza njalo"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Uze uvale isikrini"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"Khona manje"</string>
+ <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uhlelo lokusebenza lokukhetha ukungena olungasebenzisa idrayivu yamagrafikhi ekuthuthukiseni"</string>
+ <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Isipikha sefoni"</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 45a3bb0..9270d13 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -54,18 +54,17 @@
private final Context mContext;
private final BluetoothAdapter mLocalAdapter;
private final LocalBluetoothProfileManager mProfileManager;
+ private final Object mProfileLock = new Object();
BluetoothDevice mDevice;
private long mHiSyncId;
// Need this since there is no method for getting RSSI
short mRssi;
// mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
// because current sub device is only for HearingAid and its profile is the same.
- private final List<LocalBluetoothProfile> mProfiles =
- Collections.synchronizedList(new ArrayList<>());
+ private final List<LocalBluetoothProfile> mProfiles = new ArrayList<>();
// List of profiles that were previously in mProfiles, but have been removed
- private final List<LocalBluetoothProfile> mRemovedProfiles =
- Collections.synchronizedList(new ArrayList<>());
+ private final List<LocalBluetoothProfile> mRemovedProfiles = new ArrayList<>();
// Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
private boolean mLocalNapRoleConnected;
@@ -74,7 +73,7 @@
private int mMessageRejectionCount;
- private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
+ private final Collection<Callback> mCallbacks = new ArrayList<>();
// How many times user should reject the connection to make the choice persist.
private final static int MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST = 2;
@@ -134,36 +133,42 @@
}
return;
}
- if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
- if (profile instanceof MapProfile) {
- profile.setPreferred(mDevice, true);
- }
- if (!mProfiles.contains(profile)) {
- mRemovedProfiles.remove(profile);
- mProfiles.add(profile);
- if (profile instanceof PanProfile &&
- ((PanProfile) profile).isLocalRoleNap(mDevice)) {
- // Device doesn't support NAP, so remove PanProfile on disconnect
- mLocalNapRoleConnected = true;
+
+ synchronized (mProfileLock) {
+ if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
+ if (profile instanceof MapProfile) {
+ profile.setPreferred(mDevice, true);
}
+ if (!mProfiles.contains(profile)) {
+ mRemovedProfiles.remove(profile);
+ mProfiles.add(profile);
+ if (profile instanceof PanProfile
+ && ((PanProfile) profile).isLocalRoleNap(mDevice)) {
+ // Device doesn't support NAP, so remove PanProfile on disconnect
+ mLocalNapRoleConnected = true;
+ }
+ }
+ } else if (profile instanceof MapProfile
+ && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+ profile.setPreferred(mDevice, false);
+ } else if (mLocalNapRoleConnected && profile instanceof PanProfile
+ && ((PanProfile) profile).isLocalRoleNap(mDevice)
+ && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+ Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
+ mProfiles.remove(profile);
+ mRemovedProfiles.add(profile);
+ mLocalNapRoleConnected = false;
}
- } else if (profile instanceof MapProfile &&
- newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
- profile.setPreferred(mDevice, false);
- } else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
- ((PanProfile) profile).isLocalRoleNap(mDevice) &&
- newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
- Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
- mProfiles.remove(profile);
- mRemovedProfiles.add(profile);
- mLocalNapRoleConnected = false;
}
+
fetchActiveDevices();
}
public void disconnect() {
- for (LocalBluetoothProfile profile : mProfiles) {
- disconnect(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ disconnect(profile);
+ }
}
// Disconnect PBAP server in case its connected
// This is to ensure all the profiles are disconnected as some CK/Hs do not
@@ -203,6 +208,10 @@
mHiSyncId = id;
}
+ public boolean isHearingAidDevice() {
+ return mHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ }
+
void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
@@ -210,32 +219,35 @@
}
private void connectWithoutResettingTimer(boolean connectAllProfiles) {
- // Try to initialize the profiles if they were not.
- if (mProfiles.isEmpty()) {
- // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
- // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
- // from bluetooth stack but ACTION.uuid is not sent yet.
- // Eventually ACTION.uuid will be received which shall trigger the connection of the
- // various profiles
- // If UUIDs are not available yet, connect will be happen
- // upon arrival of the ACTION_UUID intent.
- Log.d(TAG, "No profiles. Maybe we will connect later");
- return;
- }
+ synchronized (mProfileLock) {
+ // Try to initialize the profiles if they were not.
+ if (mProfiles.isEmpty()) {
+ // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
+ // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been
+ // updated from bluetooth stack but ACTION.uuid is not sent yet.
+ // Eventually ACTION.uuid will be received which shall trigger the connection of the
+ // various profiles
+ // If UUIDs are not available yet, connect will be happen
+ // upon arrival of the ACTION_UUID intent.
+ Log.d(TAG, "No profiles. Maybe we will connect later");
+ return;
+ }
- int preferredProfiles = 0;
- for (LocalBluetoothProfile profile : mProfiles) {
- if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) {
- if (profile.isPreferred(mDevice)) {
- ++preferredProfiles;
- connectInt(profile);
+ int preferredProfiles = 0;
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (connectAllProfiles ? profile.accessProfileEnabled()
+ : profile.isAutoConnectable()) {
+ if (profile.isPreferred(mDevice)) {
+ ++preferredProfiles;
+ connectInt(profile);
+ }
}
}
- }
- if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
+ if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
- if (preferredProfiles == 0) {
- connectAutoConnectableProfiles();
+ if (preferredProfiles == 0) {
+ connectAutoConnectableProfiles();
+ }
}
}
@@ -244,10 +256,12 @@
return;
}
- for (LocalBluetoothProfile profile : mProfiles) {
- if (profile.isAutoConnectable()) {
- profile.setPreferred(mDevice, true);
- connectInt(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (profile.isAutoConnectable()) {
+ profile.setPreferred(mDevice, true);
+ connectInt(profile);
+ }
}
}
}
@@ -515,14 +529,16 @@
* @return Whether it is connected.
*/
public boolean isConnected() {
- for (LocalBluetoothProfile profile : mProfiles) {
- int status = getProfileConnectionState(profile);
- if (status == BluetoothProfile.STATE_CONNECTED) {
- return true;
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ int status = getProfileConnectionState(profile);
+ if (status == BluetoothProfile.STATE_CONNECTED) {
+ return true;
+ }
}
- }
- return false;
+ return false;
+ }
}
public boolean isConnectedProfile(LocalBluetoothProfile profile) {
@@ -532,14 +548,16 @@
}
public boolean isBusy() {
- for (LocalBluetoothProfile profile : mProfiles) {
- int status = getProfileConnectionState(profile);
- if (status == BluetoothProfile.STATE_CONNECTING
- || status == BluetoothProfile.STATE_DISCONNECTING) {
- return true;
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ int status = getProfileConnectionState(profile);
+ if (status == BluetoothProfile.STATE_CONNECTING
+ || status == BluetoothProfile.STATE_DISCONNECTING) {
+ return true;
+ }
}
+ return getBondState() == BluetoothDevice.BOND_BONDING;
}
- return getBondState() == BluetoothDevice.BOND_BONDING;
}
private boolean updateProfiles() {
@@ -554,8 +572,10 @@
*/
processPhonebookAccess();
- mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
- mLocalNapRoleConnected, mDevice);
+ synchronized (mProfileLock) {
+ mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
+ mLocalNapRoleConnected, mDevice);
+ }
if (BluetoothUtils.D) {
Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
@@ -616,7 +636,9 @@
void onBondingStateChanged(int bondState) {
if (bondState == BluetoothDevice.BOND_NONE) {
- mProfiles.clear();
+ synchronized (mProfileLock) {
+ mProfiles.clear();
+ }
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
@@ -646,9 +668,11 @@
public List<LocalBluetoothProfile> getConnectableProfiles() {
List<LocalBluetoothProfile> connectableProfiles =
new ArrayList<LocalBluetoothProfile>();
- for (LocalBluetoothProfile profile : mProfiles) {
- if (profile.accessProfileEnabled()) {
- connectableProfiles.add(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ if (profile.accessProfileEnabled()) {
+ connectableProfiles.add(profile);
+ }
}
}
return connectableProfiles;
@@ -823,10 +847,12 @@
public int getMaxConnectionState() {
int maxState = BluetoothProfile.STATE_DISCONNECTED;
- for (LocalBluetoothProfile profile : getProfiles()) {
- int connectionStatus = getProfileConnectionState(profile);
- if (connectionStatus > maxState) {
- maxState = connectionStatus;
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : getProfiles()) {
+ int connectionStatus = getProfileConnectionState(profile);
+ if (connectionStatus > maxState) {
+ maxState = connectionStatus;
+ }
}
}
return maxState;
@@ -843,32 +869,34 @@
boolean hfpConnected = true; // HFP is connected
boolean hearingAidConnected = true; // Hearing Aid is connected
- for (LocalBluetoothProfile profile : getProfiles()) {
- int connectionStatus = getProfileConnectionState(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : getProfiles()) {
+ int connectionStatus = getProfileConnectionState(profile);
- switch (connectionStatus) {
- case BluetoothProfile.STATE_CONNECTING:
- case BluetoothProfile.STATE_DISCONNECTING:
- return mContext.getString(
- BluetoothUtils.getConnectionStateSummary(connectionStatus));
+ switch (connectionStatus) {
+ case BluetoothProfile.STATE_CONNECTING:
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return mContext.getString(
+ BluetoothUtils.getConnectionStateSummary(connectionStatus));
- case BluetoothProfile.STATE_CONNECTED:
- profileConnected = true;
- break;
+ case BluetoothProfile.STATE_CONNECTED:
+ profileConnected = true;
+ break;
- case BluetoothProfile.STATE_DISCONNECTED:
- if (profile.isProfileReady()) {
- if ((profile instanceof A2dpProfile) ||
- (profile instanceof A2dpSinkProfile)) {
- a2dpConnected = false;
- } else if ((profile instanceof HeadsetProfile) ||
- (profile instanceof HfpClientProfile)) {
- hfpConnected = false;
- } else if (profile instanceof HearingAidProfile) {
- hearingAidConnected = false;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ if (profile.isProfileReady()) {
+ if (profile instanceof A2dpProfile
+ || profile instanceof A2dpSinkProfile) {
+ a2dpConnected = false;
+ } else if (profile instanceof HeadsetProfile
+ || profile instanceof HfpClientProfile) {
+ hfpConnected = false;
+ } else if (profile instanceof HearingAidProfile) {
+ hearingAidConnected = false;
+ }
}
- }
- break;
+ break;
+ }
}
}
@@ -924,32 +952,34 @@
boolean hfpNotConnected = false; // HFP is preferred but not connected
boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
- for (LocalBluetoothProfile profile : getProfiles()) {
- int connectionStatus = getProfileConnectionState(profile);
+ synchronized (mProfileLock) {
+ for (LocalBluetoothProfile profile : getProfiles()) {
+ int connectionStatus = getProfileConnectionState(profile);
- switch (connectionStatus) {
- case BluetoothProfile.STATE_CONNECTING:
- case BluetoothProfile.STATE_DISCONNECTING:
- return mContext.getString(
- BluetoothUtils.getConnectionStateSummary(connectionStatus));
+ switch (connectionStatus) {
+ case BluetoothProfile.STATE_CONNECTING:
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return mContext.getString(
+ BluetoothUtils.getConnectionStateSummary(connectionStatus));
- case BluetoothProfile.STATE_CONNECTED:
- profileConnected = true;
- break;
+ case BluetoothProfile.STATE_CONNECTED:
+ profileConnected = true;
+ break;
- case BluetoothProfile.STATE_DISCONNECTED:
- if (profile.isProfileReady()) {
- if ((profile instanceof A2dpProfile) ||
- (profile instanceof A2dpSinkProfile)){
- a2dpNotConnected = true;
- } else if ((profile instanceof HeadsetProfile) ||
- (profile instanceof HfpClientProfile)) {
- hfpNotConnected = true;
- } else if (profile instanceof HearingAidProfile) {
- hearingAidNotConnected = true;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ if (profile.isProfileReady()) {
+ if (profile instanceof A2dpProfile
+ || profile instanceof A2dpSinkProfile) {
+ a2dpNotConnected = true;
+ } else if (profile instanceof HeadsetProfile
+ || profile instanceof HfpClientProfile) {
+ hfpNotConnected = true;
+ } else if (profile instanceof HearingAidProfile) {
+ hearingAidNotConnected = true;
+ }
}
- }
- break;
+ break;
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index f7f6589..3a62838 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -50,7 +50,7 @@
}
public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
- return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
+ return new ArrayList<>(mCachedDevices);
}
public static boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index 92fd868..12b8efb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -53,6 +53,8 @@
"com.android.settings.category.ia.night_display";
public static final String CATEGORY_PRIVACY =
"com.android.settings.category.ia.privacy";
+ public static final String CATEGORY_ENTERPRISE_PRIVACY =
+ "com.android.settings.category.ia.enterprise_privacy";
public static final Map<String, String> KEY_COMPAT_MAP;
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
deleted file mode 100644
index e9c5238..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.settingslib.net;
-
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.SET_FOREGROUND;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-
-import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import androidx.loader.content.AsyncTaskLoader;
-
-import com.android.settingslib.AppItem;
-
-/**
- * Loader for historical chart data for both network and UID details.
- *
- * Deprecated in favor of {@link NetworkCycleChartDataLoader} and
- * {@link NetworkCycleDataForUidLoader}
- *
- * @deprecated
- */
-@Deprecated
-public class ChartDataLoaderCompat extends AsyncTaskLoader<ChartData> {
- private static final String KEY_TEMPLATE = "template";
- private static final String KEY_APP = "app";
- private static final String KEY_FIELDS = "fields";
-
- private final INetworkStatsSession mSession;
- private final Bundle mArgs;
-
- public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
- return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
- }
-
- public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
- final Bundle args = new Bundle();
- args.putParcelable(KEY_TEMPLATE, template);
- args.putParcelable(KEY_APP, app);
- args.putInt(KEY_FIELDS, fields);
- return args;
- }
-
- public ChartDataLoaderCompat(Context context, INetworkStatsSession session, Bundle args) {
- super(context);
- mSession = session;
- mArgs = args;
- }
-
- @Override
- protected void onStartLoading() {
- super.onStartLoading();
- forceLoad();
- }
-
- @Override
- public ChartData loadInBackground() {
- final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
- final AppItem app = mArgs.getParcelable(KEY_APP);
- final int fields = mArgs.getInt(KEY_FIELDS);
-
- try {
- return loadInBackground(template, app, fields);
- } catch (RemoteException e) {
- // since we can't do much without history, and we don't want to
- // leave with half-baked UI, we bail hard.
- throw new RuntimeException("problem reading network stats", e);
- }
- }
-
- private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
- throws RemoteException {
- final ChartData data = new ChartData();
- data.network = mSession.getHistoryForNetwork(template, fields);
-
- if (app != null) {
- // load stats for current uid and template
- final int size = app.uids.size();
- for (int i = 0; i < size; i++) {
- final int uid = app.uids.keyAt(i);
- data.detailDefault = collectHistoryForUid(
- template, uid, SET_DEFAULT, data.detailDefault);
- data.detailForeground = collectHistoryForUid(
- template, uid, SET_FOREGROUND, data.detailForeground);
- }
-
- if (size > 0) {
- data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
- data.detail.recordEntireHistory(data.detailDefault);
- data.detail.recordEntireHistory(data.detailForeground);
- } else {
- data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
- data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
- data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
- }
- }
-
- return data;
- }
-
- @Override
- protected void onStopLoading() {
- super.onStopLoading();
- cancelLoad();
- }
-
- @Override
- protected void onReset() {
- super.onReset();
- cancelLoad();
- }
-
- /**
- * Collect {@link NetworkStatsHistory} for the requested UID, combining with
- * an existing {@link NetworkStatsHistory} if provided.
- */
- private NetworkStatsHistory collectHistoryForUid(
- NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
- throws RemoteException {
- final NetworkStatsHistory history = mSession.getHistoryForUid(
- template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
-
- if (existing != null) {
- existing.recordEntireHistory(history);
- return existing;
- } else {
- return history;
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 183d485..180b77e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -32,30 +32,24 @@
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
-import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Range;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import java.time.ZonedDateTime;
-import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
public class DataUsageController {
private static final String TAG = "DataUsageController";
- @VisibleForTesting
- static final String DATA_USAGE_V2 = "settings_data_usage_v2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
@@ -95,21 +89,6 @@
* mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
}
- @VisibleForTesting
- @Deprecated
- INetworkStatsSession getSession() {
- if (mSession == null) {
- try {
- mSession = mStatsService.openSession();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to open stats session", e);
- } catch (RuntimeException e) {
- Log.w(TAG, "Failed to open stats session", e);
- }
- }
- return mSession;
- }
-
public void setCallback(Callback callback) {
mCallback = callback;
}
@@ -149,13 +128,7 @@
end = now;
start = now - DateUtils.WEEK_IN_MILLIS * 4;
}
- final long totalBytes;
- final long callStart = System.currentTimeMillis();
- if (FeatureFlagUtils.isEnabled(mContext, DATA_USAGE_V2)) {
- totalBytes = getUsageLevel(template, start, end);
- } else {
- totalBytes = getUsageLevel(template, start, end, now);
- }
+ final long totalBytes = getUsageLevel(template, start, end);
if (totalBytes < 0L) {
return warn("no entry data");
}
@@ -185,32 +158,7 @@
* retrieving the data.
*/
public long getHistoricalUsageLevel(NetworkTemplate template) {
- if (FeatureFlagUtils.isEnabled(mContext, DATA_USAGE_V2)) {
- return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */);
- } else {
- final long now = System.currentTimeMillis();
- return getUsageLevel(template, 0L /* start */, now /* end */, now);
- }
- }
-
- @Deprecated
- private long getUsageLevel(NetworkTemplate template, long start, long end, long now) {
- final INetworkStatsSession session = getSession();
- if (session != null) {
- try {
- final NetworkStatsHistory history =
- session.getHistoryForNetwork(template, FIELDS);
- final NetworkStatsHistory.Entry entry = history.getValues(
- start, end, System.currentTimeMillis() /* now */, null /* recycle */);
- if (entry != null) {
- return entry.rxBytes + entry.txBytes;
- }
- Log.w(TAG, "Failed to get data usage, no entry data");
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get data usage, remote call failed");
- }
- }
- return -1L;
+ return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */);
}
private long getUsageLevel(NetworkTemplate template, long start, long end) {
@@ -241,20 +189,6 @@
return null;
}
- @Deprecated
- private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
- return entry == null ? null : new StringBuilder("Entry[")
- .append("bucketDuration=").append(entry.bucketDuration)
- .append(",bucketStart=").append(entry.bucketStart)
- .append(",activeTime=").append(entry.activeTime)
- .append(",rxBytes=").append(entry.rxBytes)
- .append(",rxPackets=").append(entry.rxPackets)
- .append(",txBytes=").append(entry.txBytes)
- .append(",txPackets=").append(entry.txPackets)
- .append(",operations=").append(entry.operations)
- .append(']').toString();
- }
-
private static String statsBucketToString(Bucket bucket) {
return bucket == null ? null : new StringBuilder("Entry[")
.append("bucketDuration=").append(bucket.getEndTimeStamp() - bucket.getStartTimeStamp())
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
deleted file mode 100644
index 82bb011..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.settingslib.net;
-
-import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import androidx.loader.content.AsyncTaskLoader;
-
-/**
- * Deprecated in favor of {@link NetworkStatsDetailLoader}
- *
- * @deprecated
- */
-@Deprecated
-public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> {
- private static final String KEY_TEMPLATE = "template";
- private static final String KEY_START = "start";
- private static final String KEY_END = "end";
-
- private final INetworkStatsSession mSession;
- private final Bundle mArgs;
-
- public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
- final Bundle args = new Bundle();
- args.putParcelable(KEY_TEMPLATE, template);
- args.putLong(KEY_START, start);
- args.putLong(KEY_END, end);
- return args;
- }
-
- public SummaryForAllUidLoaderCompat(Context context, INetworkStatsSession session,
- Bundle args) {
- super(context);
- mSession = session;
- mArgs = args;
- }
-
- @Override
- protected void onStartLoading() {
- super.onStartLoading();
- forceLoad();
- }
-
- @Override
- public NetworkStats loadInBackground() {
- final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
- final long start = mArgs.getLong(KEY_START);
- final long end = mArgs.getLong(KEY_END);
-
- try {
- return mSession.getSummaryForAllUid(template, start, end, false);
- } catch (RemoteException e) {
- return null;
- }
- }
-
- @Override
- protected void onStopLoading() {
- super.onStopLoading();
- cancelLoad();
- }
-
- @Override
- protected void onReset() {
- super.onReset();
- cancelLoad();
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index d15a3ef..cfa067f 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -32,12 +32,13 @@
include $(BUILD_PACKAGE)
-#############################################
-# SettingsLib Robolectric test target. #
-#############################################
+############################################################
+# SettingsLib Robolectric test target. #
+############################################################
include $(CLEAR_VARS)
LOCAL_MODULE := SettingsLibRoboTests
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -53,6 +54,9 @@
LOCAL_MODULE_TAGS := optional
+# Generate test_config.properties
+include external/robolectric-shadows/gen_test_config.mk
+
include $(BUILD_STATIC_JAVA_LIBRARY)
#############################################################
diff --git a/packages/SettingsLib/tests/robotests/config/robolectric.properties b/packages/SettingsLib/tests/robotests/config/robolectric.properties
index 6b5b8e5..fab7251 100644
--- a/packages/SettingsLib/tests/robotests/config/robolectric.properties
+++ b/packages/SettingsLib/tests/robotests/config/robolectric.properties
@@ -1,5 +1 @@
-manifest=frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml
sdk=NEWEST_SDK
-
-shadows=\
- com.android.settingslib.testutils.shadow.ShadowXmlUtils
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
index 9ba9967..3a4e2e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
@@ -30,10 +30,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CustomEditTextPreferenceComaptTest {
@Mock
@@ -70,7 +71,7 @@
}
private static class TestPreference extends CustomEditTextPreferenceCompat {
- public TestPreference(Context context) {
+ private TestPreference(Context context) {
super(context);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
index 9d7f59a..e94a06c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
@@ -30,10 +30,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CustomEditTextPreferenceTest {
@Mock
@@ -70,7 +71,7 @@
}
private static class TestPreference extends CustomEditTextPreference {
- public TestPreference(Context context) {
+ private TestPreference(Context context) {
super(context);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
index 19a916c..4e8af73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
@@ -24,9 +24,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DeviceInfoUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
index 36b70df..4d76331 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
@@ -18,12 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.R;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -36,20 +37,19 @@
import android.provider.Settings;
import android.view.MenuItem;
-import android.R;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Tests for {@link HelpUtils}.
*/
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class HelpUtilsTest {
private static final String TEST_HELP_URL = "intent:#Intent;action=com.android.test;end";
private static final String PACKAGE_NAME_KEY = "package-name-key";
@@ -83,8 +83,6 @@
when(mContext.getResources().getString(R.string.config_feedbackIntentNameKey))
.thenReturn(FEEDBACK_INTENT_NAME_KEY);
when(mActivity.getPackageManager()).thenReturn(mPackageManager);
-
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 88ac8ce..2b5a4e0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -25,8 +25,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
@@ -44,11 +44,12 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.Collections;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RestrictedLockUtilsTest {
@Mock
@@ -178,8 +179,7 @@
public void checkIfKeyguardFeaturesAreDisabled_doesMatchAllowedFeature_unifiedManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -207,8 +207,7 @@
public void checkIfKeyguardFeaturesAreDisabled_notMatchOtherFeatures_unifiedManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -231,8 +230,7 @@
public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesProfile_separateManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -268,8 +266,7 @@
public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesParent_profileParentPolicy() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
- when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
- userInfo, profileInfo}));
+ when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mProxy.getParentProfileInstance(any(DevicePolicyManager.class), any())
.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index 79d682d6..1b10c73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -16,7 +16,6 @@
package com.android.settingslib;
-
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -35,8 +34,9 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RestrictedPreferenceHelperTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
deleted file mode 100644
index 8757eed..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib;
-
-import android.annotation.NonNull;
-
-import org.junit.runners.model.InitializationError;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.manifest.AndroidManifest;
-import org.robolectric.res.Fs;
-import org.robolectric.res.ResourcePath;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner {
-
- public SettingsLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
- super(testClass);
- }
-
- /**
- * We are going to create our own custom manifest so we can add multiple resource paths to it.
- */
- @Override
- protected AndroidManifest getAppManifest(Config config) {
- try {
- // Using the manifest file's relative path, we can figure out the application directory.
- final URL appRoot =
- new URL("file:frameworks/base/packages/SettingsLib/tests/robotests");
- final URL manifestPath = new URL(appRoot, "AndroidManifest.xml");
- final URL resDir = new URL(appRoot, "res");
- final URL assetsDir = new URL(appRoot, "assets");
-
- return new AndroidManifest(Fs.fromURL(manifestPath), Fs.fromURL(resDir),
- Fs.fromURL(assetsDir), "com.android.settingslib") {
- @Override
- public List<ResourcePath> getIncludedResourcePaths() {
- final List<ResourcePath> paths = super.getIncludedResourcePaths();
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/"
- + "SettingsLayoutPreference/res"));
- paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
- paths.add(resourcePath("file:frameworks/base/core/res/res"));
- paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
- return paths;
- }
- };
- } catch (MalformedURLException e) {
- throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e);
- }
- }
-
- private static ResourcePath resourcePath(@NonNull String spec) {
- try {
- return new ResourcePath(null, Fs.fromURL(new URL(spec)), null);
- } catch (MalformedURLException e) {
- throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e);
- }
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
index e70baa1..0ca7791 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
@@ -32,12 +32,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TetherUtilTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
index c0b69f2..3f0ba13 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
@@ -36,9 +36,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TwoTargetPreferenceTest {
private PreferenceViewHolder mViewHolder;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 08a75ab..594d767 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -49,6 +49,7 @@
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -58,10 +59,8 @@
import java.util.HashMap;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = {
- UtilsTest.ShadowSecure.class,
- UtilsTest.ShadowLocationManager.class})
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {UtilsTest.ShadowSecure.class, UtilsTest.ShadowLocationManager.class})
public class UtilsTest {
private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
private static final String PERCENTAGE_0 = "0%";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
index 152d024..44fdaec 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
@@ -23,14 +23,13 @@
import android.os.UserHandle;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AccessibilityUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index b307b47..ccec175a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -41,7 +41,6 @@
import android.os.UserHandle;
import android.util.IconDrawableFactory;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
import com.android.settingslib.applications.ApplicationsState.Session;
@@ -55,6 +54,7 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -67,7 +67,7 @@
import java.util.List;
import java.util.UUID;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class,
ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
ApplicationsStateRoboTest.ShadowPackageManager.class})
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
index a92a2dd..50fad70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
@@ -18,8 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -32,16 +32,15 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DefaultAppInfoTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
index d8c459c..f7fd25b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
@@ -26,14 +26,13 @@
import android.content.ComponentName;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ServiceListingTest {
private static final String TEST_SETTING = "testSetting";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 29831a8..c555cbe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -17,8 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -32,7 +32,6 @@
import android.content.res.Resources;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -40,26 +39,27 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class A2dpProfileTest {
@Mock
- Context mContext;
+ private Context mContext;
@Mock
- CachedBluetoothDeviceManager mDeviceManager;
+ private CachedBluetoothDeviceManager mDeviceManager;
@Mock
- LocalBluetoothProfileManager mProfileManager;
+ private LocalBluetoothProfileManager mProfileManager;
@Mock
- BluetoothDevice mDevice;
+ private BluetoothDevice mDevice;
@Mock
- BluetoothA2dp mBluetoothA2dp;
- BluetoothProfile.ServiceListener mServiceListener;
+ private BluetoothA2dp mBluetoothA2dp;
+ private BluetoothProfile.ServiceListener mServiceListener;
- A2dpProfile mProfile;
+ private A2dpProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
index 274fff8..976445e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class A2dpSinkProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothA2dpSink mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private A2dpSinkProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index c147d5e..27b8dfc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -29,20 +29,18 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.Handler;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothEventManagerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 07310bd..0eb6de9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -21,14 +21,14 @@
import android.graphics.drawable.Drawable;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothUtilsTest {
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 9c75491..47b1210 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -28,18 +28,17 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Collection;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceManagerTest {
private final static String DEVICE_NAME_1 = "TestName_1";
private final static String DEVICE_NAME_2 = "TestName_2";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5ceede1..4e5d38a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -31,16 +31,15 @@
import android.content.Context;
import android.media.AudioManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceTest {
private final static String DEVICE_NAME = "TestName";
private final static String DEVICE_ALIAS = "TestAlias";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
index c0a1f0c..9adef82 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -11,7 +11,6 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -19,11 +18,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HeadsetProfileTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index cb1b12d..2b5466c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -29,16 +29,15 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class HearingAidDeviceManagerTest {
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
index 187be0b..69c020d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HfpClientProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothHeadsetClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HfpClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
index c91ee22..f38af70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HidDeviceProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothHidDevice mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HidDeviceProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index a3c3a54..5d5872e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -37,7 +37,6 @@
import android.content.Intent;
import android.os.ParcelUuid;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -45,6 +44,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@@ -52,7 +52,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
private final static long HISYNCID = 10;
@@ -270,13 +270,13 @@
verify(mCachedBluetoothDevice).refresh();
}
- private List<Integer> generateList(int[] profile) {
- if (profile == null) {
+ private List<Integer> generateList(int[] profiles) {
+ if (profiles == null) {
return null;
}
- final List<Integer> profileList = new ArrayList<>(profile.length);
- for(int i = 0; i < profile.length; i++) {
- profileList.add(profile[i]);
+ final List<Integer> profileList = new ArrayList<>(profiles.length);
+ for (int profile : profiles) {
+ profileList.add(profile);
}
return profileList;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
index c4c48a8..6f66709 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class MapClientProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothMapClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private MapClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
index e4a444c..b21ec9c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothPbapClient;
import android.bluetooth.BluetoothProfile;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,12 +33,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothAdapter.class})
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowBluetoothAdapter.class)
public class PbapClientProfileTest {
@Mock
@@ -52,8 +49,6 @@
@Mock
private BluetoothPbapClient mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private PbapClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
index 9bb53ee..ec88034 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -18,18 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothSap;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
@@ -37,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class SapProfileTest {
@@ -52,8 +49,6 @@
@Mock
private BluetoothSap mService;
@Mock
- private CachedBluetoothDevice mCachedBluetoothDevice;
- @Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private SapProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 4d7553c..28de191 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -24,16 +24,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AbstractPreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index 4ec6fb2..8a0ae91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -27,7 +27,6 @@
import android.content.Intent;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -35,13 +34,14 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class MetricsFeatureProviderTest {
@Mock
private LogWriter mLogWriter;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
index 6285fcd..8f51dec 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
@@ -17,8 +17,8 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -26,16 +26,15 @@
import android.content.Context;
import android.content.SharedPreferences;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SharedPreferenceLoggerTest {
private static final String TEST_TAG = "tag";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index b251c09..097db17 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -17,10 +17,10 @@
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -35,7 +35,6 @@
import androidx.fragment.app.FragmentActivity;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -43,10 +42,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class VisibilityLoggerMixinTest {
@Mock
@@ -139,7 +138,7 @@
private final class TestInstrumentable implements Instrumentable {
- public static final int TEST_METRIC = 12345;
+ private static final int TEST_METRIC = 12345;
@Override
public int getMetricsCategory() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index 887c1d5..29e37e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -28,7 +28,6 @@
import androidx.lifecycle.LifecycleOwner;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.events.OnAttach;
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
@@ -43,10 +42,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadows.androidx.fragment.FragmentController;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LifecycleTest {
private LifecycleOwner mLifecycleOwner;
@@ -56,7 +56,7 @@
final TestObserver mFragObserver;
- public TestDialogFragment() {
+ private TestDialogFragment() {
mFragObserver = new TestObserver();
mLifecycle.addObserver(mFragObserver);
}
@@ -236,11 +236,11 @@
}
private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected {
- public boolean wasCalled = false;
+ private boolean mWasCalled = false;
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
- wasCalled = true;
+ mWasCalled = true;
return false;
}
}
@@ -258,14 +258,14 @@
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
- assertThat(accepter.wasCalled).isFalse();
+ assertThat(accepter.mWasCalled).isFalse();
}
private class OnStartObserver implements LifecycleObserver, OnStart {
private final Lifecycle mLifecycle;
- public OnStartObserver(Lifecycle lifecycle) {
+ private OnStartObserver(Lifecycle lifecycle) {
mLifecycle = lifecycle;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
index 9dd93b3a..6191a00 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
@@ -22,16 +22,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DeveloperOptionsPreferenceControllerTest {
private static final String TEST_KEY = "Test_pref_key";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index a0fa6b5..3475ff7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -18,23 +18,19 @@
import static com.google.common.truth.Truth.assertThat;
-import android.os.UserManager;
import android.content.Context;
+import android.os.UserManager;
import android.provider.Settings;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.robolectric.shadows.ShadowUserManager;
-import org.robolectric.shadow.api.Shadow;
-
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowUserManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DevelopmentSettingsEnablerTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index d7b23b0..e84a25c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -32,17 +32,16 @@
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class EnableAdbPreferenceControllerTest {
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
@@ -150,7 +149,7 @@
}
class ConcreteEnableAdbPreferenceController extends AbstractEnableAdbPreferenceController {
- public ConcreteEnableAdbPreferenceController(Context context) {
+ private ConcreteEnableAdbPreferenceController(Context context) {
super(context);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
index 2f78899..146be23 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
@@ -45,16 +45,16 @@
import androidx.preference.PreferenceScreen;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LogdSizePreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
index ed128e0..d5afb4b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
@@ -29,7 +29,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,9 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LogpersistPreferenceControllerTest {
private LifecycleOwner mLifecycleOwner;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
index 40db478..d1212fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
@@ -27,16 +27,15 @@
import android.os.IBinder;
import android.os.Parcel;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SystemPropPokerTest {
@Spy
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
index 234b4d5..16de5f8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
@@ -26,7 +26,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -34,11 +33,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothAddressPreferenceControllerTest {
@Mock
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
index aee956c..4444e63 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -30,7 +30,6 @@
import android.content.IntentFilter;
import android.os.Handler;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -39,8 +38,9 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ConnectivityPreferenceControllerTest {
@Mock
private Context mContext;
@@ -91,8 +91,7 @@
private static class ConcreteConnectivityPreferenceController
extends AbstractConnectivityPreferenceController {
-
- public ConcreteConnectivityPreferenceController(Context context,
+ private ConcreteConnectivityPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
index 2b490ee..bd223bd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
@@ -25,12 +25,10 @@
import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -38,11 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowSubscriptionManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ImsStatusPreferenceControllerTest {
@Mock
private Context mContext;
@@ -61,8 +58,9 @@
}
@Test
- @Config(shadows = ShadowSubscriptionManager.class)
public void testIsAvailable() {
+ ShadowSubscriptionManager.setDefaultDataSubscriptionId(1234);
+
CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class);
doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class);
@@ -92,18 +90,10 @@
.that(imsStatusPreferenceController.isAvailable()).isFalse();
}
- @Implements(SubscriptionManager.class)
- public static class ShadowSubscriptionManager {
- @Implementation
- public static int getDefaultDataSubscriptionId() {
- return 1234;
- }
- }
-
private static class ConcreteImsStatusPreferenceController
extends AbstractImsStatusPreferenceController {
- public ConcreteImsStatusPreferenceController(Context context,
+ private ConcreteImsStatusPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
index 1d957c3..76a26d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -27,7 +27,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -35,11 +34,12 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class IpAddressPreferenceControllerTest {
@Mock
private Context mContext;
@@ -75,8 +75,7 @@
private static class ConcreteIpAddressPreferenceController extends
AbstractIpAddressPreferenceController {
- public ConcreteIpAddressPreferenceController(Context context,
- Lifecycle lifecycle) {
+ private ConcreteIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
super(context, lifecycle);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
index dc77400..5b71bdd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
@@ -25,16 +25,15 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class SerialNumberPreferenceControllerTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
index eb77cb6..5252c6c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
@@ -24,17 +24,16 @@
import android.os.UserManager;
import android.util.SparseBooleanArray;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class,
SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class})
public class SimStatusImeiInfoPreferenceControllerTest {
@@ -106,7 +105,7 @@
private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
- public void setNetworkSupported(int networkType, boolean supported) {
+ private void setNetworkSupported(int networkType, boolean supported) {
mSupportedNetworkTypes.put(networkType, supported);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
index 2e0348d..f09879b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
@@ -28,7 +28,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -36,9 +35,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLooper;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class UptimePreferenceControllerTest {
@Mock
private Context mContext;
@@ -92,7 +92,7 @@
private static class ConcreteUptimePreferenceController
extends AbstractUptimePreferenceController {
- public ConcreteUptimePreferenceController(Context context,
+ private ConcreteUptimePreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 359ea77..74e5bf5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -33,7 +33,6 @@
import androidx.preference.PreferenceScreen;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -41,13 +40,14 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
import java.util.List;
@SuppressLint("HardwareIds")
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class WifiMacAddressPreferenceControllerTest {
@Mock
private Lifecycle mLifecycle;
@@ -197,7 +197,7 @@
private static class ConcreteWifiMacAddressPreferenceController
extends AbstractWifiMacAddressPreferenceController {
- public ConcreteWifiMacAddressPreferenceController(Context context,
+ private ConcreteWifiMacAddressPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
index ca621ca..c0924d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
@@ -20,12 +20,11 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BrightnessUtilsTest {
private static final int MIN = 1;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
index 59a3dd6..605c861 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
@@ -20,14 +20,13 @@
import android.util.ArraySet;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Set;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class CategoryKeyTest {
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index 40e7386..b77670b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -13,15 +13,14 @@
import android.os.Bundle;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TileTest {
private ActivityInfo mActivityInfo;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 362ae4c..bbb4249 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -54,7 +54,6 @@
import android.util.Pair;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -62,12 +61,13 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TileUtilsTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
index d0b6dab..2988905 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
@@ -33,28 +33,26 @@
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BatterySaverUtilsTest {
- final int BATTERY_SAVER_THRESHOLD_1 = 15;
- final int BATTERY_SAVER_THRESHOLD_2 = 20;
+ private static final int BATTERY_SAVER_THRESHOLD_1 = 15;
+ private static final int BATTERY_SAVER_THRESHOLD_2 = 20;
@Mock
- Context mMockContext;
+ private Context mMockContext;
@Mock
- ContentResolver mMockResolver;
+ private ContentResolver mMockResolver;
@Mock
- PowerManager mMockPowerManager;
+ private PowerManager mMockPowerManager;
@Before
public void setUp() throws Exception {
@@ -66,11 +64,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_firstCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_firstCall_needWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(false, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isFalse();
verify(mMockContext, times(1)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(0)).setPowerSaveMode(anyBoolean());
@@ -83,11 +81,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_secondCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_secondCall_needWarning() {
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -97,11 +95,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_thridCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_thridCall_needWarning() {
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
Secure.putInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 1);
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -111,11 +109,11 @@
}
@Test
- public void testSetPowerSaveMode_enable_firstCall_noWarning() throws Exception {
+ public void testSetPowerSaveMode_enable_firstCall_noWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, false));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, false)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -125,12 +123,12 @@
}
@Test
- public void testSetPowerSaveMode_disable_firstCall_noWarning() throws Exception {
+ public void testSetPowerSaveMode_disable_firstCall_noWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
// When disabling, needFirstTimeWarning doesn't matter.
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, false));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, false)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
@@ -141,12 +139,12 @@
}
@Test
- public void testSetPowerSaveMode_disable_firstCall_needWarning() throws Exception {
+ public void testSetPowerSaveMode_disable_firstCall_needWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
// When disabling, needFirstTimeWarning doesn't matter.
- assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, true));
+ assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, true)).isTrue();
verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
@@ -157,7 +155,7 @@
}
@Test
- public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() throws Exception {
+ public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() {
Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_1);
@@ -172,7 +170,7 @@
}
@Test
- public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() throws Exception {
+ public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() {
Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null");
Secure.putString(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, "null");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
index 9b1fe5f..bbf807d2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
@@ -31,7 +31,6 @@
import android.content.pm.PackageManager;
import android.os.IDeviceIdleController;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowDefaultDialerManager;
import com.android.settingslib.testutils.shadow.ShadowSmsApplication;
@@ -40,12 +39,13 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class})
public class PowerWhitelistBackendTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index 49dde0e..35743c2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -16,8 +16,8 @@
package com.android.settingslib.graph;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -25,17 +25,16 @@
import android.graphics.Canvas;
import android.graphics.Paint;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BatteryMeterDrawableBaseTest {
private static final int CRITICAL_LEVEL = 5;
private static final int PADDING = 5;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
index 5dbb5ca..1b350cc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
@@ -22,14 +22,14 @@
import android.graphics.drawable.VectorDrawable;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class BluetoothDeviceLayerDrawableTest {
private static final int RES_ID = R.drawable.ic_bt_cellphone;
private static final int BATTERY_LEVEL = 15;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
index fa64afe..b930aa6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -25,26 +25,21 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class InputMethodAndSubtypeUtilCompatTest {
private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
private static HashSet<String> asHashSet(String... strings) {
- HashSet<String> hashSet = new HashSet<>();
- for (String s : strings) {
- hashSet.add(s);
- }
- return hashSet;
+ return new HashSet<>(Arrays.asList(strings));
}
@Test
@@ -105,7 +100,6 @@
"ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype1", "subtype2"));
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 03ab261..84606b4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -25,26 +25,21 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class InputMethodAndSubtypeUtilTest {
private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
private static HashSet<String> asHashSet(String... strings) {
- HashSet<String> hashSet = new HashSet<>();
- for (String s : strings) {
- hashSet.add(s);
- }
- return hashSet;
+ return new HashSet<>(Arrays.asList(strings));
}
@Test
@@ -103,7 +98,6 @@
"ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype1", "subtype2"));
-
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index b00476b2..4b5e909 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -18,10 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
@@ -32,7 +31,7 @@
import java.util.HashMap;
import java.util.Map;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LicenseHtmlGeneratorFromXmlTest {
private static final String VALILD_XML_STRING =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
@@ -92,8 +91,8 @@
@Test
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
@@ -107,8 +106,8 @@
@Test(expected = XmlPullParserException.class)
public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(INVALILD_XML_STRING.getBytes())),
@@ -117,8 +116,8 @@
@Test
public void testGenerateHtml() {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
fileNameToContentIdMap.put("/file0", "0");
fileNameToContentIdMap.put("/file1", "0");
@@ -132,8 +131,8 @@
@Test
public void testGenerateHtmlWithCustomHeading() {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
- Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+ Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<>();
fileNameToContentIdMap.put("/file0", "0");
fileNameToContentIdMap.put("/file1", "0");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
index c90de5f..e82bc06 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -20,14 +20,13 @@
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -37,7 +36,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = LicenseHtmlLoaderCompatTest.ShadowLicenseHtmlLoaderCompat.class)
public class LicenseHtmlLoaderCompatTest {
@@ -58,7 +57,7 @@
@Test
public void testLoadInBackground() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -69,7 +68,7 @@
@Test
public void testLoadInBackgroundWithNoVaildXmlFiles() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
File cachedHtmlFile = new File("test.html");
setupFakeData(xmlFiles, cachedHtmlFile, true, true);
@@ -79,7 +78,7 @@
@Test
public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -90,7 +89,7 @@
@Test
public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
- ArrayList<File> xmlFiles = new ArrayList();
+ ArrayList<File> xmlFiles = new ArrayList<>();
xmlFiles.add(new File("test.xml"));
File cachedHtmlFile = new File("test.html");
@@ -112,10 +111,10 @@
@Implements(LicenseHtmlLoaderCompat.class)
public static class ShadowLicenseHtmlLoaderCompat {
- public static List<File> sValidXmlFiles;
- public static File sCachedHtmlFile;
- public static boolean sIsCachedHtmlFileOutdated;
- public static boolean sGenerateHtmlFileSucceeded;
+ private static List<File> sValidXmlFiles;
+ private static File sCachedHtmlFile;
+ private static boolean sIsCachedHtmlFileOutdated;
+ private static boolean sGenerateHtmlFileSucceeded;
@Resetter
public static void reset() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
index c29481f..8c2e899 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
@@ -18,12 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public final class InjectedSettingTest {
private static final String TEST_STRING = "test";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 9c168f7..08d5367 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -2,7 +2,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.isA;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -17,20 +17,19 @@
import android.os.UserHandle;
import android.os.UserManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class RecentLocationAppsTest {
private static final int TEST_UID = 1234;
@@ -56,8 +55,6 @@
private int mTestUserId;
private RecentLocationApps mRecentLocationApps;
-
-
@Before
public void setUp() throws NameNotFoundException {
MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index 50044f2..acf99a2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
@@ -35,23 +34,19 @@
import android.net.ConnectivityManager;
import android.net.INetworkStatsSession;
import android.net.NetworkStatsHistory;
-import android.net.NetworkStatsHistory.Entry;
import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
-import android.util.FeatureFlagUtils;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class DataUsageControllerTest {
private static final String SUB_ID = "Test Subscriber";
@@ -62,16 +57,18 @@
private TelephonyManager mTelephonyManager;
@Mock
private NetworkStatsManager mNetworkStatsManager;
-
+ @Mock
private Context mContext;
+
private DataUsageController mController;
private NetworkStatsHistory mNetworkStatsHistory;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mController = spy(new DataUsageController(mContext));
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+ mController = new DataUsageController(mContext);
mNetworkStatsHistory = spy(
new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
doReturn(mNetworkStatsHistory)
@@ -80,78 +77,25 @@
}
@Test
- public void getHistoricalUsageLevel_v1_noNetworkSession_shouldReturnNegative1() {
- FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
- doReturn(null).when(mController).getSession();
+ public void getHistoricalUsageLevel_shouldQuerySummaryForDevice() throws Exception {
- assertThat(mController.getHistoricalUsageLevel(null /* template */)).isEqualTo(-1L);
-
- }
-
- @Test
- public void getHistoriclUsageLevel_v1_noUsageData_shouldReturn0() {
- FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
- doReturn(mSession).when(mController).getSession();
-
- assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
- .isEqualTo(0L);
-
- }
-
- @Test
- public void getHistoricalUsageLevel_v1_hasUsageData_shouldReturnTotalUsage() {
- FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
- doReturn(mSession).when(mController).getSession();
- final long receivedBytes = 743823454L;
- final long transmittedBytes = 16574289L;
- final Entry entry = new Entry();
- entry.bucketStart = 1521583200000L;
- entry.rxBytes = receivedBytes;
- entry.txBytes = transmittedBytes;
- when(mNetworkStatsHistory.getValues(eq(0L), anyLong(), anyLong(), nullable(Entry.class)))
- .thenReturn(entry);
-
- assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
- .isEqualTo(receivedBytes + transmittedBytes);
-
- }
-
- @Test
- public void getHistoricalUsageLevel_v2_shouldQuerySummaryForDevice() throws Exception {
- final Context context = mock(Context.class);
- FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
- when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
- when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
- final DataUsageController controller = new DataUsageController(context);
-
- controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
+ mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
verify(mNetworkStatsManager).querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */);
}
@Test
- public void getHistoricalUsageLevel_v2NoUsageData_shouldReturn0() throws Exception {
- final Context context = mock(Context.class);
- FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
- when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
- when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+ public void getHistoricalUsageLevel_noUsageData_shouldReturn0() throws Exception {
when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */))
.thenReturn(mock(NetworkStats.Bucket.class));
- final DataUsageController controller = new DataUsageController(context);
-
- assertThat(controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
.isEqualTo(0L);
}
@Test
- public void getHistoricalUsageLevel_v2HasUsageData_shouldReturnTotalUsage()
- throws Exception {
- final Context context = mock(Context.class);
- FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
- when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
- when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+ public void getHistoricalUsageLevel_hasUsageData_shouldReturnTotalUsage() throws Exception {
final long receivedBytes = 743823454L;
final long transmittedBytes = 16574289L;
final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
@@ -159,9 +103,8 @@
when(bucket.getTxBytes()).thenReturn(transmittedBytes);
when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */)).thenReturn(bucket);
- final DataUsageController controller = new DataUsageController(context);
- assertThat(controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
.isEqualTo(receivedBytes + transmittedBytes);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
index 0a03631..011f234 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -27,15 +27,14 @@
import android.os.RemoteException;
import android.text.format.DateUtils;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleChartDataLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index 2314f27..d915963 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -19,7 +19,7 @@
import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -33,15 +33,14 @@
import android.net.NetworkPolicyManager;
import android.text.format.DateUtils;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataForUidLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 9d60a97..2d8ea12 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -16,8 +16,8 @@
package com.android.settingslib.net;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.nullable;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -37,20 +37,19 @@
import android.text.format.DateUtils;
import android.util.Range;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.util.ReflectionHelpers;
import java.time.ZonedDateTime;
import java.util.Iterator;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataLoaderTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
index 89c319a..59d5674 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
@@ -16,11 +16,10 @@
package com.android.settingslib.notification;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -38,16 +37,15 @@
import android.service.notification.Condition;
import android.view.LayoutInflater;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class EnableZenModeDialogTest {
private EnableZenModeDialog mController;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
index 8147656..437c0d4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
@@ -25,27 +25,23 @@
import android.content.ContentResolver;
import android.content.Context;
import android.provider.Settings;
-import android.service.notification.Condition;
import android.view.LayoutInflater;
import android.view.View;
import androidx.appcompat.app.AlertDialog;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ZenDurationDialogTest {
private ZenDurationDialog mController;
private Context mContext;
private LayoutInflater mLayoutInflater;
- private Condition mCountdownCondition;
- private Condition mAlarmCondition;
private ContentResolver mContentResolver;
private AlertDialog.Builder mBuilder;
@@ -102,7 +98,6 @@
ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
}
-
@Test
public void testChooseAlwaysPromptSetting() {
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
index 449451a..ffaa7443 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
@@ -31,7 +31,6 @@
import androidx.lifecycle.LifecycleOwner;
import androidx.loader.app.LoaderManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
@@ -40,10 +39,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowSuggestionController.class)
public class SuggestionControllerMixinCompatTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
index aac582f..4dc80f4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
@@ -31,7 +31,6 @@
import androidx.lifecycle.LifecycleOwner;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
@@ -40,10 +39,11 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowSuggestionController.class)
public class SuggestionControllerMixinTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
index f4afdb1..3e91641 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
@@ -26,19 +26,19 @@
@Implements(DefaultDialerManager.class)
public class ShadowDefaultDialerManager {
- private static String sDefaultDailer;
+ private static String sDefaultDialer;
@Resetter
public void reset() {
- sDefaultDailer = null;
+ sDefaultDialer = null;
}
@Implementation
public static String getDefaultDialerApplication(Context context) {
- return sDefaultDailer;
+ return sDefaultDialer;
}
public static void setDefaultDialerApplication(String dialer) {
- sDefaultDailer = dialer;
+ sDefaultDialer = dialer;
}
}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
index 4705cd2..9a169d2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
@@ -27,7 +27,6 @@
import android.os.UserHandle;
import android.os.UserManager;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.testutils.shadow.ShadowActivityManager;
import org.junit.After;
@@ -36,6 +35,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
@@ -45,7 +45,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
@Config(shadows = { ShadowActivityManager.class, UserManagerHelperRoboTest.ShadowUserHandle.class})
public class UserManagerHelperRoboTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
index 645dfa1..026ad47 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
@@ -30,14 +30,13 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class IconCacheTest {
private Icon mIcon;
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 6a9579b..7ef31df 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -22,31 +22,30 @@
import android.content.Context;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.time.Duration;
import java.util.regex.Pattern;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class PowerUtilTest {
- public static final String TEST_BATTERY_LEVEL_10 = "10%";
- public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
- public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
- public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
- public static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
- public static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
- public static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
- public static final String ENHANCED_SUFFIX = " based on your usage";
+ private static final String TEST_BATTERY_LEVEL_10 = "10%";
+ private static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
+ private static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
+ private static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
+ private static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
+ private static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
+ private static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
+ private static final String ENHANCED_SUFFIX = " based on your usage";
// matches a time (ex: '1:15 PM', '2 AM', '23:00')
- public static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
+ private static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
// matches a percentage with parenthesis (ex: '(10%)')
- public static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)";
+ private static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)";
private Context mContext;
@@ -108,7 +107,6 @@
+ "(" + PERCENTAGE_REGEX + "){0}")); // no percentage
}
-
@Test
public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index e4bbbcb..8fbbfbb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -25,14 +25,13 @@
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class StringUtilTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 1e066b1..26db124 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -15,28 +15,22 @@
*/
package com.android.settingslib.utils;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLooper;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class ThreadUtilsTest {
@Test
public void testMainThread() throws InterruptedException {
assertThat(ThreadUtils.isMainThread()).isTrue();
- Thread background = new Thread(new Runnable() {
- public void run() {
- assertThat(ThreadUtils.isMainThread()).isFalse();
- }
- });
+ Thread background = new Thread(() -> assertThat(ThreadUtils.isMainThread()).isFalse());
background.start();
background.join();
}
@@ -44,13 +38,11 @@
@Test
public void testEnsureMainThread() throws InterruptedException {
ThreadUtils.ensureMainThread();
- Thread background = new Thread(new Runnable() {
- public void run() {
- try {
- ThreadUtils.ensureMainThread();
- fail("Should not pass ensureMainThread in a background thread");
- } catch (RuntimeException e) {
- }
+ Thread background = new Thread(() -> {
+ try {
+ ThreadUtils.ensureMainThread();
+ fail("Should not pass ensureMainThread in a background thread");
+ } catch (RuntimeException expected) {
}
});
background.start();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
new file mode 100644
index 0000000..88fef08
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ActionButtonsPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private ActionButtonsPreference mPref;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.settings_action_buttons, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPref = new ActionButtonsPreference(mContext);
+ }
+
+ @Test
+ public void onBindViewHolder_setTitle_shouldShowButtonByDefault() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setIcon_shouldShowButtonByDefault() {
+ mPref.setButton1Icon(R.drawable.ic_plus);
+ mPref.setButton2Icon(R.drawable.ic_plus);
+ mPref.setButton3Icon(R.drawable.ic_plus);
+ mPref.setButton4Icon(R.drawable.ic_plus);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_notSetTitleOrIcon_shouldNotShowButtonByDefault() {
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibleIsGoneAndSetTitle_shouldNotShowButton() {
+ mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
+ mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
+ mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
+ mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibleIsGoneAndSetIcon_shouldNotShowButton() {
+ mPref.setButton1Icon(R.drawable.ic_plus).setButton1Visible(false);
+ mPref.setButton2Icon(R.drawable.ic_plus).setButton2Visible(false);
+ mPref.setButton3Icon(R.drawable.ic_plus).setButton3Visible(false);
+ mPref.setButton4Icon(R.drawable.ic_plus).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_setVisibility_shouldUpdateButtonVisibility() {
+ mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
+ mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
+ mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
+ mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.GONE);
+
+ mPref.setButton1Visible(true);
+ mPref.setButton2Visible(true);
+ mPref.setButton3Visible(true);
+ mPref.setButton4Visible(true);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_setEnabled_shouldEnableButton() {
+ mPref.setButton1Enabled(true);
+ mPref.setButton2Enabled(false);
+ mPref.setButton3Enabled(true);
+ mPref.setButton4Enabled(false);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.button1).isEnabled()).isTrue();
+ assertThat(mRootView.findViewById(R.id.button2).isEnabled()).isFalse();
+ assertThat(mRootView.findViewById(R.id.button3).isEnabled()).isTrue();
+ assertThat(mRootView.findViewById(R.id.button4).isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_setText_shouldShowSameText() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton2Text(R.string.install_other_apps);
+ mPref.setButton3Text(R.string.install_other_apps);
+ mPref.setButton4Text(R.string.install_other_apps);
+
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(((Button) mRootView.findViewById(R.id.button1)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button2)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button3)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ assertThat(((Button) mRootView.findViewById(R.id.button4)).getText())
+ .isEqualTo(mContext.getText(R.string.install_other_apps));
+ }
+
+ @Test
+ public void onBindViewHolder_setButtonIcon_iconMustDisplayAboveText() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(R.drawable.ic_plus);
+
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNotNull();
+ }
+
+ @Test
+ public void setButtonIcon_iconResourceIdIsZero_shouldNotDisplayIcon() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(0);
+
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNull();
+ }
+
+ @Test
+ public void setButtonIcon_iconResourceIdNotExisting_shouldNotDisplayIconAndCrash() {
+ mPref.setButton1Text(R.string.install_other_apps);
+ mPref.setButton1Icon(999999999 /* not existing id */);
+ // Should not crash here
+ mPref.onBindViewHolder(mHolder);
+ final Drawable[] drawablesAroundText =
+ ((Button) mRootView.findViewById(R.id.button1))
+ .getCompoundDrawables();
+
+ assertThat(drawablesAroundText[1 /* top */]).isNull();
+ }
+
+ public static ActionButtonsPreference createMock() {
+ final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
+ when(pref.setButton1Text(anyInt())).thenReturn(pref);
+ when(pref.setButton1Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton2Text(anyInt())).thenReturn(pref);
+ when(pref.setButton2Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton3Text(anyInt())).thenReturn(pref);
+ when(pref.setButton3Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton4Text(anyInt())).thenReturn(pref);
+ when(pref.setButton4Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton4Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+ return pref;
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
index a00f12d..d41d511 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -22,14 +22,13 @@
import android.graphics.drawable.AnimatedRotateDrawable;
import android.view.View;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AnimatedImageViewTest {
private AnimatedImageView mAnimatedImageView;
@@ -47,5 +46,4 @@
AnimatedRotateDrawable drawable = (AnimatedRotateDrawable) mAnimatedImageView.getDrawable();
assertThat(drawable.isRunning()).isTrue();
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
index e030005..601da051 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,7 +29,6 @@
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,9 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceMixinCompatTest {
@Mock
@@ -97,5 +97,4 @@
verify(mScreen).removePreference(any(FooterPreference.class));
verify(mScreen, times(2)).addPreference(any(FooterPreference.class));
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 8817ff7..7ae5d2d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,7 +29,6 @@
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -37,10 +36,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowApplication;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceMixinTest {
@Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index e0eceb4..0d2399e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -26,14 +26,14 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class FooterPreferenceTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
index 427a611..99261a3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
@@ -27,14 +27,13 @@
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class LayoutPreferenceTest {
private LayoutPreference mPreference;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
index 10c9dfb..da97cc8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
@@ -23,14 +23,13 @@
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AppPreferenceTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 86443bd..c5cbea7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -25,16 +25,15 @@
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class AccessPointPreferenceTest {
private Context mContext = RuntimeEnvironment.application;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
index f0e8c66..b059df1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
@@ -22,15 +22,14 @@
import android.net.WifiKey;
import android.os.Parcel;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import java.util.Date;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class TimestampedScoredNetworkTest {
private TimestampedScoredNetwork impl;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 07c50fd..89960cb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -37,19 +37,19 @@
import android.util.ArraySet;
import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Set;
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
public class WifiUtilsTest {
private static final String TEST_SSID = "\"test_ssid\"";
private static final String TEST_BSSID = "00:00:00:00:00:00";
@@ -79,7 +79,7 @@
Bundle bundle = new Bundle();
ArrayList<ScanResult> scanResults = buildScanResultCache();
bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS,
- scanResults.toArray(new Parcelable[scanResults.size()]));
+ scanResults.toArray(new Parcelable[0]));
AccessPoint ap = new AccessPoint(mContext, bundle);
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index cbb6e82..df5b146 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -855,6 +855,10 @@
GlobalSettingsProto.MultiSim.SMS_PROMPT);
p.end(multiSimToken);
+ dumpSetting(s, p,
+ Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
+ GlobalSettingsProto.NATIVE_FLAGS_HEALTH_CHECK_ENABLED);
+
final long netstatsToken = p.start(GlobalSettingsProto.NETSTATS);
dumpSetting(s, p,
Settings.Global.NETSTATS_ENABLED,
@@ -1161,6 +1165,12 @@
GlobalSettingsProto.SmartSelection.UPDATE_METADATA_URL);
p.end(smartSelectToken);
+ final long smartSuggestionsToken = p.start(GlobalSettingsProto.SMART_SUGGESTIONS);
+ dumpSetting(s, p,
+ Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED,
+ GlobalSettingsProto.SmartSuggestions.SERVICE_EXPLICITLY_ENABLED);
+ p.end(smartSuggestionsToken);
+
final long smsToken = p.start(GlobalSettingsProto.SMS);
dumpSetting(s, p,
Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
@@ -1302,6 +1312,9 @@
dumpSetting(s, p,
Settings.Global.WARNING_TEMPERATURE,
GlobalSettingsProto.TemperatureWarning.WARNING_TEMPERATURE_LEVEL);
+ dumpSetting(s, p,
+ Settings.Global.USB_ALARM_TEMPERATURE,
+ GlobalSettingsProto.TemperatureWarning.USB_ALARM_TEMPERATURE_LEVEL);
p.end(tempWarningToken);
final long tetherToken = p.start(GlobalSettingsProto.TETHER);
@@ -1910,6 +1923,15 @@
SecureSettingsProto.Location.CHANGER);
p.end(locationToken);
+ final long locationAccessCheckToken = p.start(SecureSettingsProto.LOCATION_ACCESS_CHECK);
+ dumpSetting(s, p,
+ Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS,
+ SecureSettingsProto.LocationAccessCheck.INTERVAL_MILLIS);
+ dumpSetting(s, p,
+ Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS,
+ SecureSettingsProto.LocationAccessCheck.DELAY_MILLIS);
+ p.end(locationAccessCheckToken);
+
final long lockScreenToken = p.start(SecureSettingsProto.LOCK_SCREEN);
// Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
// Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cb55231..83e8369 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -130,6 +130,7 @@
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
+ <uses-permission android:name="android.permission.MANAGE_SMART_SUGGESTIONS" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SET_TIME" />
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index f8b61cd..44354bc1 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -302,11 +302,13 @@
}
/**
- * Returns whether there is a soft nav bar.
+ * Returns whether there is a soft nav bar on specified display.
+ *
+ * @param displayId the id of display to check if there is a software navigation bar.
*/
- public boolean hasSoftNavigationBar() {
+ public boolean hasSoftNavigationBar(int displayId) {
try {
- return mIwm.hasNavigationBar();
+ return mIwm.hasNavigationBar(displayId);
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
index dfa38ba..8723fb9 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
@@ -631,7 +631,8 @@
}
};
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
- future, animStartedListener, getHandler(), true /* scaleUp */);
+ future, animStartedListener, getHandler(), true /* scaleUp */,
+ getContext().getDisplayId());
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
event.task.getTopComponent().flattenToShortString());
} else {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index f492208..4891e50 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -30,6 +30,12 @@
int VERSION = 1;
void startPendingIntentDismissingKeyguard(PendingIntent intent);
+
+ /**
+ * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but
+ * allow you to specify the callback that is executed after the intent is sent.
+ */
+ void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentCallback);
void startActivity(Intent intent, boolean dismissShade);
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
void startActivity(Intent intent, boolean dismissShade, Callback callback);
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml
deleted file mode 100644
index 3cc98d8..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- Copyright (C) 2014 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="500"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M 0,0 c 31.33333,0 156.66667,0 188,0 "
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml
deleted file mode 100644
index eda843d..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- Copyright (C) 2014 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="33"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M -12,18 L -12,18" />
- <objectAnimator
- android:duration="500"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M -12,18 c 31.33333,0 156.66667,0 188,0 "
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml
deleted file mode 100644
index cab3d5c..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- Copyright (C) 2014 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="67"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M -24,36 L -24,36" />
- <objectAnimator
- android:duration="500"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:pathData="M -24,36 c 31.33333,0 156.66667,0 188,0 "
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml
deleted file mode 100644
index e435d9a..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="150"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="100"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml
deleted file mode 100644
index e31a7db..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="183"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="100"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml
deleted file mode 100644
index 2409612..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 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.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="217"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="100"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/fab_elevation.xml b/packages/SystemUI/res/anim/fab_elevation.xml
deleted file mode 100644
index 2c76a86..0000000
--- a/packages/SystemUI/res/anim/fab_elevation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:state_pressed="true">
- <set>
- <objectAnimator
- android:duration="@android:integer/config_shortAnimTime"
- android:propertyName="translationZ"
- android:valueTo="@dimen/fab_press_translation_z"
- android:valueType="floatType" />
- </set>
- </item>
- <item>
- <set>
- <objectAnimator
- android:duration="@android:integer/config_shortAnimTime"
- android:propertyName="translationZ"
- android:valueTo="0"
- android:valueType="floatType" />
- </set>
- </item>
-</selector>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml
deleted file mode 100644
index 8fdad80..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="-90.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml
deleted file mode 100644
index 3c3c131..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml
deleted file mode 100644
index 57132e1..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="466"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="0.909"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_landscape_to_rotate_animation_interpolator_0" />
- <objectAnimator
- android:duration="466"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="0.909"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_landscape_to_rotate_animation_interpolator_0" />
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="400"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="45.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml
deleted file mode 100644
index ad2a5fa..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="0.9"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="0.9"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml
deleted file mode 100644
index cdb7890..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="-221.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml
deleted file mode 100644
index 46100b4..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="400"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml
deleted file mode 100644
index 8f6d24d..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="400"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="-135.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml
deleted file mode 100644
index 300ed530..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="66"
- android:propertyName="pathData"
- android:valueFrom="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:valueTo="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="216"
- android:propertyName="pathData"
- android:valueFrom="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:valueTo="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml
deleted file mode 100644
index ad2a5fa..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="0.9"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:valueFrom="1.0"
- android:valueTo="0.9"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml
deleted file mode 100644
index c152152..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="0.0"
- android:valueTo="-180.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml
deleted file mode 100644
index b2c1eb8..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="200"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="1.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml
deleted file mode 100644
index 2a9bbe3..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleX"
- android:valueFrom="0.909"
- android:valueTo="0.909"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="166"
- android:propertyName="scaleX"
- android:valueFrom="0.909"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_rotate_to_landscape_animation_interpolator_0" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="116"
- android:propertyName="scaleY"
- android:valueFrom="0.909"
- android:valueTo="0.909"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="166"
- android:propertyName="scaleY"
- android:valueFrom="0.909"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_rotate_to_landscape_animation_interpolator_0" />
- </set>
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="45.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml
deleted file mode 100644
index ce26770..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="466"
- android:propertyName="scaleX"
- android:valueFrom="0.9"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_rotate_to_portrait_animation_interpolator_0" />
- <objectAnimator
- android:duration="466"
- android:propertyName="scaleY"
- android:valueFrom="0.9"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@interpolator/ic_rotate_to_portrait_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml
deleted file mode 100644
index 6e8941d..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <objectAnimator
- android:duration="616"
- android:propertyName="rotation"
- android:valueFrom="-221.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml
deleted file mode 100644
index 3c3c131..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="0.0"
- android:valueTo="1.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml
deleted file mode 100644
index fd8e4f8..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="rotation"
- android:valueFrom="-135.0"
- android:valueTo="-135.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="400"
- android:propertyName="rotation"
- android:valueFrom="-135.0"
- android:valueTo="0.0"
- android:valueType="floatType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml
deleted file mode 100644
index a77a536..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set
- xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="pathData"
- android:valueFrom="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:valueTo="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="500"
- android:propertyName="pathData"
- android:valueFrom="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
- android:valueTo="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_1.xml b/packages/SystemUI/res/anim/ic_signal_blink_1.xml
deleted file mode 100644
index 64580d1..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_1.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
- Copyright (C) 2015 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/linear_interpolator"
- android:duration="@integer/carrier_network_change_anim_time"
- android:repeatCount="-1">
-
- <propertyValuesHolder
- android:propertyName="fillColor"
- android:valueType="colorType">
- <keyframe
- android:fraction="0.0"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="0.32"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="0.33"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="1.0"
- android:value="?attr/backgroundColor"/>
- </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_2.xml b/packages/SystemUI/res/anim/ic_signal_blink_2.xml
deleted file mode 100644
index f055cd07..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_2.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<!--
- Copyright (C) 2015 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/linear_interpolator"
- android:duration="@integer/carrier_network_change_anim_time"
- android:repeatCount="-1">
-
- <propertyValuesHolder
- android:propertyName="fillColor"
- android:valueType="colorType">
- <keyframe
- android:fraction="0.0"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="0.32"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="0.33"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="0.66"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="0.67"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="1.0"
- android:value="?attr/backgroundColor"/>
- </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_3.xml b/packages/SystemUI/res/anim/ic_signal_blink_3.xml
deleted file mode 100644
index abcd774..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_3.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
- Copyright (C) 2015 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/linear_interpolator"
- android:duration="@integer/carrier_network_change_anim_time"
- android:repeatCount="-1">
-
- <propertyValuesHolder
- android:propertyName="fillColor"
- android:valueType="colorType">
- <keyframe
- android:fraction="0.0"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="0.66"
- android:value="?attr/backgroundColor"/>
- <keyframe
- android:fraction="0.67"
- android:value="?attr/fillColor"/>
- <keyframe
- android:fraction="1.0"
- android:value="?attr/fillColor"/>
- </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/system_out.xml b/packages/SystemUI/res/anim/system_out.xml
deleted file mode 100644
index 4717e47..0000000
--- a/packages/SystemUI/res/anim/system_out.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- >
- <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
- android:duration="@android:integer/config_longAnimTime"
- />
-</set>
diff --git a/packages/SystemUI/res/drawable/car_ic_music.xml b/packages/SystemUI/res/drawable/car_ic_music.xml
deleted file mode 100644
index f90cd69..0000000
--- a/packages/SystemUI/res/drawable/car_ic_music.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- Copyright (C) 2016 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="56dp"
- android:height="56dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:fillAlpha=".1"
- android:strokeAlpha=".1"
- android:pathData="M0 0h48v48H0z" />
- <path
- android:fillColor="@color/car_grey_50"
- android:pathData="M24 2C14.06 2 6 10.06 6 20v14c0 3.31 2.69 6 6 6h6V24h-8v-4c0-7.73 6.27-14
-14-14s14 6.27 14 14v4h-8v16h6c3.31 0 6-2.69 6-6V20c0-9.94-8.06-18-18-18z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/car_ic_navigation.xml b/packages/SystemUI/res/drawable/car_ic_navigation.xml
deleted file mode 100644
index 328efa0..0000000
--- a/packages/SystemUI/res/drawable/car_ic_navigation.xml
+++ /dev/null
@@ -1,28 +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
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="32dp"
- android:height="37dp"
- android:viewportWidth="32.0"
- android:viewportHeight="37.0">
- <path
- android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
- android:strokeColor="#00000000"
- android:fillType="evenOdd"
- android:fillColor="@color/car_grey_50"
- android:strokeWidth="1"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml
deleted file mode 100644
index 61d937b90..0000000
--- a/packages/SystemUI/res/drawable/car_ic_notification.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="56dp"
- android:height="56dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4
-4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73
-1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8
-9v12z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml
deleted file mode 100644
index 4651dcb..0000000
--- a/packages/SystemUI/res/drawable/car_ic_overview.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- Copyright (C) 2016 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="56dp"
- android:height="56dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:pathData="M0 0h48v48H0z" />
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82
-0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
deleted file mode 100644
index 25b449a..0000000
--- a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?android:attr/colorBackgroundFloating" />
- <corners
- android:bottomLeftRadius="@dimen/car_radius_3"
- android:topLeftRadius="0dp"
- android:bottomRightRadius="@dimen/car_radius_3"
- android:topRightRadius="0dp"
- />
-</shape>
diff --git a/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml b/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml
deleted file mode 100644
index 9e71cbe..0000000
--- a/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/dismiss_all_shape" >
- <target
- android:name="3"
- android:animation="@anim/dismiss_all_shape_animation_3" />
- <target
- android:name="rectangle_path_1_2"
- android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1_2" />
- <target
- android:name="2"
- android:animation="@anim/dismiss_all_shape_animation_2" />
- <target
- android:name="rectangle_path_1_1"
- android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1_1" />
- <target
- android:name="1"
- android:animation="@anim/dismiss_all_shape_animation_1" />
- <target
- android:name="rectangle_path_1"
- android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_notification_block.xml b/packages/SystemUI/res/drawable/ic_notification_block.xml
index 572e97b..27690740 100644
--- a/packages/SystemUI/res/drawable/ic_notification_block.xml
+++ b/packages/SystemUI/res/drawable/ic_notification_block.xml
@@ -20,6 +20,6 @@
android:viewportHeight="24.0">
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="#FF000000"
android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/>
</vector>
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
index 5ca34b0..1e8cd5a 100644
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -160,6 +160,15 @@
android:maxLines="2"
android:text="@string/biometric_dialog_confirm"
android:visibility="gone"/>
+ <!-- Try Again Button -->
+ <Button android:id="@+id/button_try_again"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:gravity="center"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_try_again"
+ android:visibility="gone"/>
<Space android:id="@+id/rightSpacer"
android:layout_width="12dip"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
deleted file mode 100644
index d568d0d..0000000
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2016, 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:background="@drawable/system_bar_background">
-
- <LinearLayout
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:orientation="horizontal"
- android:id="@+id/nav_buttons"
- android:gravity="left"
- android:paddingLeft="30dp"
- android:layout_weight="1"
- android:animateLayoutChanges="true">
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/home"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- android:src="@drawable/car_ic_overview"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
- />
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/hvac"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
- systemui:broadcast="true"
- android:src="@drawable/car_ic_hvac"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
- />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="right"
- android:orientation="horizontal">
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/notifications"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:src="@drawable/car_ic_notification"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:alpha="0.7"
- />
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_clock_end_padding"
- android:gravity="center_vertical"
- android:paddingRight="20dp"
- />
-
- <Space
- android:layout_width="10dp"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
deleted file mode 100644
index 4ba6c06..0000000
--- a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:background="@drawable/system_bar_background">
-
- <LinearLayout
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:orientation="horizontal"
- android:id="@+id/nav_buttons"
- android:gravity="left"
- android:paddingLeft="30dp"
- android:layout_weight="1"
- android:animateLayoutChanges="true">
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/home"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- android:src="@drawable/car_ic_overview"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
- />
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/hvac"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
- systemui:broadcast="true"
- android:src="@drawable/car_ic_hvac"
- android:background="?android:attr/selectableItemBackground"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
- />
- </LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
deleted file mode 100644
index 91ba026..0000000
--- a/packages/SystemUI/res/layout/car_right_navigation_bar.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2016, 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:background="@drawable/system_bar_background">
-
- <LinearLayout
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:id="@+id/nav_buttons"
- android:orientation="vertical"
- android:gravity="top"
- android:paddingTop="30dp"
- android:layout_weight="1"
- android:background="@drawable/system_bar_background"
- android:animateLayoutChanges="true">
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/home"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- android:src="@drawable/car_ic_overview"
- android:background="?android:attr/selectableItemBackground"
- android:paddingTop="30dp"
- android:paddingBottom="30dp"
- />
-
- <com.android.systemui.statusbar.car.CarNavigationButton
- android:id="@+id/hvac"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
- systemui:broadcast="true"
- android:src="@drawable/car_ic_hvac"
- android:background="?android:attr/selectableItemBackground"
- android:paddingTop="30dp"
- android:paddingBottom="30dp"
- />
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="bottom"
- android:orientation="vertical">
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/notifications"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:src="@drawable/car_ic_notification"
- android:background="?android:attr/selectableItemBackground"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:alpha="0.7"
- />
-
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_clock_end_padding"
- android:gravity="center_horizontal"
- android:paddingBottom="20dp"
- />
-
- <Space
- android:layout_height="10dp"
- android:layout_width="match_parent"/>
-
- </LinearLayout>
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml
deleted file mode 100644
index f2ef301..0000000
--- a/packages/SystemUI/res/layout/car_status_bar_header.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!-- Extends LinearLayout -->
-<com.android.systemui.qs.car.CarStatusBarHeader
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_qs_header_system_icons_area_height"
- android:paddingStart="8dp"
- android:paddingEnd="8dp" >
-
- <include layout="@layout/system_icons"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical|end"
- android:layout_weight="1"
- />
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_clock_end_padding"
- android:gravity="center_vertical|end"
- />
-</com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/car_top_navigation_bar.xml b/packages/SystemUI/res/layout/car_top_navigation_bar.xml
deleted file mode 100644
index e16014b..0000000
--- a/packages/SystemUI/res/layout/car_top_navigation_bar.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
-** Copyright 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:background="@drawable/system_bar_background">
-
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_clock_end_padding"
- android:gravity="center_vertical"
- />
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
deleted file mode 100644
index a6beaa1..0000000
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- 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.
--->
-<androidx.car.widget.PagedListView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:background="@drawable/car_card_rounded_background"
- android:id="@+id/volume_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/car_margin"
- android:layout_marginEnd="@dimen/car_margin"
- android:minWidth="@dimen/volume_dialog_panel_width"
- android:theme="@style/Theme.Car.NoActionBar"
- app:dividerStartMargin="@dimen/car_keyline_1"
- app:dividerEndMargin="@dimen/car_keyline_1"
- app:gutter="none"
- app:showPagedListViewDivider="true"
- app:scrollBarEnabled="false" />
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index eb3f70a..d502baa 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -144,6 +144,7 @@
<!-- Settings and Done buttons -->
<LinearLayout
+ android:id="@+id/block_or_minimize"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_guts_button_spacing"
@@ -178,13 +179,6 @@
android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
style="@style/TextAppearance.NotificationInfo.Button" />
<TextView
- android:id="@+id/toggle_silent"
- android:text="@string/inline_silent_button_silent"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
- style="@style/TextAppearance.NotificationInfo.Button" />
- <TextView
android:id="@+id/keep"
android:minWidth="48dp"
android:text="@string/inline_keep_button"
@@ -193,6 +187,44 @@
android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
style="@style/TextAppearance.NotificationInfo.Button"/>
</LinearLayout>
+ <LinearLayout
+ android:id="@+id/interruptiveness_settings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_guts_button_spacing"
+ android:layout_marginStart="@dimen/notification_guts_button_side_margin"
+ android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:visibility="gone">
+ <TextView
+ android:id="@+id/int_block"
+ android:text="@string/inline_block_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:drawableTop="@drawable/ic_notification_block"
+ android:drawableTint="?android:attr/colorAccent"
+ android:layout_weight="1"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/int_silent"
+ android:text="@string/inline_minimize_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:drawableTop="@drawable/ic_notifications_silence"
+ android:drawableTint="?android:attr/colorAccent"
+ android:layout_weight="1"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/int_alert"
+ android:text="@string/inline_keep_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:drawableTop="@drawable/ic_notifications_alert"
+ android:drawableTint="?android:attr/colorAccent"
+ android:layout_weight="1"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </LinearLayout>
</LinearLayout>
<com.android.systemui.statusbar.notification.row.NotificationUndoLayout
android:id="@+id/confirmation"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 0cc3c9e..34c208a 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -56,11 +56,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
- android:layout="@layout/car_fullscreen_user_switcher"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/arrays_car.xml b/packages/SystemUI/res/values/arrays_car.xml
deleted file mode 100644
index 8c760fc..0000000
--- a/packages/SystemUI/res/values/arrays_car.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- These should be overriden in an overlay. The default implementation is empty.
- There needs to be correspondence per index between these arrays, which means that if there
- isn't a longpress action associated with a shortcut item, put in an empty item to make
- sure everything lines up.
- -->
- <array name="car_facet_icons" />
- <array name="car_facet_intent_uris" />
- <array name="car_facet_longpress_intent_uris" />
- <array name="car_facet_package_filters"/>
- <array name="car_facet_category_filters"/>
-</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b0a519c..8e0bfb6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -881,6 +881,7 @@
<dimen name="smart_reply_button_stroke_width">1dp</dimen>
<dimen name="smart_reply_button_font_size">14sp</dimen>
<dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
+ <dimen name="smart_action_button_icon_size">24dp</dimen>
<dimen name="smart_action_button_icon_padding">10dp</dimen>
<!-- A reasonable upper bound for the height of the smart reply button. The measuring code
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c5654f0..9917257 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -272,6 +272,8 @@
<string name="accessibility_biometric_dialog_help_area">Help message area</string>
<!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_confirm">Confirm</string>
+ <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR_LIMIT=30] -->
+ <string name="biometric_dialog_try_again">Try again</string>
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
@@ -700,6 +702,8 @@
<string name="quick_settings_bluetooth_secondary_label_headset">Headset</string>
<!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_input">Input</string>
+ <!-- QuickSettings: Bluetooth secondary label for a Hearing Aids device being connected [CHAR LIMIT=20]-->
+ <string name="quick_settings_bluetooth_secondary_label_hearing_aids">Hearing Aids</string>
<!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] -->
<string name="quick_settings_bluetooth_secondary_label_transient">Turning on…</string>
<!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
@@ -1556,17 +1560,26 @@
<!-- Notification inline controls: block notifications button -->
<string name="inline_stop_button">Stop notifications</string>
+ <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] -->
+ <string name="inline_block_button">Block</string>
+
<!-- Notification inline controls: keep getting notifications button -->
<string name="inline_keep_button">Keep showing</string>
<!-- Notification inline controls: minimize notifications button -->
<string name="inline_minimize_button">Minimize</string>
- <!-- Notification inline controls: show notifications silently button [CHAR_LIMIT=25] -->
+ <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] -->
<string name="inline_silent_button_silent">Show silently</string>
- <!-- Notification inline controls: show and alert button [CHAR_LIMIT=25] -->
- <string name="inline_silent_button_alert">Show and alert</string>
+ <!-- Notification inline controls: button to continue showing notifications silently [CHAR_LIMIT=35] -->
+ <string name="inline_silent_button_stay_silent">Stay silent</string>
+
+ <!-- Notification inline controls: button to make notifications alert the user [CHAR_LIMIT=35] -->
+ <string name="inline_silent_button_alert">Alert me</string>
+
+ <!-- Notification inline controls: button to continue alerting the user when notifications arrive [CHAR_LIMIT=35] -->
+ <string name="inline_silent_button_keep_alerting">Keep alerting</string>
<!-- Notification Inline controls: continue receiving notifications prompt, app level -->
<string name="inline_keep_showing_app">Keep showing notifications from this app?</string>
@@ -2255,6 +2268,7 @@
<!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
<plurals name="ongoing_privacy_chip_multiple_apps">
+ <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> app</item>
<item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
<item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
</plurals>
@@ -2267,6 +2281,7 @@
<!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
<plurals name="ongoing_privacy_chip_content_multiple_apps_single_op">
+ <item quantity="one"><xliff:g id="num_apps" example="1">%1$d</xliff:g> application is using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
<item quantity="few"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
<item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
</plurals>
diff --git a/packages/SystemUI/res/xml/car_volume_items.xml b/packages/SystemUI/res/xml/car_volume_items.xml
deleted file mode 100644
index 742dfdd..0000000
--- a/packages/SystemUI/res/xml/car_volume_items.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- *
- * Copyright 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.
-*/
--->
-
-<!--
- Defines all possible items on car volume settings UI, keyed by usage.
-
- This enables the CarSettings UI to associate VolumeGroups surfaced by
- CarAudioManager.getVolumeGroupCount with renderable assets (ie: title, icon)
- for presentation.
-
- Order matters in this configuration. If one volume group contains multiple
- audio usages, the first one appears in this file would be picked to be
- presented on UI.
-
- When overriding this configuration, please consult also the
- car_volume_groups.xml, which is read by car audio service.
--->
-<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
- <item car:usage="unknown"
- car:icon="@drawable/car_ic_music"/>
- <item car:usage="media"
- car:icon="@drawable/car_ic_music"/>
- <item car:usage="voice_communication"
- car:icon="@*android:drawable/ic_audio_ring_notif"/>
- <item car:usage="voice_communication_signalling"
- car:icon="@*android:drawable/ic_audio_ring_notif"/>
- <item car:usage="alarm"
- car:icon="@*android:drawable/ic_audio_alarm"/>
- <item car:usage="notification"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="notification_ringtone"
- car:icon="@*android:drawable/ic_audio_ring_notif"/>
- <item car:usage="notification_communication_request"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="notification_communication_instant"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="notification_communication_delayed"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="notification_event"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="assistance_accessibility"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="assistance_navigation_guidance"
- car:icon="@drawable/car_ic_navigation"/>
- <item car:usage="assistance_sonification"
- car:icon="@drawable/car_ic_notification"/>
- <item car:usage="game"
- car:icon="@drawable/car_ic_music"/>
- <item car:usage="assistant"
- car:icon="@drawable/car_ic_music"/>
-</carVolumeItems>
-
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index ece2bb9..f3bdbae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -71,4 +71,9 @@
*/
void onStatusBarMotionEvent(in MotionEvent event) = 9;
+ /**
+ * Get the corner radius of windows in pixels.
+ */
+ float getWindowCornerRadius() = 10;
+
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
index 65c5220..a9cf857 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
@@ -83,6 +83,7 @@
t.setWindowCrop(params.surface, params.windowCrop);
t.setAlpha(params.surface, params.alpha);
t.setLayer(params.surface, params.layer);
+ t.setCornerRadius(params.surface, params.cornerRadius);
t.show(params.surface);
}
@@ -98,12 +99,13 @@
* @param windowCrop Crop to apply.
*/
public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
- Rect windowCrop, int layer) {
+ Rect windowCrop, int layer, float cornerRadius) {
this.surface = surface.mSurfaceControl;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
this.windowCrop = new Rect(windowCrop);
this.layer = layer;
+ this.cornerRadius = cornerRadius;
}
final SurfaceControl surface;
@@ -111,5 +113,6 @@
final Matrix matrix;
final Rect windowCrop;
final int layer;
+ final float cornerRadius;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index 70258c2..2aba3fa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -20,7 +20,6 @@
import android.graphics.Rect;
import android.os.IBinder;
import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
public class TransactionCompat {
@@ -53,7 +52,7 @@
}
public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) {
- mTransaction.setSize(surfaceControl.mSurfaceControl, w, h);
+ mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);
return this;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 42e60aa..8a251ae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -18,6 +18,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
@@ -61,7 +62,7 @@
public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
- public static final int NAV_BAR_POS_INVALID = -1;
+ public static final int NAV_BAR_POS_INVALID = NAV_BAR_INVALID;
public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT;
public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM;
@@ -100,23 +101,23 @@
* Overrides a pending app transition.
*/
public void overridePendingAppTransitionMultiThumbFuture(
- AppTransitionAnimationSpecsFuture animationSpecFuture,
- Runnable animStartedCallback, Handler animStartedCallbackHandler, boolean scaleUp) {
+ AppTransitionAnimationSpecsFuture animationSpecFuture, Runnable animStartedCallback,
+ Handler animStartedCallbackHandler, boolean scaleUp, int displayId) {
try {
WindowManagerGlobal.getWindowManagerService()
.overridePendingAppTransitionMultiThumbFuture(animationSpecFuture.getFuture(),
RecentsTransition.wrapStartedListener(animStartedCallbackHandler,
- animStartedCallback), scaleUp);
+ animStartedCallback), scaleUp, displayId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e);
}
}
public void overridePendingAppTransitionRemote(
- RemoteAnimationAdapterCompat remoteAnimationAdapter) {
+ RemoteAnimationAdapterCompat remoteAnimationAdapter, int displayId) {
try {
WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote(
- remoteAnimationAdapter.getWrapped());
+ remoteAnimationAdapter.getWrapped(), displayId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to override pending app transition (remote): ", e);
}
@@ -159,11 +160,13 @@
}
/**
- * @return whether there is a soft nav bar.
+ * @param displayId the id of display to check if there is a software navigation bar.
+ *
+ * @return whether there is a soft nav bar on specific display.
*/
- public boolean hasSoftNavigationBar() {
+ public boolean hasSoftNavigationBar(int displayId) {
try {
- return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
+ return WindowManagerGlobal.getWindowManagerService().hasNavigationBar(displayId);
} catch (RemoteException e) {
return false;
}
@@ -176,10 +179,9 @@
* @see #NAV_BAR_POS_BOTTOM
* @see #NAV_BAR_POS_INVALID
*/
- public int getNavBarPosition() {
+ public int getNavBarPosition(int displayId) {
try {
- // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
- return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(displayId);
} catch (RemoteException e) {
Log.w(TAG, "Failed to get nav bar position");
}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index e1b8dc8..9e7c5ba 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -36,6 +36,15 @@
}
@Override
+ public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+ Runnable intentSentCallback) {
+ if (mActualStarter == null) {
+ return;
+ }
+ mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback);
+ }
+
+ @Override
public void startActivity(Intent intent, boolean dismissShade) {
if (mActualStarter == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 417d516..867c917 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.volume.VolumeDialogComponent;
import java.util.function.Consumer;
@@ -132,6 +133,10 @@
return new QSTileHost(context, statusBar, iconController);
}
+ public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
+ return new VolumeDialogComponent(systemUi, context);
+ }
+
public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
Context context) {
providers.put(StatusBarStateController.class, StatusBarStateController::new);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index c0047c0..a90a7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -21,7 +21,7 @@
import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -52,15 +52,20 @@
private static final int MSG_BUTTON_NEGATIVE = 6;
private static final int MSG_USER_CANCELED = 7;
private static final int MSG_BUTTON_POSITIVE = 8;
+ private static final int MSG_BIOMETRIC_SHOW_TRY_AGAIN = 9;
+ private static final int MSG_TRY_AGAIN_PRESSED = 10;
private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
private SomeArgs mCurrentDialogArgs;
private BiometricDialogView mCurrentDialog;
private WindowManager mWindowManager;
- private IBiometricPromptReceiver mReceiver;
+ private IBiometricServiceReceiverInternal mReceiver;
private boolean mDialogShowing;
private Callback mCallback = new Callback();
+ private boolean mTryAgainShowing; // No good place to save state before config change :/
+ private boolean mConfirmShowing; // No good place to save state before config change :/
+
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -89,6 +94,15 @@
case MSG_BUTTON_POSITIVE:
handleButtonPositive();
break;
+ case MSG_BIOMETRIC_SHOW_TRY_AGAIN:
+ handleShowTryAgain();
+ break;
+ case MSG_TRY_AGAIN_PRESSED:
+ handleTryAgainPressed();
+ break;
+ default:
+ Log.w(TAG, "Unknown message: " + msg.what);
+ break;
}
}
};
@@ -96,7 +110,7 @@
private class Callback implements DialogViewCallback {
@Override
public void onUserCanceled() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget();
+ mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
}
@Override
@@ -107,12 +121,17 @@
@Override
public void onNegativePressed() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+ mHandler.obtainMessage(MSG_BUTTON_NEGATIVE).sendToTarget();
}
@Override
public void onPositivePressed() {
- mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+ mHandler.obtainMessage(MSG_BUTTON_POSITIVE).sendToTarget();
+ }
+
+ @Override
+ public void onTryAgainPressed() {
+ mHandler.obtainMessage(MSG_TRY_AGAIN_PRESSED).sendToTarget();
}
}
@@ -139,13 +158,14 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type);
// Remove these messages as they are part of the previous client
mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
mHandler.removeMessages(MSG_BIOMETRIC_HELP);
mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
+ mHandler.removeMessages(MSG_HIDE_DIALOG);
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
@@ -179,6 +199,12 @@
mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
}
+ @Override
+ public void showBiometricTryAgain() {
+ if (DEBUG) Log.d(TAG, "showBiometricTryAgain");
+ mHandler.obtainMessage(MSG_BIOMETRIC_SHOW_TRY_AGAIN).sendToTarget();
+ }
+
private void handleShowDialog(SomeArgs args, boolean skipAnimation) {
mCurrentDialogArgs = args;
final int type = args.argi1;
@@ -193,11 +219,13 @@
Log.w(TAG, "Dialog already showing");
return;
}
- mReceiver = (IBiometricPromptReceiver) args.arg2;
+ mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
mCurrentDialog.setBundle((Bundle)args.arg1);
mCurrentDialog.setRequireConfirmation((boolean) args.arg3);
mCurrentDialog.setUserId(args.argi2);
mCurrentDialog.setSkipIntro(skipAnimation);
+ mCurrentDialog.setPendingTryAgain(mTryAgainShowing);
+ mCurrentDialog.setPendingConfirm(mConfirmShowing);
mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
mDialogShowing = true;
}
@@ -209,7 +237,8 @@
mContext.getResources()
.getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
if (mCurrentDialog.requiresConfirmation()) {
- mCurrentDialog.showConfirmationButton();
+ mConfirmShowing = true;
+ mCurrentDialog.showConfirmationButton(true /* show */);
} else {
handleHideDialog(false /* userCanceled */);
}
@@ -226,6 +255,7 @@
if (DEBUG) Log.d(TAG, "Dialog already dismissed");
return;
}
+ mTryAgainShowing = false;
mCurrentDialog.showErrorMessage(error);
}
@@ -246,6 +276,8 @@
}
mReceiver = null;
mDialogShowing = false;
+ mConfirmShowing = false;
+ mTryAgainShowing = false;
mCurrentDialog.startDismiss();
}
@@ -259,6 +291,7 @@
} catch (RemoteException e) {
Log.e(TAG, "Remote exception when handling negative button", e);
}
+ mTryAgainShowing = false;
handleHideDialog(false /* userCanceled */);
}
@@ -272,13 +305,31 @@
} catch (RemoteException e) {
Log.e(TAG, "Remote exception when handling positive button", e);
}
+ mConfirmShowing = false;
handleHideDialog(false /* userCanceled */);
}
private void handleUserCanceled() {
+ mTryAgainShowing = false;
+ mConfirmShowing = false;
handleHideDialog(true /* userCanceled */);
}
+ private void handleShowTryAgain() {
+ mCurrentDialog.showTryAgainButton(true /* show */);
+ mTryAgainShowing = true;
+ }
+
+ private void handleTryAgainPressed() {
+ try {
+ mCurrentDialog.clearTemporaryMessage();
+ mTryAgainShowing = false;
+ mReceiver.onTryAgainPressed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException when handling try again", e);
+ }
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 38427ad..e085f23 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -87,6 +87,9 @@
protected boolean mRequireConfirmation;
private int mUserId; // used to determine if we should show work background
+ private boolean mPendingShowTryAgain;
+ private boolean mPendingShowConfirm;
+
protected abstract void updateIcon(int lastState, int newState);
protected abstract int getHintStringResourceId();
protected abstract int getAuthenticatedAccessibilityResourceId();
@@ -178,6 +181,7 @@
final Button negative = mLayout.findViewById(R.id.button2);
final Button positive = mLayout.findViewById(R.id.button1);
final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
+ final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
@@ -193,6 +197,11 @@
mCallback.onPositivePressed();
});
+ tryAgain.setOnClickListener((View v) -> {
+ showTryAgainButton(false /* show */);
+ mCallback.onTryAgainPressed();
+ });
+
mLayout.setFocusableInTouchMode(true);
mLayout.requestFocus();
}
@@ -207,7 +216,6 @@
final TextView subtitle = mLayout.findViewById(R.id.subtitle);
final TextView description = mLayout.findViewById(R.id.description);
final Button negative = mLayout.findViewById(R.id.button2);
- final Button positive = mLayout.findViewById(R.id.button1);
final ImageView backgroundView = mLayout.findViewById(R.id.background);
if (mUserManager.isManagedProfile(mUserId)) {
@@ -233,8 +241,6 @@
title.setText(titleText);
title.setSelected(true);
- positive.setVisibility(View.INVISIBLE);
-
final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
if (TextUtils.isEmpty(subtitleText)) {
subtitle.setVisibility(View.GONE);
@@ -243,7 +249,8 @@
subtitle.setText(subtitleText);
}
- final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+ final CharSequence descriptionText =
+ mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
if (TextUtils.isEmpty(descriptionText)) {
description.setVisibility(View.GONE);
} else {
@@ -253,6 +260,9 @@
negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
+ showTryAgainButton(mPendingShowTryAgain);
+ showConfirmationButton(mPendingShowConfirm);
+
if (mWasForceRemoved || mSkipIntro) {
// Show the dialog immediately
mLayout.animate().cancel();
@@ -281,11 +291,17 @@
public void startDismiss() {
mAnimatingAway = true;
+ // This is where final cleanup should occur.
final Runnable endActionRunnable = new Runnable() {
@Override
public void run() {
mWindowManager.removeView(BiometricDialogView.this);
mAnimatingAway = false;
+ // Set the icons / text back to normal state
+ handleClearMessage();
+ showTryAgainButton(false /* show */);
+ mPendingShowTryAgain = false;
+ mPendingShowConfirm = false;
}
};
@@ -345,9 +361,13 @@
return mRequireConfirmation;
}
- public void showConfirmationButton() {
+ public void showConfirmationButton(boolean show) {
final Button positive = mLayout.findViewById(R.id.button1);
- positive.setVisibility(View.VISIBLE);
+ if (show) {
+ positive.setVisibility(View.VISIBLE);
+ } else {
+ positive.setVisibility(View.GONE);
+ }
}
public void setUserId(int userId) {
@@ -376,12 +396,18 @@
BiometricPrompt.HIDE_DIALOG_DELAY);
}
+ public void clearTemporaryMessage() {
+ mHandler.removeMessages(MSG_CLEAR_MESSAGE);
+ mHandler.obtainMessage(MSG_CLEAR_MESSAGE).sendToTarget();
+ }
+
public void showHelpMessage(String message) {
showTemporaryMessage(message);
}
public void showErrorMessage(String error) {
showTemporaryMessage(error);
+ showTryAgainButton(false /* show */);
mCallback.onErrorShown();
}
@@ -390,6 +416,25 @@
mLastState = newState;
}
+ public void showTryAgainButton(boolean show) {
+ final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
+ if (show) {
+ tryAgain.setVisibility(View.VISIBLE);
+ } else {
+ tryAgain.setVisibility(View.GONE);
+ }
+ }
+
+ // Set the state before the window is attached, so we know if the dialog should be started
+ // with or without the button. This is because there's no good onPause signal
+ public void setPendingTryAgain(boolean show) {
+ mPendingShowTryAgain = show;
+ }
+
+ public void setPendingConfirm(boolean show) {
+ mPendingShowConfirm = show;
+ }
+
public WindowManager.LayoutParams getLayoutParams() {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
index f388d9c..24fd22e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -43,4 +43,9 @@
* should be dismissed.
*/
void onPositivePressed();
+
+ /**
+ * Invoked when the "try again" button is pressed.
+ */
+ void onTryAgainPressed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index c6dcfc7..416cc59 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -23,10 +23,10 @@
import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
import android.app.Notification;
-import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -57,6 +57,11 @@
// When a bubble is dismissed, recreate it as a notification
public static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
+ // Secure settings
+ private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
+ private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
+ private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
+
private Context mContext;
private BubbleDismissListener mDismissListener;
private BubbleStateChangeListener mStateChangeListener;
@@ -318,11 +323,15 @@
/**
* Whether the notification should bubble or not.
*/
- public static boolean shouldAutoBubble(NotificationData.Entry entry, int priority,
- boolean canAppOverlay) {
- if (!DEBUG_ENABLE_AUTO_BUBBLE || entry.isBubbleDismissed()) {
+ public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) {
+ if (entry.isBubbleDismissed()) {
return false;
}
+
+ boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+ boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+ boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+
StatusBarNotification n = entry.notification;
boolean hasRemoteInput = false;
if (n.getNotification().actions != null) {
@@ -333,12 +342,28 @@
}
}
}
+
Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle();
- boolean shouldBubble = priority >= NotificationManager.IMPORTANCE_HIGH
- || Notification.MessagingStyle.class.equals(style)
+ boolean isMessageType = Notification.MessagingStyle.class.equals(style)
|| Notification.CATEGORY_MESSAGE.equals(n.getNotification().category)
- || hasRemoteInput
- || canAppOverlay;
- return shouldBubble && !entry.isBubbleDismissed();
+ || hasRemoteInput;
+ return (isMessageType && autoBubbleMessages)
+ || (n.isOngoing() && autoBubbleOngoing)
+ || autoBubbleAll;
+ }
+
+ private static boolean shouldAutoBubbleMessages(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_AUTO_BUBBLE_MESSAGES, 0) != 0;
+ }
+
+ private static boolean shouldAutoBubbleOngoing(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_AUTO_BUBBLE_ONGOING, 0) != 0;
+ }
+
+ private static boolean shouldAutoBubbleAll(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
deleted file mode 100644
index 09c000b..0000000
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 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.car;
-
-import android.content.Context;
-import android.util.ArrayMap;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.SystemUIFactory;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.car.CarFacetButtonController;
-import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-
-/**
- * Class factory to provide car specific SystemUI components.
- */
-public class CarSystemUIFactory extends SystemUIFactory {
-
- public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
- ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
- return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
- }
-
- @Override
- public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
- Context context) {
- super.injectDependencies(providers, context);
- providers.put(NotificationEntryManager.class,
- () -> new CarNotificationEntryManager(context));
- providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
- providers.put(HvacController.class, () -> new HvacController(context));
- providers.put(NotificationMediaManager.class,
- () -> new CarNotificationMediaManager(context));
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 2c61da3..1718cff 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -108,7 +108,7 @@
UserHandle.USER_ALL);
updateConfiguration();
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
}
public static FalsingManager getInstance(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 01a2345..1dd31010 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -24,20 +24,21 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.os.Build;
import android.os.Handler;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
/**
* Controls the screen brightness when dozing.
*/
public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
SensorEventListener {
+ private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties
+ .getBoolean("debug.aod_brightness", false);
protected static final String ACTION_AOD_BRIGHTNESS =
"com.android.systemui.doze.AOD_BRIGHTNESS";
protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
@@ -83,11 +84,9 @@
mSensorToScrimOpacity = sensorToScrimOpacity;
if (mDebuggable) {
- Dependency.get(Dependency.BG_HANDLER).post(()-> {
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_AOD_BRIGHTNESS);
- mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
- });
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_AOD_BRIGHTNESS);
+ mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
}
}
@@ -97,7 +96,7 @@
this(context, service, sensorManager, lightSensor, host, handler,
context.getResources().getInteger(
com.android.internal.R.integer.config_screenBrightnessDoze),
- policy.screenBrightnessArray, policy.dimmingScrimArray, Build.IS_DEBUGGABLE);
+ policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS);
}
@Override
@@ -126,9 +125,7 @@
private void onDestroy() {
setLightSensorEnabled(false);
if (mDebuggable) {
- Dependency.get(Dependency.BG_HANDLER).post(()-> {
- mContext.unregisterReceiver(this);
- });
+ mContext.unregisterReceiver(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index bf8e04d..7e77843 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -401,7 +401,9 @@
}
mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
event.values);
- updateListener(); // reregister, this sensor only fires once
+ if (!mRegistered) {
+ updateListener(); // reregister, this sensor only fires once
+ }
}));
}
@@ -440,7 +442,9 @@
mRegistered = false;
mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
event.getValues());
- updateListener(); // reregister, this sensor only fires once
+ if (!mRegistered) {
+ updateListener(); // reregister, this sensor only fires once
+ }
}));
};
@@ -487,7 +491,6 @@
}
return sb.append(']').toString();
}
-
}
public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bad0148..afe9a74 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -184,6 +184,7 @@
if (DEBUG) Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch);
mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch);
}
+
if (far && (paused || pausing)) {
if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
mMachine.requestState(DozeMachine.State.DOZE_AOD);
@@ -205,13 +206,13 @@
// In pocket, drop event.
return;
}
- if (pausing || paused) {
+ if (mMachine.getState() == DozeMachine.State.DOZE) {
mMachine.requestState(DozeMachine.State.DOZE_AOD);
}
}, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
} else {
if (!pausing && !paused) {
- mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
+ mMachine.requestState(DozeMachine.State.DOZE);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 9a5a5b8..be504ef 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -23,6 +23,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.PrintWriter;
@@ -80,11 +81,12 @@
if (isAmbientMode != mIsAmbientMode) {
mIsAmbientMode = isAmbientMode;
try {
+ long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L;
if (DEBUG) {
Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
- + ", animated: " + animated);
+ + ", animationDuration: " + duration);
}
- mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated);
+ mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration);
} catch (RemoteException e) {
// Cannot notify wallpaper manager service, but it's fine, let's just skip it.
Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index e447def..8495fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -19,6 +19,8 @@
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT;
@@ -65,6 +67,7 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -90,8 +93,8 @@
public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
public static final int MESSAGE_ANIMATION_ENDED = 6;
- private static final long INITIAL_DISMISS_DELAY = 3500;
- private static final long POST_INTERACTION_DISMISS_DELAY = 2000;
+ private static final int INITIAL_DISMISS_DELAY = 3500;
+ private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
private static final long MENU_FADE_DURATION = 125;
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
@@ -105,6 +108,7 @@
private final List<RemoteAction> mActions = new ArrayList<>();
+ private AccessibilityManager mAccessibilityManager;
private View mViewRoot;
private Drawable mBackgroundDrawable;
private View mMenuContainer;
@@ -194,6 +198,7 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.pip_menu_activity);
+ mAccessibilityManager = getSystemService(AccessibilityManager.class);
mBackgroundDrawable = new ColorDrawable(Color.BLACK);
mBackgroundDrawable.setAlpha(0);
mViewRoot = findViewById(R.id.background);
@@ -639,8 +644,10 @@
mHandler.removeCallbacks(mFinishRunnable);
}
- private void repostDelayedFinish(long delay) {
+ private void repostDelayedFinish(int delay) {
+ int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+ FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
mHandler.removeCallbacks(mFinishRunnable);
- mHandler.postDelayed(mFinishRunnable, delay);
+ mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 03a573e..6a9f24c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -368,7 +368,7 @@
private void onAccessibilityShowMenu() {
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
+ mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
}
private boolean handleTouchEvent(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 8b434a5..496aa0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -323,7 +323,9 @@
post(new Runnable() {
@Override
public void run() {
- handleShowingDetail(detail, x, y, false /* toggleQs */);
+ if (isAttachedToWindow()) {
+ handleShowingDetail(detail, x, y, false /* toggleQs */);
+ }
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 591e9e0..9d2be39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -26,6 +26,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
+import android.provider.Settings;
import android.service.quicksettings.TileService;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -69,7 +70,8 @@
mSpecs.clear();
mFinished = false;
// Enqueue jobs to fetch every system tile and then ever package tile.
- addStockTiles(host);
+ addCurrentAndStockTiles(host);
+
addPackageTiles(host);
}
@@ -77,16 +79,28 @@
return mFinished;
}
- private void addStockTiles(QSTileHost host) {
- String possible = mContext.getString(R.string.quick_settings_tiles_stock);
+ private void addCurrentAndStockTiles(QSTileHost host) {
+ String stock = mContext.getString(R.string.quick_settings_tiles_stock);
+ String current = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.QS_TILES);
final ArrayList<String> possibleTiles = new ArrayList<>();
- possibleTiles.addAll(Arrays.asList(possible.split(",")));
- if (Build.IS_DEBUGGABLE) {
+ if (current != null) {
+ // The setting QS_TILES is not populated immediately upon Factory Reset
+ possibleTiles.addAll(Arrays.asList(current.split(",")));
+ }
+ String[] stockSplit = stock.split(",");
+ for (String spec : stockSplit) {
+ if (!current.contains(spec)) {
+ possibleTiles.add(spec);
+ }
+ }
+ if (Build.IS_DEBUGGABLE && !current.contains(GarbageMonitor.MemoryTile.TILE_SPEC)) {
possibleTiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
}
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
for (String spec : possibleTiles) {
+ // Only add current and stock tiles that can be created from QSFactoryImpl
final QSTile tile = host.createTile(spec);
if (tile == null) {
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index b2f6043..7d52f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -114,7 +114,7 @@
}
// Broken tiles.
- Log.w(TAG, "Bad tile spec: " + tileSpec);
+ Log.w(TAG, "No stock tile spec: " + tileSpec);
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 0638998..3a96595d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -198,7 +198,8 @@
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
- mAccessibilityClass = state.expandedAccessibilityClassName;
+ mAccessibilityClass =
+ state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
boolean newState = ((BooleanState) state).value;
if (mTileState != newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index c62a592..3ab1c21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -205,7 +205,10 @@
} else {
final BluetoothClass bluetoothClass = lastDevice.getBtClass();
if (bluetoothClass != null) {
- if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+ if (lastDevice.isHearingAidDevice()) {
+ return mContext.getString(
+ R.string.quick_settings_bluetooth_secondary_label_hearing_aids);
+ } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
return mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_audio);
} else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1b89324..12b6f67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -43,6 +43,7 @@
import android.util.Log;
import android.view.MotionEvent;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
@@ -97,6 +98,7 @@
private int mCurrentBoundedUserId = -1;
private float mBackButtonAlpha;
private MotionEvent mStatusBarGestureDownEvent;
+ private float mWindowCornerRadius;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -228,6 +230,18 @@
}
}
+ public float getWindowCornerRadius() {
+ if (!verifyCaller("getWindowCornerRadius")) {
+ return 0;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ return mWindowCornerRadius;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -334,6 +348,7 @@
.setPackage(mRecentsComponentName.getPackageName());
mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
getDefaultInteractionFlags());
+ mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
// Listen for the package update changes.
if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 216b940..f796793 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -219,7 +219,7 @@
mLayout.findViewById(R.id.screen_pinning_text_area)
.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
- if (WindowManagerWrapper.getInstance().hasSoftNavigationBar()) {
+ if (WindowManagerWrapper.getInstance().hasSoftNavigationBar(mContext.getDisplayId())) {
buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
swapChildrenIfRtlAndVertical(buttons);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index a5e7f04..8821679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -33,7 +33,7 @@
* dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly
* before automatically dismissing the alert.
*/
-public final class AmbientPulseManager extends AlertingNotificationManager {
+public class AmbientPulseManager extends AlertingNotificationManager {
protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 0c8f487..b550274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -21,7 +21,7 @@
import android.app.StatusBarManager;
import android.content.ComponentName;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -96,6 +96,7 @@
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_TRY_AGAIN = 47 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -163,12 +164,13 @@
default void onRotationProposal(int rotation, boolean isValid) { }
- default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver,
+ default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int type, boolean requireConfirmation, int userId) { }
default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
default void onBiometricError(String error) { }
default void hideBiometricDialog() { }
+ default void showBiometricTryAgain() { }
}
@VisibleForTesting
@@ -221,7 +223,9 @@
}
}
- public void disable(int state1, int state2) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void disable(int displayId, int state1, int state2) {
disable(state1, state2, true);
}
@@ -264,8 +268,10 @@
}
}
- public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
- int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
synchronized (mLock) {
// Don't coalesce these, since it might have one time flags set such as
// STATUS_BAR_UNHIDE which might get lost.
@@ -280,7 +286,9 @@
}
}
- public void topAppWindowChanged(boolean menuVisible) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void topAppWindowChanged(int displayId, boolean menuVisible) {
synchronized (mLock) {
mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
@@ -288,7 +296,9 @@
}
}
- public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
@@ -369,7 +379,9 @@
}
}
- public void setWindowState(int window, int state) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void setWindowState(int displayId, int window, int state) {
synchronized (mLock) {
// don't coalesce these
mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
@@ -383,7 +395,9 @@
}
}
- public void appTransitionPending() {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void appTransitionPending(int displayId) {
appTransitionPending(false /* forced */);
}
@@ -393,13 +407,17 @@
}
}
- public void appTransitionCancelled() {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void appTransitionCancelled(int displayId) {
synchronized (mLock) {
mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
}
}
- public void appTransitionStarting(long startTime, long duration) {
+ // TODO(b/117478341): Add multi-display support.
+ @Override
+ public void appTransitionStarting(int displayId, long startTime, long duration) {
appTransitionStarting(startTime, duration, false /* forced */);
}
@@ -410,8 +428,9 @@
}
}
+ // TODO(b/117478341): Add multi-display support.
@Override
- public void appTransitionFinished() {
+ public void appTransitionFinished(int displayId) {
synchronized (mLock) {
mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
}
@@ -523,8 +542,8 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -565,6 +584,13 @@
}
}
+ @Override
+ public void showBiometricTryAgain() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_BIOMETRIC_TRY_AGAIN).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -774,7 +800,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).showBiometricDialog(
(Bundle) someArgs.arg1,
- (IBiometricPromptReceiver) someArgs.arg2,
+ (IBiometricServiceReceiverInternal) someArgs.arg2,
someArgs.argi1 /* type */,
(boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */);
@@ -816,6 +842,11 @@
mCallbacks.get(i).showPinningEscapeToast();
}
break;
+ case MSG_BIOMETRIC_TRY_AGAIN:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showBiometricTryAgain();
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
index 3b611a31..2f26109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
@@ -22,8 +22,11 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
+import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManagerGlobal;
@@ -34,6 +37,8 @@
*/
public class DisplayNavigationBarController implements DisplayListener {
+ private static final String TAG = DisplayNavigationBarController.class.getName();
+
private final Context mContext;
private final Handler mHandler;
private final DisplayManager mDisplayManager;
@@ -112,14 +117,23 @@
}
final int displayId = display.getDisplayId();
+ final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+ try {
+ if (!wms.hasNavigationBar(displayId)) {
+ return;
+ }
+ } catch (RemoteException e) {
+ // Cannot get wms, just return with warning message.
+ Log.w(TAG, "Cannot get WindowManager.");
+ return;
+ }
final Context externalDisplayContext = mContext.createDisplayContext(display);
- NavigationBarFragment.create(externalDisplayContext,
- (tag, fragment) -> {
- final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
- // TODO(b/115978725): handle external nav bars sysuiVisibility
- navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
- mExternalNavigationBarMap.append(displayId, navBar);
- }
- );
+ NavigationBarFragment.create(externalDisplayContext, (tag, fragment) -> {
+ final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
+ // TODO(b/115978725): handle external nav bars sysuiVisibility
+ navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ mExternalNavigationBarMap.append(displayId, navBar);
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 745b2f9..b9684fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -27,7 +27,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
public class EmptyShadeView extends StackScrollerDecorView {
@@ -74,7 +73,7 @@
}
@Override
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+ public ExpandableViewState createExpandableViewState() {
return new EmptyShadeViewState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 960d221..7d1b640 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -156,7 +156,7 @@
new IntentFilter(Intent.ACTION_TIME_TICK), null,
Dependency.get(Dependency.TIME_TICK_HANDLER));
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
/**
@@ -167,7 +167,7 @@
*/
public void destroy() {
mContext.unregisterReceiver(mTickReceiver);
- Dependency.get(StatusBarStateController.class).removeListener(this);
+ Dependency.get(StatusBarStateController.class).removeCallback(this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index b0724b1..bba4369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -165,7 +165,7 @@
mCurrentUserId = ActivityManager.getCurrentUser();
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
mLockPatternUtils = new LockPatternUtils(context);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 2ee5443..ecadf96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -141,7 +141,7 @@
ActivityManager.getService().resumeAppSwitches();
} catch (RemoteException e) {
}
- return mCallback.handleRemoteViewClick(pendingIntent, () -> {
+ return mCallback.handleRemoteViewClick(view, pendingIntent, () -> {
Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
options.second.setLaunchWindowingMode(
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
@@ -150,34 +150,42 @@
}
private void logActionClick(View view) {
+ Integer actionIndex = (Integer)
+ view.getTag(com.android.internal.R.id.notification_action_index_tag);
+ if (actionIndex == null) {
+ Log.e(TAG, "Couldn't retrieve the actionIndex from the clicked button");
+ return;
+ }
ViewParent parent = view.getParent();
- String key = getNotificationKeyForParent(parent);
- if (key == null) {
+ StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+ if (statusBarNotification == null) {
Log.w(TAG, "Couldn't determine notification for click.");
return;
}
- int index = -1;
+ String key = statusBarNotification.getKey();
+ int buttonIndex = -1;
// If this is a default template, determine the index of the button.
if (view.getId() == com.android.internal.R.id.action0 &&
parent != null && parent instanceof ViewGroup) {
ViewGroup actionGroup = (ViewGroup) parent;
- index = actionGroup.indexOfChild(view);
+ buttonIndex = actionGroup.indexOfChild(view);
}
final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
final int rank = mEntryManager.getNotificationData().getRank(key);
+ final Notification.Action action =
+ statusBarNotification.getNotification().actions[actionIndex];
final NotificationVisibility nv = NotificationVisibility.obtain(key, rank, count, true);
try {
- mBarService.onNotificationActionClick(key, index, nv);
+ mBarService.onNotificationActionClick(key, buttonIndex, action, nv, false);
} catch (RemoteException e) {
// Ignore
}
}
- private String getNotificationKeyForParent(ViewParent parent) {
+ private StatusBarNotification getNotificationForParent(ViewParent parent) {
while (parent != null) {
if (parent instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) parent)
- .getStatusBarNotification().getKey();
+ return ((ExpandableNotificationRow) parent).getStatusBarNotification();
}
parent = parent.getParent();
}
@@ -658,11 +666,13 @@
* Performs any special handling for a remote view click. The default behaviour can be
* called through the defaultHandler parameter.
*
+ * @param view
* @param pendingIntent
* @param defaultHandler
* @return true iff the click was handled
*/
- boolean handleRemoteViewClick(PendingIntent pendingIntent, ClickHandler defaultHandler);
+ boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
+ ClickHandler defaultHandler);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 37cc299..6cec36a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -47,7 +47,6 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -68,7 +67,6 @@
private boolean mDark;
private NotificationIconContainer mShelfIcons;
- private ShelfState mShelfState;
private int[] mTmp = new int[2];
private boolean mHideBackground;
private int mIconAppearTopPadding;
@@ -115,7 +113,6 @@
setClipChildren(false);
setClipToPadding(false);
mShelfIcons.setIsStaticLayout(false);
- mShelfState = new ShelfState();
setBottomRoundness(1.0f, false /* animate */);
initDimens();
}
@@ -124,13 +121,13 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(StatusBarStateController.class)
- .addListener(mStateListener, StatusBarStateController.RANK_SHELF);
+ .addCallback(mStateListener, StatusBarStateController.RANK_SHELF);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
}
public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
@@ -187,52 +184,53 @@
}
@Override
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
- return mShelfState;
+ public ExpandableViewState createExpandableViewState() {
+ return new ShelfState();
}
- public void updateState(StackScrollState resultState,
- AmbientState ambientState) {
- View lastView = ambientState.getLastVisibleBackgroundChild();
+ /** Update the state of the shelf. */
+ public void updateState(AmbientState ambientState) {
+ ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
+ ShelfState viewState = (ShelfState) getViewState();
if (mShowNotificationShelf && lastView != null) {
float maxShelfEnd = ambientState.getInnerHeight() + ambientState.getTopPadding()
+ ambientState.getStackTranslation();
- ExpandableViewState lastViewState = resultState.getViewStateForView(lastView);
+ ExpandableViewState lastViewState = lastView.getViewState();
float viewEnd = lastViewState.yTranslation + lastViewState.height;
- mShelfState.copyFrom(lastViewState);
- mShelfState.height = getIntrinsicHeight();
+ viewState.copyFrom(lastViewState);
+ viewState.height = getIntrinsicHeight();
- float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
+ float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
getFullyClosedTranslation());
float darkTranslation = mAmbientState.getDarkTopPadding();
float yRatio = mAmbientState.hasPulsingNotifications() ?
0 : mAmbientState.getDarkAmount();
- mShelfState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
- mShelfState.zTranslation = ambientState.getBaseZHeight();
+ viewState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
+ viewState.zTranslation = ambientState.getBaseZHeight();
// For the small display size, it's not enough to make the icon not covered by
// the top cutout so the denominator add the height of cutout.
// Totally, (getIntrinsicHeight() * 2 + mCutoutHeight) should be smaller then
// mAmbientState.getTopPadding().
- float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
+ float openedAmount = (viewState.yTranslation - getFullyClosedTranslation())
/ (getIntrinsicHeight() * 2 + mCutoutHeight);
openedAmount = Math.min(1.0f, openedAmount);
- mShelfState.openedAmount = openedAmount;
- mShelfState.clipTopAmount = 0;
- mShelfState.alpha = mAmbientState.hasPulsingNotifications() ? 0 : 1;
- mShelfState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
- mShelfState.hideSensitive = false;
- mShelfState.xTranslation = getTranslationX();
+ viewState.openedAmount = openedAmount;
+ viewState.clipTopAmount = 0;
+ viewState.alpha = mAmbientState.hasPulsingNotifications() ? 0 : 1;
+ viewState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
+ viewState.hideSensitive = false;
+ viewState.xTranslation = getTranslationX();
if (mNotGoneIndex != -1) {
- mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex);
+ viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex);
}
- mShelfState.hasItemsInStableShelf = lastViewState.inShelf;
- mShelfState.hidden = !mAmbientState.isShadeExpanded()
- || mAmbientState.isQsCustomizerShowing();
- mShelfState.maxShelfEnd = maxShelfEnd;
+ viewState.hasItemsInStableShelf = lastViewState.inShelf;
+ viewState.hidden = !mAmbientState.isShadeExpanded()
+ || mAmbientState.isQsCustomizerShowing() || mAmbientState.isFullyDark();
+ viewState.maxShelfEnd = maxShelfEnd;
} else {
- mShelfState.hidden = true;
- mShelfState.location = ExpandableViewState.LOCATION_GONE;
- mShelfState.hasItemsInStableShelf = false;
+ viewState.hidden = true;
+ viewState.location = ExpandableViewState.LOCATION_GONE;
+ viewState.hasItemsInStableShelf = false;
}
}
@@ -261,7 +259,7 @@
int notGoneIndex = 0;
int colorOfViewBeforeLast = NO_COLOR;
boolean backgroundForceHidden = false;
- if (mHideBackground && !mShelfState.hasItemsInStableShelf) {
+ if (mHideBackground && !((ShelfState) getViewState()).hasItemsInStableShelf) {
backgroundForceHidden = true;
}
int colorTwoBefore = NO_COLOR;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index daa2fd4..dc3a607 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -129,7 +129,7 @@
res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
- Dependency.get(StatusBarStateController.class).addListener(mStatusBarStateListener);
+ Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
}
public void setUpWithPresenter(NotificationPresenter presenter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 37bdc1c..f5d6904 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -15,12 +15,15 @@
*/
package com.android.systemui.statusbar;
+import android.app.Notification;
import android.os.RemoteException;
import android.util.ArraySet;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.Set;
@@ -32,6 +35,9 @@
private IStatusBarService mBarService;
private Set<String> mSendingKeys = new ArraySet<>();
private Callback mCallback;
+ private final NotificationEntryManager mEntryManager =
+ Dependency.get(NotificationEntryManager.class);
+
public SmartReplyController() {
mBarService = Dependency.get(IStatusBarService.class);
@@ -57,6 +63,24 @@
}
/**
+ * Notifies StatusBarService a smart action is clicked.
+ */
+ public void smartActionClicked(
+ NotificationData.Entry entry, int actionIndex, Notification.Action action,
+ boolean generatedByAssistant) {
+ final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
+ final int rank = mEntryManager.getNotificationData().getRank(entry.key);
+ final NotificationVisibility nv =
+ NotificationVisibility.obtain(entry.key, rank, count, true);
+ try {
+ mBarService.onNotificationActionClick(
+ entry.key, actionIndex, action, nv, generatedByAssistant);
+ } catch (RemoteException e) {
+ // Nothing to do, system going down
+ }
+ }
+
+ /**
* Have we posted an intent to an app about sending a smart reply from the
* notification with this key.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index eaf52cb..3f84416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -26,8 +26,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.CallbackController;
import java.lang.annotation.Retention;
import java.util.ArrayList;
@@ -36,7 +38,7 @@
/**
* Tracks and reports on {@link StatusBarState}.
*/
-public class StatusBarStateController {
+public class StatusBarStateController implements CallbackController<StateListener> {
private static final String TAG = "SbStateController";
private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
@@ -228,7 +230,7 @@
return mLastState == StatusBarState.SHADE_LOCKED;
}
- public void addListener(StateListener listener) {
+ public void addCallback(StateListener listener) {
synchronized (mListeners) {
addListenerInternalLocked(listener, Integer.MAX_VALUE);
}
@@ -244,7 +246,7 @@
* StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
* (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
*/
- public void addListener(StateListener listener, @SbStateListenerRank int rank) {
+ public void addCallback(StateListener listener, @SbStateListenerRank int rank) {
synchronized (mListeners) {
addListenerInternalLocked(listener, rank);
}
@@ -264,7 +266,7 @@
mListeners.sort(mComparator);
}
- public void removeListener(StateListener listener) {
+ public void removeCallback(StateListener listener) {
synchronized (mListeners) {
mListeners.removeIf((it) -> it.listener.equals(listener));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index f0e5462..f899863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -29,6 +29,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
@@ -55,6 +56,7 @@
private final NotificationPanelView mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
private final StatusBarWindowView mStatusBarWindow;
+ private final float mWindowCornerRadius;
private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
@@ -72,6 +74,8 @@
mNotificationContainer = container;
mStatusBarWindow = statusBarWindow;
mCallback = callback;
+ mWindowCornerRadius = ScreenDecorationsUtils
+ .getWindowCornerRadius(statusBarWindow.getResources());
}
public RemoteAnimationAdapter getLaunchAnimation(
@@ -124,6 +128,8 @@
private final ExpandableNotificationRow mSourceNotification;
private final ExpandAnimationParameters mParams;
private final Rect mWindowCrop = new Rect();
+ private final float mNotificationCornerRadius;
+ private float mCornerRadius;
private boolean mIsFullScreenLaunch = true;
private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
@@ -131,6 +137,8 @@
mSourceNotification = sourceNofitication;
mParams = new ExpandAnimationParameters();
mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
+ mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),
+ mSourceNotification.getCurrentBottomRoundness());
}
@Override
@@ -181,8 +189,7 @@
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mParams.linearProgress = animation.getAnimatedFraction();
- float progress
- = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
mParams.linearProgress);
int newWidth = (int) MathUtils.lerp(notificationWidth,
targetWidth, progress);
@@ -194,6 +201,8 @@
+ notificationHeight,
primary.position.y + primary.sourceContainerBounds.bottom,
progress);
+ mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,
+ mWindowCornerRadius, progress);
applyParamsToWindow(primary);
applyParamsToNotification(mParams);
applyParamsToNotificationList(mParams);
@@ -259,7 +268,7 @@
m.postTranslate(0, (float) (mParams.top - app.position.y));
mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
SurfaceParams params = new SurfaceParams(new SurfaceControlCompat(app.leash),
- 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex);
+ 1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex, mCornerRadius);
mSyncRtTransactionApplier.scheduleApply(params);
}
@@ -290,6 +299,10 @@
return top;
}
+ public int getBottom() {
+ return bottom;
+ }
+
public int getWidth() {
return right - left;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 314a31d..0a2e04f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -250,23 +250,24 @@
otherChild = null;
}
}
- if (otherChild == null) {
+ if (otherChild == null && previousTranslation < 0) {
+ // Let's fade out as we approach the top of the screen. We can only do this if
+ // we're actually moving up
float distanceToTop = child.getTop() + child.getHeight() + previousTranslation;
transformationAmount = distanceToTop / child.getHeight();
transformationAmount = Math.max(0.0f, Math.min(1.0f, transformationAmount));
- if (to) {
- transformationAmount = 1.0f - transformationAmount;
- }
}
transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */
useLinearTransformation);
- if (transformationAmount == 0.0f
- && otherGroup.getIsolatedMessage() == otherChild) {
+ boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild;
+ if (transformationAmount == 0.0f && otherIsIsolated) {
ownGroup.setTransformingImages(true);
}
if (otherChild == null) {
child.setTranslationY(previousTranslation);
setClippingDeactivated(child, true);
+ } else if (ownGroup.getIsolatedMessage() == child || otherIsIsolated) {
+ // We don't want to add any translation for the image that is transforming
} else if (to) {
float totalTranslation = child.getTop() + ownGroup.getTop()
- otherChild.getTop() - otherChild.getTop();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index d7680b3..3f8583c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -66,7 +66,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -174,7 +173,6 @@
public void populateFromRanking(@NonNull Ranking ranking) {
channel = ranking.getChannel();
audiblyAlerted = ranking.audiblyAlerted();
- noisy = ranking.isNoisy();
importance = ranking.getImportance();
snoozeCriteria = ranking.getSnoozeCriteria();
userSentiment = ranking.getUserSentiment();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index e333729..70d144e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,7 +24,6 @@
import android.annotation.Nullable;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
@@ -177,9 +176,12 @@
}
// Check if the notification is displaying the menu, if so slide notification back
- if (row.getProvider() != null && row.getProvider().isMenuVisible()) {
+ if (isMenuVisible(row)) {
row.animateTranslateNotification(0);
return;
+ } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
+ row.getNotificationParent().animateTranslateNotification(0);
+ return;
}
// Mark notification for one frame.
@@ -194,6 +196,10 @@
mCallback.onNotificationClicked(sbn, row);
}
+ private boolean isMenuVisible(ExpandableNotificationRow row) {
+ return row.getProvider() != null && row.getProvider().isMenuVisible();
+ }
+
public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
if (notification.contentIntent != null || notification.fullScreenIntent != null) {
@@ -766,7 +772,7 @@
}
NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
- if (shouldAutoBubble(entry)) {
+ if (BubbleController.shouldAutoBubble(getContext(), entry)) {
entry.setIsBubble(true);
}
@@ -946,10 +952,12 @@
// Has a copy of the current UI adjustments.
ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
+ ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
for (NotificationData.Entry entry : entries) {
NotificationUiAdjustment adjustment =
NotificationUiAdjustment.extractFromNotificationEntry(entry);
oldAdjustments.put(entry.key, adjustment);
+ oldImportances.put(entry.key, entry.importance);
}
// Populate notification entries from the new rankings.
@@ -972,6 +980,11 @@
// Once the RowInflaterTask is done, it will pick up the updated entry, so
// no-op here.
}
+ } else if (oldImportances.containsKey(entry.key)
+ && entry.importance != oldImportances.get(entry.key)) {
+ if (entry.rowExists()) {
+ entry.getRow().onNotificationRankingUpdated();
+ }
}
}
@@ -1207,17 +1220,6 @@
}
}
-
- /**
- * Whether a bubble is appropriate to auto-bubble or not.
- */
- private boolean shouldAutoBubble(NotificationData.Entry entry) {
- int priority = mNotificationData.getImportance(entry.key);
- NotificationChannel channel = mNotificationData.getChannel(entry.key);
- boolean canAppOverlay = channel != null && channel.canOverlayApps();
- return BubbleController.shouldAutoBubble(entry, priority, canAppOverlay);
- }
-
/**
* Callback for NotificationEntryManager.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 1f48c15..09eb8a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -75,6 +75,6 @@
/** Returns the value of the new interruption model setting. */
public static boolean useNewInterruptionModel(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) != 0;
+ NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) != 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 247c1ab..a194eef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,15 +16,13 @@
package com.android.systemui.statusbar.notification;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
/**
* An object that can determine the visibility of a Notification.
*/
public interface VisibilityLocationProvider {
/**
- * Returns whether an ExpandableNotificationRow is in a visible location or not.
+ * Returns whether an Entry is in a visible location or not.
*
* @param entry
* @return true if row is in a visible location
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 87313b8..9f02e54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -149,7 +149,7 @@
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
// Not expected to be destroyed, don't need to unsubscribe
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
public void setUpWithContainer(NotificationListContainer listContainer) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 23bfdad..69e698f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,22 +17,14 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
- .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
- .VISIBLE_TYPE_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
- .VISIBLE_TYPE_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
- .VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
- .InflationCallback;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -105,7 +97,6 @@
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -337,7 +328,6 @@
private float mTranslationWhenRemoved;
private boolean mWasChildInGroupWhenRemoved;
private int mNotificationColorAmbient;
- private NotificationViewState mNotificationViewState;
private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
new SystemNotificationAsyncTask();
@@ -598,6 +588,13 @@
updateRippleAllowed();
}
+ /** Called when the notification's ranking was changed (but nothing else changed). */
+ public void onNotificationRankingUpdated() {
+ if (mMenuRow != null) {
+ mMenuRow.onNotificationUpdated(mStatusBarNotification);
+ }
+ }
+
@VisibleForTesting
void updateShelfIconColor() {
StatusBarIconView expandedIcon = mEntry.expandedIcon;
@@ -894,29 +891,32 @@
visualStabilityManager, callback);
}
- public void getChildrenStates(StackScrollState resultState,
- AmbientState ambientState) {
+ /** Updates states of all children. */
+ public void updateChildrenStates(AmbientState ambientState) {
if (mIsSummaryWithChildren) {
- ExpandableViewState parentState = resultState.getViewStateForView(this);
- mChildrenContainer.getState(resultState, parentState, ambientState);
+ ExpandableViewState parentState = getViewState();
+ mChildrenContainer.updateState(parentState, ambientState);
}
}
- public void applyChildrenState(StackScrollState state) {
+ /** Applies children states. */
+ public void applyChildrenState() {
if (mIsSummaryWithChildren) {
- mChildrenContainer.applyState(state);
+ mChildrenContainer.applyState();
}
}
- public void prepareExpansionChanged(StackScrollState state) {
+ /** Prepares expansion changed. */
+ public void prepareExpansionChanged() {
if (mIsSummaryWithChildren) {
- mChildrenContainer.prepareExpansionChanged(state);
+ mChildrenContainer.prepareExpansionChanged();
}
}
- public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
+ /** Starts child animations. */
+ public void startChildAnimation(AnimationProperties properties) {
if (mIsSummaryWithChildren) {
- mChildrenContainer.startAnimationToState(finalState, properties);
+ mChildrenContainer.startAnimationToState(properties);
}
}
@@ -2008,7 +2008,8 @@
float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
int startClipTopAmount = params.getStartClipTopAmount();
if (mNotificationParent != null) {
- top -= mNotificationParent.getTranslationY();
+ float parentY = mNotificationParent.getTranslationY();
+ top -= parentY;
mNotificationParent.setTranslationZ(translationZ);
int parentStartClipTopAmount = params.getParentStartClipTopAmount();
if (startClipTopAmount != 0) {
@@ -2018,8 +2019,12 @@
mNotificationParent.setClipTopAmount(clipTopAmount);
}
mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
- mNotificationParent.setMinimumHeightForClipping(params.getHeight()
- + mNotificationParent.getActualHeight());
+ float clipBottom = Math.max(params.getBottom(),
+ parentY + mNotificationParent.getActualHeight()
+ - mNotificationParent.getClipBottomAmount());
+ float clipTop = Math.min(params.getTop(), parentY);
+ int minimumHeightForClipping = (int) (clipBottom - clipTop);
+ mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping);
} else if (startClipTopAmount != 0) {
int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
setClipTopAmount(clipTopAmount);
@@ -2047,7 +2052,7 @@
.setInterpolator(Interpolators.ALPHA_OUT);
setAboveShelf(true);
mExpandAnimationRunning = true;
- mNotificationViewState.cancelAnimations(this);
+ getViewState().cancelAnimations(this);
mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext());
} else {
mExpandAnimationRunning = false;
@@ -2074,6 +2079,8 @@
private void setChildIsExpanding(boolean isExpanding) {
mChildIsExpanding = isExpanding;
+ updateClipping();
+ invalidate();
}
@Override
@@ -2924,13 +2931,8 @@
}
@Override
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
- mNotificationViewState = new NotificationViewState(stackScrollState);
- return mNotificationViewState;
- }
-
- public NotificationViewState getViewState() {
- return mNotificationViewState;
+ public ExpandableViewState createExpandableViewState() {
+ return new NotificationViewState();
}
@Override
@@ -2980,7 +2982,7 @@
return true;
}
} else if (child == mChildrenContainer) {
- if (!mChildIsExpanding && (isClippingNeeded() || !hasNoRounding())) {
+ if (isClippingNeeded() || !hasNoRounding()) {
return true;
}
} else if (child instanceof NotificationGuts) {
@@ -3040,14 +3042,7 @@
}
}
- public static class NotificationViewState extends ExpandableViewState {
-
- private final StackScrollState mOverallState;
-
-
- private NotificationViewState(StackScrollState stackScrollState) {
- mOverallState = stackScrollState;
- }
+ private static class NotificationViewState extends ExpandableViewState {
@Override
public void applyToView(View view) {
@@ -3058,7 +3053,7 @@
}
handleFixedTranslationZ(row);
super.applyToView(view);
- row.applyChildrenState(mOverallState);
+ row.applyChildrenState();
}
}
@@ -3089,7 +3084,7 @@
}
handleFixedTranslationZ(row);
super.animateTo(child, properties);
- row.startChildAnimation(mOverallState, properties);
+ row.startChildAnimation(properties);
}
}
}
@@ -3144,8 +3139,8 @@
pw.println();
showingLayout.dump(fd, pw, args);
pw.print(" ");
- if (mNotificationViewState != null) {
- mNotificationViewState.dump(fd, pw, args);
+ if (getViewState() != null) {
+ getViewState().dump(fd, pw, args);
} else {
pw.print("no viewState!!!");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index a7aed5f..0efb130 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -115,12 +115,14 @@
if (!mCustomOutline) {
int translation = mShouldTranslateContents && !ignoreTranslation
? (int) getTranslation() : 0;
- left = Math.max(translation, 0);
+ int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
+ left = Math.max(translation, 0) - halfExtraWidth;
top = mClipTopAmount + mBackgroundTop;
- right = getWidth() + Math.min(translation, 0);
+ right = getWidth() + halfExtraWidth + Math.min(translation, 0);
// If the top is rounded we want the bottom to be at most at the top roundness, in order
// to avoid the shadow changing when scrolling up.
- bottom = Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness));
+ bottom = Math.max(mMinimumHeightForClipping,
+ Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness)));
} else {
left = mOutlineRect.left;
top = mOutlineRect.top;
@@ -219,10 +221,12 @@
public void setExtraWidthForClipping(float extraWidthForClipping) {
mExtraWidthForClipping = extraWidthForClipping;
+ invalidate();
}
public void setMinimumHeightForClipping(int minimumHeightForClipping) {
mMinimumHeightForClipping = minimumHeightForClipping;
+ invalidate();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 0589e3f..1e8de07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -21,23 +21,27 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
+
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
/**
* An abstract view for expandable views.
*/
public abstract class ExpandableView extends FrameLayout implements Dumpable {
+ private static final String TAG = "ExpandableView";
public static final float NO_ROUNDNESS = -1;
protected OnHeightChangedListener mOnHeightChangedListener;
@@ -54,6 +58,7 @@
private ViewGroup mTransientContainer;
private boolean mInShelf;
private boolean mTransformingInShelf;
+ @Nullable private ExpandableViewState mViewState;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -511,10 +516,56 @@
public void setActualHeightAnimating(boolean animating) {}
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+ protected ExpandableViewState createExpandableViewState() {
return new ExpandableViewState();
}
+ /** Sets {@link ExpandableViewState} to default state. */
+ public ExpandableViewState resetViewState() {
+ // TODO(http://b/119762423): Move the null check to getViewState().
+ if (mViewState == null) {
+ mViewState = createExpandableViewState();
+ }
+
+ // initialize with the default values of the view
+ mViewState.height = getIntrinsicHeight();
+ mViewState.gone = getVisibility() == View.GONE;
+ mViewState.alpha = 1f;
+ mViewState.notGoneIndex = -1;
+ mViewState.xTranslation = getTranslationX();
+ mViewState.hidden = false;
+ mViewState.scaleX = getScaleX();
+ mViewState.scaleY = getScaleY();
+ mViewState.inShelf = false;
+ mViewState.headsUpIsVisible = false;
+
+ // handling reset for child notifications
+ if (this instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) this;
+ List<ExpandableNotificationRow> children = row.getNotificationChildren();
+ if (row.isSummaryWithChildren() && children != null) {
+ for (ExpandableNotificationRow childRow : children) {
+ childRow.resetViewState();
+ }
+ }
+ }
+
+ return mViewState;
+ }
+
+ @Nullable public ExpandableViewState getViewState() {
+ return mViewState;
+ }
+
+ /** Applies internal {@link ExpandableViewState} to this view. */
+ public void applyViewState() {
+ if (mViewState == null) {
+ Log.wtf(TAG, "No child state was found when applying this state to the hostView");
+ } else if (!mViewState.gone) {
+ mViewState.applyToView(this);
+ }
+ }
+
/**
* @return whether the current view doesn't add height to the overall content. This means that
* if it is added to a list of items, it's content will still have the same height.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 1f15ed0..311bf7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -24,7 +24,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
public class FooterView extends StackScrollerDecorView {
private final int mClearAllTopPadding;
@@ -87,7 +86,7 @@
}
@Override
- public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+ public ExpandableViewState createExpandableViewState() {
return new FooterViewState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index a4fdc08..689d6d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -57,7 +57,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Collections;
import java.util.List;
/**
@@ -493,12 +492,12 @@
if (child == null) {
mExpandedChild = null;
mExpandedWrapper = null;
- if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
- mVisibleType = VISIBLE_TYPE_CONTRACTED;
- }
if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
mTransformationStartVisibleType = UNDEFINED;
}
+ if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
+ selectLayout(false /* animate */, true /* force */);
+ }
return;
}
addView(child);
@@ -531,12 +530,12 @@
if (child == null) {
mHeadsUpChild = null;
mHeadsUpWrapper = null;
- if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
- mVisibleType = VISIBLE_TYPE_CONTRACTED;
- }
if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
mTransformationStartVisibleType = UNDEFINED;
}
+ if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
+ selectLayout(false /* animate */, true /* force */);
+ }
return;
}
addView(child);
@@ -558,12 +557,12 @@
if (child == null) {
mAmbientChild = null;
mAmbientWrapper = null;
- if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
- mVisibleType = VISIBLE_TYPE_CONTRACTED;
- }
if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
mTransformationStartVisibleType = UNDEFINED;
}
+ if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
+ selectLayout(false /* animate */, true /* force */);
+ }
return;
}
addView(child);
@@ -1520,7 +1519,8 @@
smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
}
if (smartRepliesAndActions.smartActions != null) {
- smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
+ smartReplyView.addSmartActions(
+ smartRepliesAndActions.smartActions, mSmartReplyController, entry);
}
smartReplyContainer.setVisibility(View.VISIBLE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 7895a8e..3dc50ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -18,10 +18,7 @@
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
-
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import android.app.INotificationManager;
import android.app.NotificationChannel;
@@ -43,7 +40,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.plugins.ActivityStarter;
@@ -188,13 +184,7 @@
} else if (gutsView instanceof AppOpsInfo) {
initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
} else if (gutsView instanceof NotificationInfo) {
- int action;
- if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) {
- action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction;
- } else {
- action = ACTION_NONE;
- }
- initializeNotificationInfo(row, (NotificationInfo) gutsView, action);
+ initializeNotificationInfo(row, (NotificationInfo) gutsView);
}
return true;
} catch (Exception e) {
@@ -253,13 +243,11 @@
* Sets up the {@link NotificationInfo} inside the notification row's guts.
* @param row view to set up the guts for
* @param notificationInfoView view to set up/bind within {@code row}
- * @param action The action to take immediately upon binding, if any.
*/
@VisibleForTesting
void initializeNotificationInfo(
final ExpandableNotificationRow row,
- NotificationInfo notificationInfoView,
- @NotificationInfo.NotificationInfoAction int action) throws Exception {
+ NotificationInfo notificationInfoView) throws Exception {
NotificationGuts guts = row.getGuts();
StatusBarNotification sbn = row.getStatusBarNotification();
String packageName = sbn.getPackageName();
@@ -302,9 +290,7 @@
row.getIsNonblockable(),
isForBlockingHelper,
row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
- row.getEntry().noisy,
- row.getEntry().importance,
- action);
+ row.getEntry().importance);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 70860258..ef343fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -697,7 +697,7 @@
&& newView.getPackage() != null
&& newView.getPackage().equals(oldView.getPackage())
&& newView.getLayoutId() == oldView.getLayoutId()
- && !oldView.isReapplyDisallowed());
+ && !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED));
}
public void setInflationCallback(InflationCallback callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 0d36d2c..5329541 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -104,7 +104,6 @@
private AnimatorSet mExpandAnimation;
private boolean mIsForeground;
private boolean mIsDeviceProvisioned;
- private boolean mIsNoisy;
private CheckSaveListener mCheckSaveListener;
private OnSettingsClickListener mOnSettingsClickListener;
@@ -186,15 +185,13 @@
final OnAppSettingsClickListener onAppSettingsClick,
boolean isDeviceProvisioned,
boolean isNonblockable,
- boolean isNoisy,
- int importance,
- @NotificationInfoAction int action)
+ int importance)
throws RemoteException {
bindNotification(pm, iNotificationManager, pkg, notificationChannel,
numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
- false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy,
- importance, action);
+ false /* isBlockingHelper */, false /* isUserSentimentNegative */,
+ importance);
}
public void bindNotification(
@@ -211,9 +208,7 @@
boolean isNonblockable,
boolean isForBlockingHelper,
boolean isUserSentimentNegative,
- boolean isNoisy,
- int importance,
- @NotificationInfoAction int action)
+ int importance)
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -238,7 +233,6 @@
mAppUid = mSbn.getUid();
mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
- mIsNoisy = isNoisy;
int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
pkg, mAppUid, false /* includeDeleted */);
@@ -255,10 +249,6 @@
bindHeader();
bindPrompt();
bindButtons();
-
- if (action != ACTION_NONE) {
- swapContent(action, false /* don't animate */);
- }
}
private void bindHeader() throws RemoteException {
@@ -417,54 +407,74 @@
}
private void bindButtons() {
- // Set up stay-in-notification actions
- View block = findViewById(R.id.block);
- TextView keep = findViewById(R.id.keep);
- TextView silent = findViewById(R.id.toggle_silent);
- View minimize = findViewById(R.id.minimize);
-
findViewById(R.id.undo).setOnClickListener(mOnUndo);
- block.setOnClickListener(mOnStopOrMinimizeNotifications);
- keep.setOnClickListener(mOnKeepShowing);
- silent.setOnClickListener(mOnToggleSilent);
- minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
- if (mIsNonblockable) {
- keep.setText(android.R.string.ok);
- block.setVisibility(GONE);
- silent.setVisibility(GONE);
- minimize.setVisibility(GONE);
- } else if (mIsForeground) {
- block.setVisibility(GONE);
- silent.setVisibility(GONE);
- minimize.setVisibility(VISIBLE);
- } else {
- block.setVisibility(VISIBLE);
- boolean showToggleSilent = mIsNoisy
- && NotificationUtils.useNewInterruptionModel(mContext);
- silent.setVisibility(showToggleSilent ? VISIBLE : GONE);
+ boolean showInterruptivenessSettings =
+ !mIsNonblockable
+ && !mIsForeground
+ && !mIsForBlockingHelper
+ && NotificationUtils.useNewInterruptionModel(mContext);
+ if (showInterruptivenessSettings) {
+ findViewById(R.id.block_or_minimize).setVisibility(GONE);
+ findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
+ View block = findViewById(R.id.int_block);
+ TextView silent = findViewById(R.id.int_silent);
+ TextView alert = findViewById(R.id.int_alert);
+
boolean isCurrentlyAlerting =
mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT;
- silent.setText(isCurrentlyAlerting
- ? R.string.inline_silent_button_silent
- : R.string.inline_silent_button_alert);
- minimize.setVisibility(GONE);
- }
- // Set up app settings link (i.e. Customize)
- TextView settingsLinkView = findViewById(R.id.app_settings);
- Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel,
- mSbn.getId(), mSbn.getTag());
- if (!mIsForBlockingHelper
- && settingsIntent != null
- && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
- settingsLinkView.setVisibility(VISIBLE);
- settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
- settingsLinkView.setOnClickListener((View view) -> {
- mAppSettingsClickListener.onClick(view, settingsIntent);
- });
+ block.setOnClickListener(mOnStopOrMinimizeNotifications);
+ if (isCurrentlyAlerting) {
+ silent.setOnClickListener(mOnToggleSilent);
+ silent.setText(R.string.inline_silent_button_silent);
+ alert.setOnClickListener(mOnKeepShowing);
+ alert.setText(R.string.inline_silent_button_keep_alerting);
+ } else {
+ silent.setOnClickListener(mOnKeepShowing);
+ silent.setText(R.string.inline_silent_button_stay_silent);
+ alert.setOnClickListener(mOnToggleSilent);
+ alert.setText(R.string.inline_silent_button_alert);
+ }
} else {
- settingsLinkView.setVisibility(View.GONE);
+ findViewById(R.id.block_or_minimize).setVisibility(VISIBLE);
+ findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
+ View block = findViewById(R.id.block);
+ TextView keep = findViewById(R.id.keep);
+ View minimize = findViewById(R.id.minimize);
+
+ block.setOnClickListener(mOnStopOrMinimizeNotifications);
+ keep.setOnClickListener(mOnKeepShowing);
+ minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
+
+ if (mIsNonblockable) {
+ keep.setText(android.R.string.ok);
+ block.setVisibility(GONE);
+ minimize.setVisibility(GONE);
+ } else if (mIsForeground) {
+ block.setVisibility(GONE);
+ minimize.setVisibility(VISIBLE);
+ } else {
+ block.setVisibility(VISIBLE);
+ minimize.setVisibility(GONE);
+ }
+
+ // Set up app settings link (i.e. Customize)
+ TextView settingsLinkView = findViewById(R.id.app_settings);
+ Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
+ mSingleNotificationChannel,
+ mSbn.getId(), mSbn.getTag());
+ if (!mIsForBlockingHelper
+ && settingsIntent != null
+ && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
+ settingsLinkView.setVisibility(VISIBLE);
+ settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
+ settingsLinkView.setOnClickListener((View view) -> {
+ mAppSettingsClickListener.onClick(view, settingsIntent);
+ });
+ } else {
+ settingsLinkView.setVisibility(View.GONE);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index b6ff6fc..50564e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -17,9 +17,6 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_BLOCK;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_TOGGLE_SILENT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -46,7 +43,6 @@
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
-import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
@@ -73,7 +69,7 @@
private Context mContext;
private FrameLayout mMenuContainer;
- private NotificationInfoMenuItem mInfoItem;
+ private NotificationMenuItem mInfoItem;
private MenuItem mAppOpsItem;
private MenuItem mSnoozeItem;
private ArrayList<MenuItem> mLeftMenuItems;
@@ -248,36 +244,32 @@
if (!isForeground) {
// Only show snooze for non-foreground notifications
mSnoozeItem = createSnoozeItem(mContext);
- mLeftMenuItems.add(mSnoozeItem);
}
- mInfoItem = createInfoItem(mContext);
- if (!NotificationUtils.useNewInterruptionModel(mContext)) {
- mLeftMenuItems.add(mInfoItem);
- }
-
mAppOpsItem = createAppOpsItem(mContext);
- mLeftMenuItems.add(mAppOpsItem);
-
if (NotificationUtils.useNewInterruptionModel(mContext)) {
- if (!mParent.getIsNonblockable()) {
- mRightMenuItems.add(createBlockItem(mContext, mInfoItem.getGutsView()));
- }
- // TODO(kprevas): this is duplicated logic
- // but it's currently spread across NotificationGutsManager and NotificationInfo.
- // Try to consolidate and reuse here.
- boolean canToggleSilent = !mParent.getIsNonblockable()
- && !isForeground
- && mParent.getEntry().noisy;
- if (canToggleSilent) {
- int channelImportance = mParent.getEntry().channel.getImportance();
- int effectiveImportance =
- channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
- ? mParent.getEntry().importance : channelImportance;
- mRightMenuItems.add(createToggleSilentItem(mContext, mInfoItem.getGutsView(),
- effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT));
- }
+ int channelImportance = mParent.getEntry().channel.getImportance();
+ int effectiveImportance =
+ channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
+ ? mParent.getEntry().importance : channelImportance;
+ mInfoItem = createInfoItem(mContext,
+ effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT);
} else {
- mRightMenuItems.addAll(mLeftMenuItems);
+ mInfoItem = createInfoItem(mContext);
+ }
+
+ if (!NotificationUtils.useNewInterruptionModel(mContext)) {
+ if (!isForeground) {
+ mRightMenuItems.add(mSnoozeItem);
+ }
+ mRightMenuItems.add(mInfoItem);
+ mRightMenuItems.add(mAppOpsItem);
+ mLeftMenuItems.addAll(mRightMenuItems);
+ } else {
+ mRightMenuItems.add(mInfoItem);
+ mRightMenuItems.add(mAppOpsItem);
+ if (!isForeground) {
+ mRightMenuItems.add(mSnoozeItem);
+ }
}
populateMenuViews();
@@ -634,13 +626,24 @@
return snooze;
}
- static NotificationInfoMenuItem createInfoItem(Context context) {
+ static NotificationMenuItem createInfoItem(Context context) {
Resources res = context.getResources();
String infoDescription = res.getString(R.string.notification_menu_gear_description);
NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
R.layout.notification_info, null, false);
- return new NotificationInfoMenuItem(context, infoDescription, infoContent,
- R.drawable.ic_settings, ACTION_NONE);
+ return new NotificationMenuItem(context, infoDescription, infoContent,
+ R.drawable.ic_settings);
+ }
+
+ static NotificationMenuItem createInfoItem(Context context, boolean isCurrentlySilent) {
+ Resources res = context.getResources();
+ String infoDescription = res.getString(R.string.notification_menu_gear_description);
+ NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
+ R.layout.notification_info, null, false);
+ int iconResId = isCurrentlySilent
+ ? R.drawable.ic_notifications_alert
+ : R.drawable.ic_notifications_silence;
+ return new NotificationMenuItem(context, infoDescription, infoContent, iconResId);
}
static MenuItem createAppOpsItem(Context context) {
@@ -651,29 +654,6 @@
return info;
}
- private static MenuItem createBlockItem(Context context, NotificationInfo gutsView) {
- return new NotificationInfoMenuItem(
- context,
- context.getResources().getString(R.string.inline_stop_button),
- gutsView,
- R.drawable.ic_notification_block,
- ACTION_BLOCK);
- }
-
- private static MenuItem createToggleSilentItem(Context context, NotificationInfo gutsView,
- boolean isCurrentlySilent) {
- return new NotificationInfoMenuItem(
- context,
- isCurrentlySilent
- ? context.getResources().getString(R.string.inline_silent_button_alert)
- : context.getResources().getString(R.string.inline_silent_button_silent),
- gutsView,
- isCurrentlySilent
- ? R.drawable.ic_notifications_alert
- : R.drawable.ic_notifications_silence,
- ACTION_TOGGLE_SILENT);
- }
-
private void addMenuView(MenuItem item, ViewGroup parent) {
View menuView = item.getMenuView();
if (menuView != null) {
@@ -789,23 +769,4 @@
return mContentDescription;
}
}
-
- /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */
- public static class NotificationInfoMenuItem extends NotificationMenuItem {
-
- @NotificationInfoAction
- int mAction;
-
- public NotificationInfoMenuItem(Context context, String contentDescription,
- NotificationInfo content, int iconResId,
- @NotificationInfoAction int action) {
- super(context, contentDescription, content, iconResId);
- this.mAction = action;
- }
-
- @Override
- public NotificationInfo getGutsView() {
- return (NotificationInfo) super.getGutsView();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ff1a6fc..670908f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -40,7 +40,7 @@
private static final int NO_SECTION_BOUNDARY = -1;
- private ArrayList<View> mDraggedViews = new ArrayList<>();
+ private ArrayList<ExpandableView> mDraggedViews = new ArrayList<>();
private int mScrollY;
private boolean mDimmed;
private ActivatableNotificationView mActivatedChild;
@@ -131,7 +131,8 @@
this.mScrollY = scrollY;
}
- public void onBeginDrag(View view) {
+ /** Call when dragging begins. */
+ public void onBeginDrag(ExpandableView view) {
mDraggedViews.add(view);
}
@@ -139,7 +140,7 @@
mDraggedViews.remove(view);
}
- public ArrayList<View> getDraggedViews() {
+ public ArrayList<ExpandableView> getDraggedViews() {
return mDraggedViews;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 74b4aa2a..0f38bd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -274,7 +274,7 @@
updateGroupOverflow();
row.setContentTransformationAmount(0, false /* isLastChild */);
// It doesn't make sense to keep old animations around, lets cancel them!
- ExpandableNotificationRow.NotificationViewState viewState = row.getViewState();
+ ExpandableViewState viewState = row.getViewState();
if (viewState != null) {
viewState.cancelAnimations(row);
row.cancelAppearDrawing();
@@ -562,12 +562,10 @@
/**
* Update the state of all its children based on a linear layout algorithm.
- * @param resultState the state to update
* @param parentState the state of the parent
* @param ambientState
*/
- public void getState(StackScrollState resultState, ExpandableViewState parentState,
- AmbientState ambientState) {
+ public void updateState(ExpandableViewState parentState, AmbientState ambientState) {
int childCount = mChildren.size();
int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
boolean firstChild = true;
@@ -605,7 +603,7 @@
firstChild = false;
}
- ExpandableViewState childState = resultState.getViewStateForView(child);
+ ExpandableViewState childState = child.getViewState();
int intrinsicHeight = child.getIntrinsicHeight();
childState.height = intrinsicHeight;
childState.yTranslation = yPosition + launchTransitionCompensation;
@@ -639,7 +637,7 @@
if (mOverflowNumber != null) {
ExpandableNotificationRow overflowView = mChildren.get(Math.min(
getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
- mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
+ mGroupOverFlowState.copyFrom(overflowView.getViewState());
if (mContainingNotification.isOnAmbient()) {
mGroupOverFlowState.alpha = 0.0f;
@@ -724,7 +722,8 @@
return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
}
- public void applyState(StackScrollState state) {
+ /** Applies state to children. */
+ public void applyState() {
int childCount = mChildren.size();
ViewState tmpState = new ViewState();
float expandFraction = 0.0f;
@@ -737,7 +736,7 @@
&& !mHideDividersDuringExpand);
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
- ExpandableViewState viewState = state.getViewStateForView(child);
+ ExpandableViewState viewState = child.getViewState();
viewState.applyToView(child);
// layout the divider
@@ -799,14 +798,14 @@
* This is called when the children expansion has changed and positions the children properly
* for an appear animation.
*
- * @param state the new state we animate to
*/
- public void prepareExpansionChanged(StackScrollState state) {
+ public void prepareExpansionChanged() {
// TODO: do something that makes sense, like placing the invisible views correctly
return;
}
- public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
+ /** Animate to a given state. */
+ public void startAnimationToState(AnimationProperties properties) {
int childCount = mChildren.size();
ViewState tmpState = new ViewState();
float expandFraction = getGroupExpandFraction();
@@ -816,7 +815,7 @@
&& !mHideDividersDuringExpand);
for (int i = childCount - 1; i >= 0; i--) {
ExpandableNotificationRow child = mChildren.get(i);
- ExpandableViewState viewState = state.getViewStateForView(child);
+ ExpandableViewState viewState = child.getViewState();
viewState.animateTo(child, properties);
// layout the divider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 6de4fc8..f0a2653 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -45,34 +45,31 @@
/**
* Change the position of child to a new location
- *
- * @param child the view to change the position for
+ * @param child the view to change the position for
* @param newIndex the new index
*/
- void changeViewPosition(View child, int newIndex);
+ void changeViewPosition(ExpandableView child, int newIndex);
/**
* Called when a child was added to a group.
*
* @param row row of the group child that was added
*/
- void notifyGroupChildAdded(View row);
+ void notifyGroupChildAdded(ExpandableView row);
/**
* Called when a child was removed from a group.
- *
- * @param row row of the child that was removed
+ * @param row row of the child that was removed
* @param childrenContainer ViewGroup of the group that the child was removed from
*/
- void notifyGroupChildRemoved(View row, ViewGroup childrenContainer);
+ void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer);
/**
* Generate an animation for an added child view.
- *
- * @param child The view to be added.
+ * @param child The view to be added.
* @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
*/
- void generateAddAnimation(View child, boolean fromMoreCard);
+ void generateAddAnimation(ExpandableView child, boolean fromMoreCard);
/**
* Generate a child order changed event.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 27838a2..4f0831f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,14 +16,12 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
- .NUM_SECTIONS;
-
-import android.view.View;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.HashSet;
@@ -38,7 +36,7 @@
private ActivatableNotificationView[] mLastInSectionViews;
private ActivatableNotificationView[] mTmpFirstInSectionViews;
private ActivatableNotificationView[] mTmpLastInSectionViews;
- private HashSet<View> mAnimatedChildren;
+ private HashSet<ExpandableView> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
private ExpandableNotificationRow mTrackedHeadsUp;
private float mAppearFraction;
@@ -212,7 +210,7 @@
return anyChanged;
}
- public void setAnimatedChildren(HashSet<View> animatedChildren) {
+ public void setAnimatedChildren(HashSet<ExpandableView> animatedChildren) {
mAnimatedChildren = animatedChildren;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index faccff3..eca1a14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -18,6 +18,8 @@
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator
+ .ANIMATION_DURATION_SWIPE;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
import android.animation.Animator;
@@ -208,16 +210,12 @@
*/
protected final StackScrollAlgorithm mStackScrollAlgorithm;
- /**
- * The current State this Layout is in
- */
- private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
private final AmbientState mAmbientState;
private NotificationGroupManager mGroupManager;
- private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
+ private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
- private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
- private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
+ private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
+ private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>();
private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
private ArrayList<View> mSwipedOutViews = new ArrayList<>();
@@ -275,7 +273,7 @@
private boolean mDontReportNextOverScroll;
private boolean mDontClampNextScroll;
private boolean mNeedViewResizeAnimation;
- private View mExpandedGroupView;
+ private ExpandableView mExpandedGroupView;
private boolean mEverythingNeedsAnimation;
/**
@@ -382,7 +380,7 @@
private boolean mGroupExpandedForMeasure;
private boolean mScrollable;
private View mForcedScroll;
- private View mNeedingPulseAnimation;
+ private ExpandableView mNeedingPulseAnimation;
/**
* @see #setDarkAmount(float, float)
@@ -620,7 +618,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(StatusBarStateController.class)
- .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
+ .addCallback(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -628,7 +626,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
Dependency.get(ConfigurationController.class).removeCallback(this);
}
@@ -899,7 +897,8 @@
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
public boolean isInVisibleLocation(NotificationData.Entry entry) {
ExpandableNotificationRow row = entry.getRow();
- ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
+ ExpandableViewState childViewState = row.getViewState();
+
if (childViewState == null) {
return false;
}
@@ -945,7 +944,7 @@
? 0
: mScroller.getCurrVelocity());
mAmbientState.setScrollY(mOwnScrollY);
- mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
+ mStackScrollAlgorithm.resetViewStates(mAmbientState);
if (!isCurrentlyAnimating() && !mNeedsAnimation) {
applyCurrentState();
} else {
@@ -1937,12 +1936,12 @@
* @return the last child which has visibility unequal to GONE
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public View getLastChildNotGone() {
+ public ExpandableView getLastChildNotGone() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE && child != mShelf) {
- return child;
+ return (ExpandableView) child;
}
}
return null;
@@ -2511,7 +2510,7 @@
// we only call our internal methods if this is actually a removal and not just a
// notification which becomes a child notification
if (!mChildTransferInProgress) {
- onViewRemovedInternal(child, this);
+ onViewRemovedInternal((ExpandableView) child, this);
}
}
@@ -2522,25 +2521,22 @@
if (child == mSwipeHelper.getTranslatingParentView()) {
mSwipeHelper.clearTranslatingParentView();
}
- mCurrentStackScrollState.removeViewStateForView(child);
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private void onViewRemovedInternal(View child, ViewGroup container) {
+ private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
if (mChangePositionInProgress) {
// This is only a position change, don't do anything special
return;
}
- ExpandableView expandableView = (ExpandableView) child;
- expandableView.setOnHeightChangedListener(null);
- mCurrentStackScrollState.removeViewStateForView(child);
- updateScrollStateForRemovedChild(expandableView);
+ child.setOnHeightChangedListener(null);
+ updateScrollStateForRemovedChild(child);
boolean animationGenerated = generateRemoveAnimation(child);
if (animationGenerated) {
if (!mSwipedOutViews.contains(child)
- || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
+ || Math.abs(child.getTranslation()) != child.getWidth()) {
container.addTransientView(child, 0);
- expandableView.setTransientContainer(container);
+ child.setTransientContainer(container);
}
} else {
mSwipedOutViews.remove(child);
@@ -2584,14 +2580,14 @@
* @return Whether an animation was generated.
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- private boolean generateRemoveAnimation(View child) {
+ private boolean generateRemoveAnimation(ExpandableView child) {
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
mAddedHeadsUpChildren.remove(child);
return false;
}
if (isClickedHeadsUp(child)) {
// An animation is already running, add it transiently
- mClearTransientViewsWhenFinished.add((ExpandableView) child);
+ mClearTransientViewsWhenFinished.add(child);
return true;
}
if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
@@ -2765,7 +2761,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onViewAdded(View child) {
super.onViewAdded(child);
- onViewAddedInternal(child);
+ onViewAddedInternal((ExpandableView) child);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2835,31 +2831,28 @@
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private void onViewAddedInternal(View child) {
+ private void onViewAddedInternal(ExpandableView child) {
updateHideSensitiveForChild(child);
- ((ExpandableView) child).setOnHeightChangedListener(this);
+ child.setOnHeightChangedListener(this);
generateAddAnimation(child, false /* fromMoreCard */);
updateAnimationState(child);
updateChronometerForChild(child);
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private void updateHideSensitiveForChild(View child) {
- if (child instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) child;
- expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
- }
+ private void updateHideSensitiveForChild(ExpandableView child) {
+ child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
}
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
+ public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
onViewRemovedInternal(row, childrenContainer);
}
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void notifyGroupChildAdded(View row) {
+ public void notifyGroupChildAdded(ExpandableView row) {
onViewAddedInternal(row);
}
@@ -2931,7 +2924,7 @@
@Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void generateAddAnimation(View child, boolean fromMoreCard) {
+ public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
// Generate Animations
mChildrenToAddAnimated.add(child);
@@ -2948,7 +2941,7 @@
@Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void changeViewPosition(View child, int newIndex) {
+ public void changeViewPosition(ExpandableView child, int newIndex) {
int currentIndex = indexOfChild(child);
if (currentIndex == -1) {
@@ -2987,8 +2980,7 @@
}
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
setAnimationRunning(true);
- mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
- mGoToFullShadeDelay);
+ mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
@@ -3035,7 +3027,7 @@
continue;
}
} else {
- ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
+ ExpandableViewState viewState = row.getViewState();
if (viewState == null) {
// A view state was never generated for this view, so we don't need to animate
// this. This may happen with notification children.
@@ -3101,7 +3093,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateChildRemovalEvents() {
- for (View child : mChildrenToRemoveAnimated) {
+ for (ExpandableView child : mChildrenToRemoveAnimated) {
boolean childWasSwipedOut = mSwipedOutViews.contains(child);
// we need to know the view after this one
@@ -3143,7 +3135,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generatePositionChangeEvents() {
- for (View child : mChildrenChangingPositions) {
+ for (ExpandableView child : mChildrenChangingPositions) {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
}
@@ -3157,7 +3149,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateChildAdditionEvents() {
- for (View child : mChildrenToAddAnimated) {
+ for (ExpandableView child : mChildrenToAddAnimated) {
if (mFromMoreCardAdditions.contains(child)) {
mAnimationEvents.add(new AnimationEvent(child,
AnimationEvent.ANIMATION_TYPE_ADD,
@@ -3249,7 +3241,7 @@
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- return new StackScrollAlgorithm(context);
+ return new StackScrollAlgorithm(context, this);
}
/**
@@ -3914,7 +3906,7 @@
mStatusBar.resetUserExpandedStates();
clearTemporaryViews();
clearUserLockedViews();
- ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+ ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
if (draggedViews.size() > 0) {
draggedViews.clear();
updateContinuousShadowDrawing();
@@ -4198,7 +4190,12 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void applyCurrentState() {
- mCurrentStackScrollState.apply();
+ int numChildren = getChildCount();
+ for (int i = 0; i < numChildren; i++) {
+ ExpandableView child = (ExpandableView) getChildAt(i);
+ child.applyViewState();
+ }
+
if (mListener != null) {
mListener.onChildLocationsChanged();
}
@@ -4942,8 +4939,7 @@
if (!(child instanceof ExpandableNotificationRow)) {
pw.println(" " + child.getClass().getSimpleName());
// Notifications dump it's viewstate as part of their dump to support children
- ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(
- child);
+ ExpandableViewState viewState = child.getViewState();
if (viewState == null) {
pw.println(" no viewState!!!");
} else {
@@ -4960,7 +4956,7 @@
ExpandableView child = (ExpandableView) getTransientView(i);
child.dump(fd, pw, args);
}
- ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+ ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
int draggedCount = draggedViews.size();
pw.println(" Dragged Views: " + draggedCount);
for (int i = 0; i < draggedCount; i++) {
@@ -5099,7 +5095,7 @@
if (i == 0) {
endRunnable = animationFinishAction;
}
- dismissViewAnimated(view, endRunnable, totalDelay, 260);
+ dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
totalDelay += currentDelay;
}
@@ -5496,7 +5492,7 @@
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
final long eventStartTime;
- final View changingView;
+ final ExpandableView mChangingView;
final int animationType;
final AnimationFilter filter;
final long length;
@@ -5504,21 +5500,21 @@
int darkAnimationOriginIndex;
boolean headsUpFromBottom;
- AnimationEvent(View view, int type) {
+ AnimationEvent(ExpandableView view, int type) {
this(view, type, LENGTHS[type]);
}
- AnimationEvent(View view, int type, AnimationFilter filter) {
+ AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
this(view, type, LENGTHS[type], filter);
}
- AnimationEvent(View view, int type, long length) {
+ AnimationEvent(ExpandableView view, int type, long length) {
this(view, type, length, FILTERS[type]);
}
- AnimationEvent(View view, int type, long length, AnimationFilter filter) {
+ AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
eventStartTime = AnimationUtils.currentAnimationTimeMillis();
- changingView = view;
+ mChangingView = view;
animationType = type;
this.length = length;
this.filter = filter;
@@ -5716,7 +5712,7 @@
public void onBeginDrag(View v) {
mFalsingManager.onNotificatonStartDismissing();
setSwipingInProgress(true);
- mAmbientState.onBeginDrag(v);
+ mAmbientState.onBeginDrag((ExpandableView) v);
updateContinuousShadowDrawing();
requestChildrenUpdate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 87c361a..25fb7f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -42,6 +42,7 @@
public class StackScrollAlgorithm {
private static final String LOG_TAG = "StackScrollAlgorithm";
+ private final ViewGroup mHostView;
private int mPaddingBetweenElements;
private int mIncreasedPaddingBetweenElements;
@@ -55,7 +56,8 @@
private float mHeadsUpInset;
private int mPinnedZTranslationExtra;
- public StackScrollAlgorithm(Context context) {
+ public StackScrollAlgorithm(Context context, ViewGroup hostView) {
+ mHostView = hostView;
initView(context);
}
@@ -79,49 +81,59 @@
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
}
- public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
+ /**
+ * Updates the state of all children in the hostview based on this algorithm.
+ */
+ public void resetViewStates(AmbientState ambientState) {
// The state of the local variables are saved in an algorithmState to easily subdivide it
// into multiple phases.
StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
// First we reset the view states to their default values.
- resultState.resetViewStates();
+ resetChildViewStates();
- initAlgorithmState(resultState, algorithmState, ambientState);
+ initAlgorithmState(mHostView, algorithmState, ambientState);
- updatePositionsForState(resultState, algorithmState, ambientState);
+ updatePositionsForState(algorithmState, ambientState);
- updateZValuesForState(resultState, algorithmState, ambientState);
+ updateZValuesForState(algorithmState, ambientState);
- updateHeadsUpStates(resultState, algorithmState, ambientState);
+ updateHeadsUpStates(algorithmState, ambientState);
- updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
- updateClipping(resultState, algorithmState, ambientState);
- updateSpeedBumpState(resultState, algorithmState, ambientState);
- updateShelfState(resultState, ambientState);
- getNotificationChildrenStates(resultState, algorithmState, ambientState);
+ updateDimmedActivatedHideSensitive(ambientState, algorithmState);
+ updateClipping(algorithmState, ambientState);
+ updateSpeedBumpState(algorithmState, ambientState);
+ updateShelfState(ambientState);
+ getNotificationChildrenStates(algorithmState, ambientState);
}
- private void getNotificationChildrenStates(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState,
+ private void resetChildViewStates() {
+ int numChildren = mHostView.getChildCount();
+ for (int i = 0; i < numChildren; i++) {
+ ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
+ child.resetViewState();
+ }
+ }
+
+ private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
ExpandableView v = algorithmState.visibleChildren.get(i);
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- row.getChildrenStates(resultState, ambientState);
+ row.updateChildrenStates(ambientState);
}
}
}
- private void updateSpeedBumpState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
int belowSpeedBump = ambientState.getSpeedBumpIndex();
for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
- ExpandableViewState childViewState = resultState.getViewStateForView(child);
+ ExpandableView child = algorithmState.visibleChildren.get(i);
+ ExpandableViewState childViewState = child.getViewState();
// The speed bump can also be gone, so equality needs to be taken when comparing
// indices.
@@ -129,15 +141,16 @@
}
}
- private void updateShelfState(StackScrollState resultState, AmbientState ambientState) {
+
+ private void updateShelfState(AmbientState ambientState) {
NotificationShelf shelf = ambientState.getShelf();
if (shelf != null) {
- shelf.updateState(resultState, ambientState);
+ shelf.updateState(ambientState);
}
}
- private void updateClipping(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateClipping(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
+ ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
: 0;
@@ -146,7 +159,7 @@
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
- ExpandableViewState state = resultState.getViewStateForView(child);
+ ExpandableViewState state = child.getViewState();
if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
previousNotificationStart = Math.max(drawStart, previousNotificationStart);
@@ -190,15 +203,15 @@
* Updates the dimmed, activated and hiding sensitive states of the children.
*/
private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
- StackScrollState resultState, StackScrollAlgorithmState algorithmState) {
+ StackScrollAlgorithmState algorithmState) {
boolean dimmed = ambientState.isDimmed();
boolean dark = ambientState.isFullyDark();
boolean hideSensitive = ambientState.isHideSensitive();
View activatedChild = ambientState.getActivatedChild();
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
- ExpandableViewState childViewState = resultState.getViewStateForView(child);
+ ExpandableView child = algorithmState.visibleChildren.get(i);
+ ExpandableViewState childViewState = child.getViewState();
childViewState.dimmed = dimmed;
childViewState.dark = dark;
childViewState.hideSensitive = hideSensitive;
@@ -212,7 +225,7 @@
/**
* Initialize the algorithm state like updating the visible children.
*/
- private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state,
+ private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
AmbientState ambientState) {
float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
@@ -224,7 +237,6 @@
state.scrollY = (int) (scrollY + bottomOverScroll);
//now init the visible children and update paddings
- ViewGroup hostView = resultState.getHostView();
int childCount = hostView.getChildCount();
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
@@ -249,7 +261,7 @@
// we need normal padding now, to be in sync with what the stack calculates
lastView = null;
}
- notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+ notGoneIndex = updateNotGoneIndex(state, notGoneIndex, v);
float increasedPadding = v.getIncreasedPaddingAmount();
if (increasedPadding != 0.0f) {
state.paddingMap.put(v, increasedPadding);
@@ -282,13 +294,11 @@
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
// handle the notgoneIndex for the children as well
- List<ExpandableNotificationRow> children =
- row.getNotificationChildren();
+ List<ExpandableNotificationRow> children = row.getNotificationChildren();
if (row.isSummaryWithChildren() && children != null) {
for (ExpandableNotificationRow childRow : children) {
if (childRow.getVisibility() != View.GONE) {
- ExpandableViewState childState
- = resultState.getViewStateForView(childRow);
+ ExpandableViewState childState = childRow.getViewState();
childState.notGoneIndex = notGoneIndex;
notGoneIndex++;
}
@@ -301,8 +311,8 @@
ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
state.indexOfExpandingNotification = expandingNotification != null
? expandingNotification.isChildInGroup()
- ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
- : state.visibleChildren.indexOf(expandingNotification)
+ ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
+ : state.visibleChildren.indexOf(expandingNotification)
: -1;
}
@@ -322,10 +332,9 @@
}
}
- private int updateNotGoneIndex(StackScrollState resultState,
- StackScrollAlgorithmState state, int notGoneIndex,
+ private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
ExpandableView v) {
- ExpandableViewState viewState = resultState.getViewStateForView(v);
+ ExpandableViewState viewState = v.getViewState();
viewState.notGoneIndex = notGoneIndex;
state.visibleChildren.add(v);
notGoneIndex++;
@@ -335,27 +344,27 @@
/**
* Determine the positions for the views. This is the main part of the algorithm.
*
- * @param resultState The result state to update if a change to the properties of a child occurs
* @param algorithmState The state in which the current pass of the algorithm is currently in
- * @param ambientState The current ambient state
+ * @param ambientState The current ambient state
*/
- private void updatePositionsForState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updatePositionsForState(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
// The y coordinate of the current child.
float currentYPosition = -algorithmState.scrollY;
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- currentYPosition = updateChild(i, resultState, algorithmState, ambientState,
- currentYPosition);
+ currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
}
}
- protected float updateChild(int i, StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState,
+ protected float updateChild(
+ int i,
+ StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState,
float currentYPosition) {
ExpandableView child = algorithmState.visibleChildren.get(i);
- ExpandableViewState childViewState = resultState.getViewStateForView(child);
+ ExpandableViewState childViewState = child.getViewState();
childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
int childHeight = getMaxAllowedChildHeight(child);
@@ -404,8 +413,8 @@
return algorithmState.getPaddingAfterChild(child);
}
- private void updateHeadsUpStates(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
ExpandableNotificationRow topHeadsUpEntry = null;
for (int i = 0; i < childCount; i++) {
@@ -417,7 +426,7 @@
if (!row.isHeadsUp()) {
break;
}
- ExpandableViewState childState = resultState.getViewStateForView(row);
+ ExpandableViewState childState = row.getViewState();
if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
topHeadsUpEntry = row;
childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
@@ -439,7 +448,8 @@
childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
childState.hidden = false;
- ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
+ ExpandableViewState topState =
+ topHeadsUpEntry == null ? null : topHeadsUpEntry.getViewState();
if (topState != null && !isTopEntry && (!mIsExpanded
|| unmodifiedEndLocation < topState.yTranslation + topState.height)) {
// Ensure that a headsUp doesn't vertically extend further than the heads-up at
@@ -491,9 +501,8 @@
* Clamp the height of the child down such that its end is at most on the beginning of
* the shelf.
*
- * @param child
* @param childViewState the view state of the child
- * @param ambientState the ambient state
+ * @param ambientState the ambient state
*/
private void clampPositionToShelf(ExpandableView child,
ExpandableViewState childViewState,
@@ -521,31 +530,31 @@
ExpandableView expandableView = (ExpandableView) child;
return expandableView.getIntrinsicHeight();
}
- return child == null? mCollapsedSize : child.getHeight();
+ return child == null ? mCollapsedSize : child.getHeight();
}
/**
* Calculate the Z positions for all children based on the number of items in both stacks and
* save it in the resultState
- * @param resultState The result state to update the zTranslation values
+ *
* @param algorithmState The state in which the current pass of the algorithm is currently in
- * @param ambientState The ambient state of the algorithm
+ * @param ambientState The ambient state of the algorithm
*/
- private void updateZValuesForState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateZValuesForState(StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
float childrenOnTop = 0.0f;
for (int i = childCount - 1; i >= 0; i--) {
childrenOnTop = updateChildZValue(i, childrenOnTop,
- resultState, algorithmState, ambientState);
+ algorithmState, ambientState);
}
}
protected float updateChildZValue(int i, float childrenOnTop,
- StackScrollState resultState, StackScrollAlgorithmState algorithmState,
+ StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
ExpandableView child = algorithmState.visibleChildren.get(i);
- ExpandableViewState childViewState = resultState.getViewStateForView(child);
+ ExpandableViewState childViewState = child.getViewState();
int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
float baseZ = ambientState.getBaseZHeight();
if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
deleted file mode 100644
index e55707c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification.stack;
-
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * A state of a
- * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout} which
- * can be applied to a viewGroup.
- */
-public class StackScrollState {
-
- private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
-
- private final ViewGroup mHostView;
- private WeakHashMap<ExpandableView, ExpandableViewState> mStateMap;
-
- public StackScrollState(ViewGroup hostView) {
- mHostView = hostView;
- mStateMap = new WeakHashMap<>();
- }
-
- public ViewGroup getHostView() {
- return mHostView;
- }
-
- public void resetViewStates() {
- int numChildren = mHostView.getChildCount();
- for (int i = 0; i < numChildren; i++) {
- ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
- resetViewState(child);
-
- // handling reset for child notifications
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- List<ExpandableNotificationRow> children =
- row.getNotificationChildren();
- if (row.isSummaryWithChildren() && children != null) {
- for (ExpandableNotificationRow childRow : children) {
- resetViewState(childRow);
- }
- }
- }
- }
- }
-
- private void resetViewState(ExpandableView view) {
- ExpandableViewState viewState = mStateMap.get(view);
- if (viewState == null) {
- viewState = view.createNewViewState(this);
- mStateMap.put(view, viewState);
- }
- // initialize with the default values of the view
- viewState.height = view.getIntrinsicHeight();
- viewState.gone = view.getVisibility() == View.GONE;
- viewState.alpha = 1f;
- viewState.notGoneIndex = -1;
- viewState.xTranslation = view.getTranslationX();
- viewState.hidden = false;
- viewState.scaleX = view.getScaleX();
- viewState.scaleY = view.getScaleY();
- viewState.inShelf = false;
- viewState.headsUpIsVisible = false;
- }
-
- public ExpandableViewState getViewStateForView(View requestedView) {
- return mStateMap.get(requestedView);
- }
-
- public void removeViewStateForView(View child) {
- mStateMap.remove(child);
- }
-
- /**
- * Apply the properties saved in {@link #mStateMap} to the children of the {@link #mHostView}.
- * The properties are only applied if they effectively changed.
- */
- public void apply() {
- int numChildren = mHostView.getChildCount();
- for (int i = 0; i < numChildren; i++) {
- ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
- ExpandableViewState state = mStateMap.get(child);
- if (state == null) {
- Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
- "to the hostView");
- continue;
- }
- if (state.gone) {
- continue;
- }
- state.applyToView(child);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 34dab53..d690547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -44,6 +44,7 @@
public static final int ANIMATION_DURATION_WAKEUP = 500;
public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
+ public static final int ANIMATION_DURATION_SWIPE = 260;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
@@ -128,25 +129,25 @@
public void startAnimationForEvents(
ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
- StackScrollState finalState, long additionalDelay) {
+ long additionalDelay) {
- processAnimationEvents(mAnimationEvents, finalState);
+ processAnimationEvents(mAnimationEvents);
int childCount = mHostLayout.getChildCount();
mAnimationFilter.applyCombination(mNewEvents);
mCurrentAdditionalDelay = additionalDelay;
mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
- mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
+ mCurrentLastNotAddedIndex = findLastNotAddedIndex();
for (int i = 0; i < childCount; i++) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
- ExpandableViewState viewState = finalState.getViewStateForView(child);
+ ExpandableViewState viewState = child.getViewState();
if (viewState == null || child.getVisibility() == View.GONE
- || applyWithoutAnimation(child, viewState, finalState)) {
+ || applyWithoutAnimation(child, viewState)) {
continue;
}
- initAnimationProperties(finalState, child, viewState);
+ initAnimationProperties(child, viewState);
viewState.animateTo(child, mAnimationProperties);
}
if (!isRunning()) {
@@ -159,7 +160,7 @@
mNewAddChildren.clear();
}
- private void initAnimationProperties(StackScrollState finalState, ExpandableView child,
+ private void initAnimationProperties(ExpandableView child,
ExpandableViewState viewState) {
boolean wasAdded = mAnimationProperties.wasAdded(child);
mAnimationProperties.duration = mCurrentLength;
@@ -173,7 +174,7 @@
|| viewState.clipTopAmount != child.getClipTopAmount()
|| viewState.dark != child.isDark())) {
mAnimationProperties.delay = mCurrentAdditionalDelay
- + calculateChildAnimationDelay(viewState, finalState);
+ + calculateChildAnimationDelay(viewState);
}
}
@@ -193,8 +194,7 @@
*
* @return true if no animation should be performed
*/
- private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState,
- StackScrollState finalState) {
+ private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState) {
if (mShadeExpanded) {
return false;
}
@@ -214,12 +214,12 @@
return true;
}
- private int findLastNotAddedIndex(StackScrollState finalState) {
+ private int findLastNotAddedIndex() {
int childCount = mHostLayout.getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
- ExpandableViewState viewState = finalState.getViewStateForView(child);
+ ExpandableViewState viewState = child.getViewState();
if (viewState == null || child.getVisibility() == View.GONE) {
continue;
}
@@ -230,8 +230,7 @@
return -1;
}
- private long calculateChildAnimationDelay(ExpandableViewState viewState,
- StackScrollState finalState) {
+ private long calculateChildAnimationDelay(ExpandableViewState viewState) {
if (mAnimationFilter.hasGoToFullShadeEvent) {
return calculateDelayGoToFullShade(viewState);
}
@@ -244,8 +243,8 @@
switch (event.animationType) {
case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
int ownIndex = viewState.notGoneIndex;
- int changingIndex = finalState
- .getViewStateForView(event.changingView).notGoneIndex;
+ int changingIndex =
+ ((ExpandableView) (event.mChangingView)).getViewState().notGoneIndex;
int difference = Math.abs(ownIndex - changingIndex);
difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
difference - 1));
@@ -258,9 +257,9 @@
case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
int ownIndex = viewState.notGoneIndex;
boolean noNextView = event.viewAfterChangingView == null;
- View viewAfterChangingView = noNextView
+ ExpandableView viewAfterChangingView = noNextView
? mHostLayout.getLastChildNotGone()
- : event.viewAfterChangingView;
+ : (ExpandableView) event.viewAfterChangingView;
if (viewAfterChangingView == null) {
// This can happen when the last view in the list is removed.
// Since the shelf is still around and the only view, the code still goes
@@ -268,8 +267,7 @@
// have changed.
continue;
}
- int nextIndex = finalState
- .getViewStateForView(viewAfterChangingView).notGoneIndex;
+ int nextIndex = viewAfterChangingView.getViewState().notGoneIndex;
if (ownIndex >= nextIndex) {
// we only have the view afterwards
ownIndex++;
@@ -351,20 +349,17 @@
/**
* Process the animationEvents for a new animation
*
- * @param animationEvents the animation events for the animation to perform
- * @param finalState the final state to animate to
+ * @param animationEvents the animation events for the animation to perform
*/
private void processAnimationEvents(
- ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
- StackScrollState finalState) {
+ ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents) {
for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
- final ExpandableView changingView = (ExpandableView) event.changingView;
+ final ExpandableView changingView = (ExpandableView) event.mChangingView;
if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
// This item is added, initialize it's properties.
- ExpandableViewState viewState = finalState
- .getViewStateForView(changingView);
+ ExpandableViewState viewState = changingView.getViewState();
if (viewState == null || viewState.gone) {
// The position for this child was never generated, let's continue.
continue;
@@ -381,12 +376,9 @@
// Find the amount to translate up. This is needed in order to understand the
// direction of the remove animation (either downwards or upwards)
- ExpandableViewState viewState = finalState
- .getViewStateForView(event.viewAfterChangingView);
- int actualHeight = changingView.getActualHeight();
// upwards by default
float translationDirection = -1.0f;
- if (viewState != null) {
+ if (event.viewAfterChangingView != null) {
float ownPosition = changingView.getTranslationY();
if (changingView instanceof ExpandableNotificationRow
&& event.viewAfterChangingView instanceof ExpandableNotificationRow) {
@@ -402,8 +394,11 @@
ownPosition = changingRow.getTranslationWhenRemoved();
}
}
+ int actualHeight = changingView.getActualHeight();
// there was a view after this one, Approximate the distance the next child
// travelled
+ ExpandableViewState viewState =
+ ((ExpandableView) event.viewAfterChangingView).getViewState();
translationDirection = ((viewState.yTranslation
- (ownPosition + actualHeight / 2.0f)) * 2 /
actualHeight);
@@ -426,11 +421,11 @@
}
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
- row.prepareExpansionChanged(finalState);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) event.mChangingView;
+ row.prepareExpansionChanged();
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
- ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ ExpandableViewState viewState = changingView.getViewState();
if (viewState != null) {
mTmpState.copyFrom(viewState);
mTmpState.yTranslation += mPulsingAppearingTranslation;
@@ -439,7 +434,7 @@
}
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
- ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ ExpandableViewState viewState = changingView.getViewState();
if (viewState != null) {
viewState.alpha = 0;
// We want to animate the alpha away before the view starts translating,
@@ -454,7 +449,7 @@
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
// This item is added, initialize it's properties.
- ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ ExpandableViewState viewState = changingView.getViewState();
mTmpState.copyFrom(viewState);
if (event.headsUpFromBottom) {
mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 3b13fe9..8418cba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -121,14 +121,14 @@
public void onResume() {
super.onResume();
mCommandQueue.addCallbacks(this);
- mStatusBarStateController.addListener(this);
+ mStatusBarStateController.addCallback(this);
}
@Override
public void onPause() {
super.onPause();
mCommandQueue.removeCallbacks(this);
- mStatusBarStateController.removeListener(this);
+ mStatusBarStateController.removeCallback(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 94b2cde..cfa751c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -88,7 +88,7 @@
public DozeScrimController(DozeParameters dozeParameters) {
mDozeParameters = dozeParameters;
//Never expected to be destroyed
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 9faada0..aa0b7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -129,7 +129,7 @@
updateTouchableRegionListener();
}
});
- Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
if (!hasBubbles) {
mBubbleGoingAway = true;
@@ -143,7 +143,7 @@
}
public void destroy() {
- Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
}
private void initResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index d8280ba..a81b7e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -22,6 +22,7 @@
import android.content.res.Resources;
import android.util.MathUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -229,6 +230,11 @@
- mBurnInPreventionOffsetX;
}
+ @VisibleForTesting
+ void setPulsingPadding(int padding) {
+ mPulsingPadding = padding;
+ }
+
public static class Result {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index b29889d..1c6d292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -74,14 +74,14 @@
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
SysUiServiceProvider.getComponent(context, CommandQueue.class)
.addCallbacks(this);
- mStatusBarStateController.addListener(this);
+ mStatusBarStateController.addCallback(this);
mDozeAmount = mStatusBarStateController.getDozeAmount();
}
public void destroy(Context context) {
SysUiServiceProvider.getComponent(context, CommandQueue.class)
.removeCallbacks(this);
- mStatusBarStateController.removeListener(this);
+ mStatusBarStateController.removeCallback(this);
}
public void saveState(Bundle outState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
index 1002f9e..b83ebc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-
import android.annotation.NonNull;
import android.hardware.input.InputManager;
import android.os.Handler;
@@ -35,10 +33,8 @@
*/
public class NavigationBackAction extends NavigationGestureAction {
- private static final String PULL_HOME_GO_BACK_PROP = "quickstepcontroller_homegoesback";
private static final String BACK_AFTER_END_PROP =
"quickstepcontroller_homegoesbackwhenend";
- private static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
@@ -60,23 +56,13 @@
}
@Override
- public int requiresTouchDownHitTarget() {
- return HIT_TARGET_HOME;
- }
-
- @Override
- public boolean requiresDragWithHitTarget() {
- return true;
- }
-
- @Override
public boolean canPerformAction() {
return mProxySender.getBackButtonAlpha() > 0;
}
@Override
public boolean isEnabled() {
- return swipeHomeGoBackGestureEnabled();
+ return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED);
}
@Override
@@ -110,13 +96,8 @@
mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
- private boolean swipeHomeGoBackGestureEnabled() {
- return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
- && getGlobalBoolean(PULL_HOME_GO_BACK_PROP);
- }
-
private boolean shouldExecuteBackOnUp() {
- return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
+ return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED)
&& getGlobalBoolean(BACK_AFTER_END_PROP);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 12a0cc8..3984405 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -24,7 +24,6 @@
import android.view.Display;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindowManager;
-import android.view.MotionEvent;
import android.view.View;
import com.android.internal.statusbar.IStatusBarService;
@@ -165,23 +164,4 @@
}
mView.onDarkIntensityChange(darkIntensity);
}
-
- private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- // even though setting the systemUI visibility below will turn these views
- // on, we need them to come up faster so that they can catch this motion
- // event
- applyLightsOut(false, false, false);
-
- try {
- mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
- "LightsOutListener");
- } catch (android.os.RemoteException ex) {
- }
- }
- return false;
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 5db43ea..cd6e1d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
@@ -77,6 +78,8 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction;
+import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -146,6 +149,8 @@
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
+ private NavigationPrototypeController mPrototypeController;
+ private NavigationGestureAction[] mDefaultGestureMap;
private QuickScrubAction mQuickScrubAction;
private QuickStepAction mQuickStepAction;
private NavigationBackAction mBackAction;
@@ -261,6 +266,18 @@
}
};
+ private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() {
+ @Override
+ public void onGestureRemap(int[] actions) {
+ updateNavigationGestures();
+ }
+
+ @Override
+ public void onBackButtonVisibilityChanged(boolean visible) {
+ getBackButton().setVisibility(visible ? VISIBLE : GONE);
+ }
+ };
+
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -309,6 +326,14 @@
mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
mBackAction = new NavigationBackAction(this, mOverviewProxyService);
+ mDefaultGestureMap = new NavigationGestureAction[] {
+ mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
+ mQuickScrubAction
+ };
+
+ mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
+ mPrototypeController.register();
+ mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
}
public BarTransitions getBarTransitions() {
@@ -323,8 +348,32 @@
mPanelView = panel;
if (mGestureHelper instanceof QuickStepController) {
((QuickStepController) mGestureHelper).setComponents(this);
- ((QuickStepController) mGestureHelper).setGestureActions(mQuickStepAction,
- null /* swipeDownAction*/, mBackAction, mQuickScrubAction);
+ updateNavigationGestures();
+ }
+ }
+
+ private void updateNavigationGestures() {
+ if (mGestureHelper instanceof QuickStepController) {
+ final int[] assignedMap = mPrototypeController.getGestureActionMap();
+ ((QuickStepController) mGestureHelper).setGestureActions(
+ getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
+ getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
+ getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
+ getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]));
+ }
+ }
+
+ private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType,
+ NavigationGestureAction defaultAction) {
+ switch(actionType) {
+ case NavigationPrototypeController.ACTION_QUICKSTEP:
+ return mQuickStepAction;
+ case NavigationPrototypeController.ACTION_QUICKSCRUB:
+ return mQuickScrubAction;
+ case NavigationPrototypeController.ACTION_BACK:
+ return mBackAction;
+ default:
+ return defaultAction;
}
}
@@ -902,10 +951,10 @@
private void updateTaskSwitchHelper() {
if (mGestureHelper == null) return;
boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
- int navBarPos = 0;
+ int navBarPos = NAV_BAR_INVALID;
try {
- // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
- navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+ navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(
+ mDisplay.getDisplayId());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get nav bar position.", e);
}
@@ -1043,6 +1092,7 @@
if (mGestureHelper != null) {
mGestureHelper.destroy();
}
+ mPrototypeController.unregister();
setUpSwipeUpOnboarding(false);
for (int i = 0; i < mButtonDispatchers.size(); ++i) {
mButtonDispatchers.valueAt(i).onDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
index 83067f6..a8d00c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -127,6 +127,15 @@
}
/**
+ * Decide if the controller should not send the current motion event to launcher via
+ * {@link OverviewProxyService}
+ * @return if controller should not proxy
+ */
+ public boolean disableProxyEvents() {
+ return false;
+ }
+
+ /**
* Tell if action is enabled. Compared to {@link #canPerformAction()} this is based on settings
* if the action is disabled for a particular gesture. For example a back action can be enabled
* however if there is nothing to back to then {@link #canPerformAction()} should return false.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
new file mode 100644
index 0000000..e8c0bf1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+
+import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different
+ * prototypes to run in the system. The class will handle communication changes from the settings
+ * app and call back to listeners.
+ */
+public class NavigationPrototypeController extends ContentObserver {
+ private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
+
+ static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
+ private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
+ @interface GestureAction {}
+ static final int ACTION_DEFAULT = 0;
+ static final int ACTION_QUICKSTEP = 1;
+ static final int ACTION_QUICKSCRUB = 2;
+ static final int ACTION_BACK = 3;
+
+ private OnPrototypeChangedListener mListener;
+
+ /**
+ * Each index corresponds to a different action set in QuickStepController
+ * {@see updateSwipeLTRBackSetting}
+ */
+ private int[] mActionMap = new int[4];
+
+ private final Context mContext;
+
+ public NavigationPrototypeController(Handler handler, Context context) {
+ super(handler);
+ mContext = context;
+ updateSwipeLTRBackSetting();
+ }
+
+ public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Observe all the settings to react to from prototype settings
+ */
+ public void register() {
+ registerObserver(HIDE_BACK_BUTTON_SETTING);
+ registerObserver(GESTURE_MATCH_SETTING);
+ }
+
+ /**
+ * Disable observing settings to react to from prototype settings
+ */
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ if (!selfChange && mListener != null) {
+ try {
+ final String path = uri.getPath();
+ if (path.endsWith(GESTURE_MATCH_SETTING)) {
+ // Get the settings gesture map corresponding to each action
+ // {@see updateSwipeLTRBackSetting}
+ updateSwipeLTRBackSetting();
+ mListener.onGestureRemap(mActionMap);
+ } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
+ mListener.onBackButtonVisibilityChanged(
+ !getGlobalBool(HIDE_BACK_BUTTON_SETTING));
+ }
+ } catch (SettingNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Retrieve the action map to apply to the quick step controller
+ * @return an action map
+ */
+ int[] getGestureActionMap() {
+ return mActionMap;
+ }
+
+ /**
+ * Since Settings.Global cannot pass arrays, use a string to represent each character as a
+ * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
+ * Number: [up] [down] [left] [right]
+ */
+ private void updateSwipeLTRBackSetting() {
+ String value = Settings.Global.getString(mContext.getContentResolver(),
+ GESTURE_MATCH_SETTING);
+ if (value != null) {
+ for (int i = 0; i < mActionMap.length; ++i) {
+ mActionMap[i] = Character.getNumericValue(value.charAt(i));
+ }
+ }
+ }
+
+ private boolean getGlobalBool(String name) throws SettingNotFoundException {
+ return Settings.Global.getInt(mContext.getContentResolver(), name) == 1;
+ }
+
+ private void registerObserver(String name) {
+ mContext.getContentResolver()
+ .registerContentObserver(Settings.Global.getUriFor(name), false, this);
+ }
+
+ public interface OnPrototypeChangedListener {
+ void onGestureRemap(@GestureAction int[] actions);
+ void onBackButtonVisibilityChanged(boolean visible);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 3e31fa0..2a68fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -82,7 +82,7 @@
private boolean mIsDozing;
public NotificationGroupAlertTransferHelper() {
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 448b5c3..8f4369a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -55,7 +55,7 @@
private boolean mIsUpdatingUnchangedGroup;
public NotificationGroupManager() {
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 33d176a..a2a11bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -366,7 +366,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener);
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
Dependency.get(ZenModeController.class).addCallback(this);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -375,7 +375,7 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener);
- Dependency.get(StatusBarStateController.class).removeListener(this);
+ Dependency.get(StatusBarStateController.class).removeCallback(this);
Dependency.get(ZenModeController.class).removeCallback(this);
Dependency.get(ConfigurationController.class).removeCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
index 74744f1..2b202eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
@@ -212,6 +212,11 @@
}
@Override
+ public boolean disableProxyEvents() {
+ return true;
+ }
+
+ @Override
protected void onGestureStart(MotionEvent event) {
updateHighlight();
ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 0eff4d4..4983618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
@@ -30,9 +31,9 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
@@ -100,6 +101,7 @@
private NavigationGestureAction mCurrentAction;
private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
+ private final Rect mLastLayoutRect = new Rect();
private final OverviewProxyService mOverviewEventSender;
private final Context mContext;
private final StatusBar mStatusBar;
@@ -107,7 +109,6 @@
private final Matrix mTransformLocalMatrix = new Matrix();
public QuickStepController(Context context) {
- final Resources res = context.getResources();
mContext = context;
mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
@@ -142,6 +143,8 @@
if (action != null) {
action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive);
action.onDarkIntensityChange(mDarkIntensity);
+ action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top,
+ mLastLayoutRect.right, mLastLayoutRect.bottom);
}
}
}
@@ -182,6 +185,7 @@
// Requires proxy and an active gesture or able to perform any gesture to continue
if (mOverviewEventSender.getProxy() == null
+ || !mOverviewEventSender.shouldShowSwipeUpUI()
|| (mCurrentAction == null && !canPerformAnyAction())) {
return deadZoneConsumed;
}
@@ -272,25 +276,21 @@
if (mDragVPositive ? (posV < touchDownV) : (posV > touchDownV)) {
// Swiping up gesture
tryToStartGesture(mGestureActions[ACTION_SWIPE_UP_INDEX],
- false /* alignedWithNavBar */, false /* positiveDirection */,
- event);
+ false /* alignedWithNavBar */, event);
} else {
// Swiping down gesture
tryToStartGesture(mGestureActions[ACTION_SWIPE_DOWN_INDEX],
- false /* alignedWithNavBar */, true /* positiveDirection */,
- event);
+ false /* alignedWithNavBar */, event);
}
} else if (exceededSwipeHorizontalTouchSlop) {
if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
// Swiping left (ltr) gesture
tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX],
- true /* alignedWithNavBar */, false /* positiveDirection */,
- event);
+ true /* alignedWithNavBar */, event);
} else {
// Swiping right (ltr) gesture
tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX],
- true /* alignedWithNavBar */, true /* positiveDirection */,
- event);
+ true /* alignedWithNavBar */, event);
}
}
}
@@ -303,7 +303,6 @@
case MotionEvent.ACTION_UP:
if (mCurrentAction != null) {
mCurrentAction.endGesture();
- mCurrentAction = null;
}
// Return the hit target back to its original position
@@ -326,6 +325,11 @@
if (shouldProxyEvents(action)) {
proxyMotionEvents(event);
}
+
+ // Clear action when gesture and event proxy finishes
+ if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+ mCurrentAction = null;
+ }
return mCurrentAction != null || deadZoneConsumed;
}
@@ -351,8 +355,7 @@
private boolean shouldProxyEvents(int action) {
final boolean actionValid = (mCurrentAction == null
- || (mGestureActions[ACTION_SWIPE_UP_INDEX] != null
- && mGestureActions[ACTION_SWIPE_UP_INDEX].isActive()));
+ || !mCurrentAction.disableProxyEvents());
if (actionValid && !mIsInScreenPinning) {
// Allow down, cancel and up events, move and other events are passed if notifications
// are not showing and disabled gestures (such as long press) are not executed
@@ -382,6 +385,7 @@
action.onLayout(changed, left, top, right, bottom);
}
}
+ mLastLayoutRect.set(left, top, right, bottom);
}
@Override
@@ -418,6 +422,9 @@
mDragHPositive = !isRTL;
mDragVPositive = true;
break;
+ case NAV_BAR_INVALID:
+ Log.e(TAG, "Invalid nav bar position");
+ break;
}
for (NavigationGestureAction action: mGestureActions) {
@@ -448,7 +455,7 @@
}
private void tryToStartGesture(NavigationGestureAction action, boolean alignedWithNavBar,
- boolean positiveDirection, MotionEvent event) {
+ MotionEvent event) {
if (action == null) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 05f8f18..58b9de4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -22,6 +22,7 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -160,7 +161,6 @@
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -195,7 +195,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -474,8 +473,10 @@
return;
}
WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
- final boolean supportsAmbientMode = info != null &&
- info.supportsAmbientMode();
+ final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+ final boolean supportsAmbientMode = deviceSupportsAodWallpaper
+ && info != null && info.supportsAmbientMode();
mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -626,7 +627,7 @@
mBubbleController.setExpandListener(mBubbleExpandListener);
mColorExtractor.addOnColorsChangedListener(this);
- mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
+ mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mDreamManager = IDreamManager.Stub.asInterface(
@@ -721,7 +722,7 @@
IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE));
try {
- wallpaperManager.setInAmbientMode(false /* ambientMode */, false /* animated */);
+ wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
} catch (RemoteException e) {
// Just pass, nothing critical.
}
@@ -843,16 +844,7 @@
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
-
- try {
- boolean showNav = mWindowManagerService.hasNavigationBar();
- if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
- if (showNav) {
- createNavigationBar();
- }
- } catch (RemoteException ex) {
- // no window manager? good luck with that
- }
+ createNavigationBar();
if (ENABLE_LOCKSCREEN_WALLPAPER) {
mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
@@ -927,8 +919,7 @@
Dependency.get(ExtensionController.class)
.newExtension(QS.class)
.withPlugin(QS.class)
- .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
- .withDefault(QSFragment::new)
+ .withDefault(this::createDefaultQSFragment)
.build());
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mIconController);
@@ -1016,6 +1007,10 @@
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
}
+ protected QS createDefaultQSFragment() {
+ return new QSFragment();
+ }
+
protected void setUpPresenter() {
// Set up the initial notification state.
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
@@ -1061,13 +1056,24 @@
}
protected void createNavigationBar() {
- mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
- mNavigationBar = (NavigationBarFragment) fragment;
- if (mLightBarController != null) {
- mNavigationBar.setLightBarController(mLightBarController);
- }
- mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
- });
+ try {
+ // TODO(117478341): Move this into DisplayNavigationBarController#createNavigationBars
+ // for-loop. We will also move the whole navigation bar logic together.
+ final boolean showNav = mWindowManagerService.hasNavigationBar(DEFAULT_DISPLAY);
+ if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
+ if (!showNav) return;
+
+ mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
+ mNavigationBar = (NavigationBarFragment) fragment;
+ if (mLightBarController != null) {
+ mNavigationBar.setLightBarController(mLightBarController);
+ }
+ mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
+ });
+ } catch (RemoteException ex) {
+ // no window manager? good luck with that
+ }
+ mNavigationBarController.createNavigationBars();
}
/**
@@ -1209,7 +1215,8 @@
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
+ final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(
+ mDisplay.getDisplayId());
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
}
@@ -2882,7 +2889,7 @@
mContext.unregisterReceiver(mDemoReceiver);
mAssistManager.destroy();
mHeadsUpManager.destroy();
- mStatusBarStateController.removeListener(this);
+ mStatusBarStateController.removeCallback(this);
if (mQSPanel != null && mQSPanel.getHost() != null) {
mQSPanel.getHost().destroy();
@@ -4321,7 +4328,14 @@
}, afterKeyguardGone);
}
+ @Override
public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+ startPendingIntentDismissingKeyguard(intent, null);
+ }
+
+ @Override
+ public void startPendingIntentDismissingKeyguard(
+ final PendingIntent intent, @Nullable final Runnable intentSentCallback) {
final boolean afterKeyguardGone = intent.isActivity()
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
@@ -4340,6 +4354,9 @@
if (intent.isActivity()) {
mAssistManager.hideAssist();
}
+ if (intentSentCallback != null) {
+ intentSentCallback.run();
+ }
}, afterKeyguardGone);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 484fe11..0f8970f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -151,7 +151,7 @@
mLockPatternUtils = lockPatternUtils;
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
- Dependency.get(StatusBarStateController.class).addListener(this);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
public void registerStatusBar(StatusBar statusBar,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index a743d41e..3f1e826 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -70,7 +70,7 @@
mContext = context;
mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
- mStatusBarStateController.addListener(mStateListener);
+ mStatusBarStateController.addCallback(mStateListener);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mCommandQueue = getComponent(context, CommandQueue.class);
mCommandQueue.addCallbacks(this);
@@ -206,7 +206,7 @@
}
@Override
- public boolean handleRemoteViewClick(PendingIntent pendingIntent,
+ public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
NotificationRemoteInputManager.ClickHandler defaultHandler) {
final boolean isActivity = pendingIntent.isActivity();
if (isActivity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 62b6d91..986a86d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -92,7 +92,7 @@
mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
- Dependency.get(StatusBarStateController.class).addListener(
+ Dependency.get(StatusBarStateController.class).addCallback(
mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -283,7 +283,6 @@
applyModalFlag(state);
applyBrightness(state);
applyHasTopUi(state);
- applySleepToken(state);
applyNotTouchable(state);
if (mLp.copyFrom(mLpChanged) != 0) {
mWindowManager.updateViewLayout(mStatusBarView, mLp);
@@ -328,14 +327,6 @@
mHasTopUiChanged = isExpanded(state);
}
- private void applySleepToken(State state) {
- if (state.dozing) {
- mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
- } else {
- mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
- }
- }
-
private void applyNotTouchable(State state) {
if (state.notTouchable) {
mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
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 978a72d..53e461d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -49,6 +49,7 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
+import android.view.WindowInsetsController;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
@@ -785,6 +786,11 @@
@Override
public void reportActivityRelaunched() {
}
+
+ @Override
+ public WindowInsetsController getInsetsController() {
+ return null;
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 8517d90..c2af95e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -165,7 +165,7 @@
mClockVisibleByUser = bundle.getBoolean(VISIBLE_BY_USER, true);
mShowSeconds = bundle.getBoolean(SHOW_SECONDS, false);
if (bundle.containsKey(VISIBILITY)) {
- setVisibility(bundle.getInt(VISIBILITY));
+ super.setVisibility(bundle.getInt(VISIBILITY));
}
}
@@ -203,6 +203,7 @@
// Make sure we update to the current time
updateClock();
+ updateClockVisibility();
updateShowSeconds();
}
@@ -247,6 +248,15 @@
}
};
+ @Override
+ public void setVisibility(int visibility) {
+ if (visibility == View.VISIBLE && !shouldBeVisible()) {
+ return;
+ }
+
+ super.setVisibility(visibility);
+ }
+
public void setClockVisibleByUser(boolean visible) {
mClockVisibleByUser = visible;
updateClockVisibility();
@@ -257,11 +267,15 @@
updateClockVisibility();
}
+ private boolean shouldBeVisible() {
+ return mClockVisibleByPolicy && mClockVisibleByUser;
+ }
+
private void updateClockVisibility() {
- boolean visible = mClockVisibleByPolicy && mClockVisibleByUser;
+ boolean visible = shouldBeVisible();
Dependency.get(IconLogger.class).onIconVisibility("clock", visible);
int visibility = visible ? View.VISIBLE : View.GONE;
- setVisibility(visibility);
+ super.setVisibility(visibility);
}
final void updateClock() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
index 639e50c..9c099f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.policy;
-import android.os.SystemProperties;
+import android.sysprop.VoldProperties;
/**
* Helper for determining whether the phone is decrypted yet.
@@ -26,7 +26,7 @@
public static final boolean IS_DATA_ENCRYPTED = isDataEncrypted();
private static boolean isDataEncrypted() {
- String voldState = SystemProperties.get("vold.decrypt");
+ String voldState = VoldProperties.decrypt().orElse("");
return "1".equals(voldState) || "trigger_restart_min_framework".equals(voldState);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 88ff078..2a4336e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -21,7 +21,6 @@
import android.text.method.TransformationMethod;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Size;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,6 +40,7 @@
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import java.text.BreakIterator;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
@@ -208,12 +208,14 @@
* Add smart actions to be shown next to smart replies. Only the actions that fit into the
* notification are shown.
*/
- public void addSmartActions(SmartActions smartActions) {
+ public void addSmartActions(SmartActions smartActions,
+ SmartReplyController smartReplyController, NotificationData.Entry entry) {
int numSmartActions = smartActions.actions.size();
for (int n = 0; n < numSmartActions; n++) {
Notification.Action action = smartActions.actions.get(n);
if (action.actionIntent != null) {
- Button actionButton = inflateActionButton(getContext(), this, action);
+ Button actionButton = inflateActionButton(
+ getContext(), this, n, smartActions, smartReplyController, entry);
addView(actionButton);
}
}
@@ -270,47 +272,35 @@
}
@VisibleForTesting
- Button inflateActionButton(Context context, ViewGroup root, Notification.Action action) {
+ Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
+ SmartActions smartActions, SmartReplyController smartReplyController,
+ NotificationData.Entry entry) {
+ Notification.Action action = smartActions.actions.get(actionIndex);
Button button = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_action_button, root, false);
button.setText(action.title);
Drawable iconDrawable = action.getIcon().loadDrawable(context);
// Add the action icon to the Smart Action button.
- Size newIconSize = calculateIconSizeFromSingleLineButton(context, root,
- new Size(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight()));
- iconDrawable.setBounds(0, 0, newIconSize.getWidth(), newIconSize.getHeight());
+ int newIconSize = context.getResources().getDimensionPixelSize(
+ R.dimen.smart_action_button_icon_size);
+ iconDrawable.setBounds(0, 0, newIconSize, newIconSize);
button.setCompoundDrawables(iconDrawable, null, null, null);
button.setOnClickListener(view ->
- getActivityStarter().startPendingIntentDismissingKeyguard(action.actionIntent));
+ getActivityStarter().startPendingIntentDismissingKeyguard(
+ action.actionIntent,
+ () -> smartReplyController.smartActionClicked(
+ entry, actionIndex, action, smartActions.fromAssistant)));
// TODO(b/119010281): handle accessibility
+ // Mark this as an Action button
+ final LayoutParams lp = (LayoutParams) button.getLayoutParams();
+ lp.buttonType = SmartButtonType.ACTION;
return button;
}
- private static Size calculateIconSizeFromSingleLineButton(Context context, ViewGroup root,
- Size originalIconSize) {
- Button button = (Button) LayoutInflater.from(context).inflate(
- R.layout.smart_action_button, root, false);
- // Add simple text here to ensure the button displays one line of text.
- button.setText("a");
- return calculateIconSizeFromButtonHeight(button, originalIconSize);
- }
-
- // Given a button with text on a single line - we want to add an icon to that button. This
- // method calculates the icon height to use to avoid making the button grow in height.
- private static Size calculateIconSizeFromButtonHeight(Button button, Size originalIconSize) {
- // A completely permissive measure spec should make the button text single-line.
- button.measure(MEASURE_SPEC_ANY_LENGTH, MEASURE_SPEC_ANY_LENGTH);
- int buttonHeight = button.getMeasuredHeight();
- int newIconHeight = buttonHeight / 2;
- int newIconWidth = (int) (originalIconSize.getWidth()
- * ((double) newIconHeight) / originalIconSize.getHeight());
- return new Size(newIconWidth, newIconHeight);
- }
-
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(mContext, attrs);
@@ -344,18 +334,26 @@
int displayedChildCount = 0;
int buttonPaddingHorizontal = mSingleLineButtonPaddingHorizontal;
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
+ // Set up a list of suggestions where actions come before replies. Note that the Buttons
+ // themselves have already been added to the view hierarchy in an order such that Smart
+ // Replies are shown before Smart Actions. The order of the list below determines which
+ // suggestions will be shown at all - only the first X elements are shown (where X depends
+ // on how much space each suggestion button needs).
+ List<View> smartActions = filterActionsOrReplies(SmartButtonType.ACTION);
+ List<View> smartReplies = filterActionsOrReplies(SmartButtonType.REPLY);
+ List<View> smartSuggestions = new ArrayList<>(smartActions);
+ smartSuggestions.addAll(smartReplies);
+ List<View> coveredSuggestions = new ArrayList<>();
+
+ for (View child : smartSuggestions) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
- continue;
- }
child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
buttonPaddingHorizontal, child.getPaddingBottom());
child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
+ coveredSuggestions.add(child);
+
final int lineCount = ((Button) child).getLineCount();
if (lineCount < 1 || lineCount > 2) {
// If smart reply has no text, or more than two lines, then don't show it.
@@ -409,7 +407,8 @@
// Mark all buttons from the last squeezing round as "failed to squeeze", so
// that they're re-measured without squeezing later.
- markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_FAILED, i);
+ markButtonsWithPendingSqueezeStatusAs(
+ LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions);
// The current button doesn't fit, so there's no point in measuring further
// buttons.
@@ -418,7 +417,8 @@
// The current button fits, so mark all squeezed buttons as "successfully squeezed"
// to prevent them from being un-squeezed in a subsequent squeezing round.
- markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, i);
+ markButtonsWithPendingSqueezeStatusAs(
+ LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, coveredSuggestions);
}
lp.show = true;
@@ -437,6 +437,22 @@
mPaddingTop + maxChildHeight + mPaddingBottom), heightMeasureSpec));
}
+ private List<View> filterActionsOrReplies(SmartButtonType buttonType) {
+ List<View> actions = new ArrayList<>();
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
+ continue;
+ }
+ if (lp.buttonType == buttonType) {
+ actions.add(child);
+ }
+ }
+ return actions;
+ }
+
private void resetButtonsLayoutParams() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -607,9 +623,9 @@
}
}
- private void markButtonsWithPendingSqueezeStatusAs(int squeezeStatus, int maxChildIndex) {
- for (int i = 0; i <= maxChildIndex; i++) {
- final View child = getChildAt(i);
+ private void markButtonsWithPendingSqueezeStatusAs(
+ int squeezeStatus, List<View> coveredChildren) {
+ for (View child : coveredChildren) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.squeezeStatus == LayoutParams.SQUEEZE_STATUS_PENDING) {
lp.squeezeStatus = squeezeStatus;
@@ -704,6 +720,11 @@
return mActivityStarter;
}
+ private enum SmartButtonType {
+ REPLY,
+ ACTION
+ }
+
@VisibleForTesting
static class LayoutParams extends ViewGroup.LayoutParams {
@@ -729,6 +750,7 @@
private boolean show = false;
private int squeezeStatus = SQUEEZE_STATUS_NONE;
+ private SmartButtonType buttonType = SmartButtonType.REPLY;
private LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
deleted file mode 100644
index 9b616e0..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
-import android.annotation.DrawableRes;
-import android.annotation.Nullable;
-import android.app.Dialog;
-import android.app.KeyguardManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.media.CarAudioManager;
-import android.car.media.ICarVolumeCallback;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.ServiceConnection;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.Xml;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-
-import androidx.car.widget.ListItem;
-import androidx.car.widget.ListItemAdapter;
-import androidx.car.widget.ListItemAdapter.BackgroundStyle;
-import androidx.car.widget.ListItemProvider.ListProvider;
-import androidx.car.widget.PagedListView;
-import androidx.car.widget.SeekbarListItem;
-
-import com.android.systemui.R;
-import com.android.systemui.plugins.VolumeDialog;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Car version of the volume dialog.
- *
- * Methods ending in "H" must be called on the (ui) handler.
- */
-public class CarVolumeDialogImpl implements VolumeDialog {
- private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
-
- private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
- private static final String XML_TAG_VOLUME_ITEM = "item";
- private static final int HOVERING_TIMEOUT = 16000;
- private static final int NORMAL_TIMEOUT = 3000;
- private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
- private static final int DISMISS_DELAY_IN_MILLIS = 50;
- private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
-
- private final Context mContext;
- private final H mHandler = new H();
-
- private Window mWindow;
- private CustomDialog mDialog;
- private PagedListView mListView;
- private ListItemAdapter mPagedListAdapter;
- // All the volume items.
- private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
- // Available volume items in car audio manager.
- private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
- // Volume items in the PagedListView.
- private final List<ListItem> mVolumeLineItems = new ArrayList<>();
- private final KeyguardManager mKeyguard;
-
- private Car mCar;
- private CarAudioManager mCarAudioManager;
-
- private boolean mHovering;
- private boolean mShowing;
- private boolean mExpanded;
-
- public CarVolumeDialogImpl(Context context) {
- mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
- mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mCar = Car.createCar(mContext, mServiceConnection);
- }
-
- public void init(int windowType, Callback callback) {
- initDialog();
-
- mCar.connect();
- }
-
- @Override
- public void destroy() {
- mHandler.removeCallbacksAndMessages(null);
-
- cleanupAudioManager();
- // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
- // audio manager beforehand.
- mCar.disconnect();
- }
-
- private void initDialog() {
- loadAudioUsageItems();
- mVolumeLineItems.clear();
- mDialog = new CustomDialog(mContext);
-
- mHovering = false;
- mShowing = false;
- mExpanded = false;
- mWindow = mDialog.getWindow();
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
- mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
- final WindowManager.LayoutParams lp = mWindow.getAttributes();
- lp.format = PixelFormat.TRANSLUCENT;
- lp.setTitle(VolumeDialogImpl.class.getSimpleName());
- lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.windowAnimations = -1;
- mWindow.setAttributes(lp);
- mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- mDialog.setCanceledOnTouchOutside(true);
- mDialog.setContentView(R.layout.car_volume_dialog);
- mDialog.setOnShowListener(dialog -> {
- mListView.setTranslationY(-mListView.getHeight());
- mListView.setAlpha(0);
- mListView.animate()
- .alpha(1)
- .translationY(0)
- .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
- .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
- .start();
- });
- mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
- mListView.setOnHoverListener((v, event) -> {
- int action = event.getActionMasked();
- mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
- || (action == MotionEvent.ACTION_HOVER_MOVE);
- rescheduleTimeoutH();
- return true;
- });
-
- mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
- BackgroundStyle.PANEL);
- mListView.setAdapter(mPagedListAdapter);
- mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
- }
-
- public void show(int reason) {
- mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
- }
-
- public void dismiss(int reason) {
- mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
- }
-
- private void showH(int reason) {
- if (D.BUG) {
- Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
- }
-
- mHandler.removeMessages(H.SHOW);
- mHandler.removeMessages(H.DISMISS);
- rescheduleTimeoutH();
- // Refresh the data set before showing.
- mPagedListAdapter.notifyDataSetChanged();
- if (mShowing) {
- return;
- }
- mShowing = true;
-
- mDialog.show();
- Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
- }
-
- protected void rescheduleTimeoutH() {
- mHandler.removeMessages(H.DISMISS);
- final int timeout = computeTimeoutH();
- mHandler.sendMessageDelayed(mHandler
- .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
-
- if (D.BUG) {
- Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
- }
- }
-
- private int computeTimeoutH() {
- return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
- }
-
- protected void dismissH(int reason) {
- if (D.BUG) {
- Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
- }
-
- mHandler.removeMessages(H.DISMISS);
- mHandler.removeMessages(H.SHOW);
- if (!mShowing) {
- return;
- }
-
- mListView.animate().cancel();
-
- mListView.setTranslationY(0);
- mListView.setAlpha(1);
- mListView.animate()
- .alpha(0)
- .translationY(-mListView.getHeight())
- .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
- .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
- .withEndAction(() -> mHandler.postDelayed(() -> {
- if (D.BUG) {
- Log.d(TAG, "mDialog.dismiss()");
- }
- mDialog.dismiss();
- mShowing = false;
- }, DISMISS_DELAY_IN_MILLIS))
- .start();
-
- Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
- }
-
- public void dump(PrintWriter writer) {
- writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
- writer.print(" mShowing: "); writer.println(mShowing);
- }
-
- private void loadAudioUsageItems() {
- try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
- AttributeSet attrs = Xml.asAttributeSet(parser);
- int type;
- // Traverse to the first start tag
- while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
- && type != XmlResourceParser.START_TAG) {
- }
-
- if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
- throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
- }
- int outerDepth = parser.getDepth();
- int rank = 0;
- while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
- && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlResourceParser.END_TAG) {
- continue;
- }
- if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
- TypedArray item = mContext.getResources().obtainAttributes(
- attrs, R.styleable.carVolumeItems_item);
- int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
- if (usage >= 0) {
- VolumeItem volumeItem = new VolumeItem();
- volumeItem.usage = usage;
- volumeItem.rank = rank;
- volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, 0);
- mVolumeItems.put(usage, volumeItem);
- rank++;
- }
- item.recycle();
- }
- }
- } catch (XmlPullParserException | IOException e) {
- Log.e(TAG, "Error parsing volume groups configuration", e);
- }
- }
-
- private VolumeItem getVolumeItemForUsages(int[] usages) {
- int rank = Integer.MAX_VALUE;
- VolumeItem result = null;
- for (int usage : usages) {
- VolumeItem volumeItem = mVolumeItems.get(usage);
- if (volumeItem.rank < rank) {
- rank = volumeItem.rank;
- result = volumeItem;
- }
- }
- return result;
- }
-
- private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
- }
-
- private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupMaxVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
- }
-
- private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, int volumeGroupId,
- int supplementalIconId, @Nullable View.OnClickListener supplementalIconOnClickListener) {
- SeekbarListItem listItem = new SeekbarListItem(mContext);
- listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
- int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
- int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
- listItem.setProgress(progress);
- listItem.setOnSeekBarChangeListener(
- new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
- Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
- primaryIcon.mutate().setTint(color);
- listItem.setPrimaryActionIcon(primaryIcon);
- if (supplementalIconId != 0) {
- Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
- supplementalIcon.mutate().setTint(color);
- listItem.setSupplementalIcon(supplementalIcon, true);
- listItem.setSupplementalIconListener(supplementalIconOnClickListener);
- } else {
- listItem.setSupplementalEmptyIcon(true);
- listItem.setSupplementalIconListener(null);
- }
-
- mVolumeLineItems.add(listItem);
- volumeItem.listItem = listItem;
- volumeItem.progress = progress;
- return listItem;
- }
-
- private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
- for (int i = 0; i < mVolumeItems.size(); ++i) {
- VolumeItem volumeItem = mVolumeItems.valueAt(i);
- if (volumeItem.listItem == targetItem) {
- return volumeItem;
- }
- }
- return null;
- }
-
- private void cleanupAudioManager() {
- try {
- mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- mVolumeLineItems.clear();
- mCarAudioManager = null;
- }
-
- private final class H extends Handler {
- private static final int SHOW = 1;
- private static final int DISMISS = 2;
-
- public H() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case SHOW:
- showH(msg.arg1);
- break;
- case DISMISS:
- dismissH(msg.arg1);
- break;
- default:
- }
- }
- }
-
- private final class CustomDialog extends Dialog implements DialogInterface {
- public CustomDialog(Context context) {
- super(context, com.android.systemui.R.style.qs_theme);
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- rescheduleTimeoutH();
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- protected void onStart() {
- super.setCanceledOnTouchOutside(true);
- super.onStart();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (isShowing()) {
- if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- mHandler.obtainMessage(
- H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
- return true;
- }
- }
- return false;
- }
- }
-
- private final class ExpandIconListener implements View.OnClickListener {
- @Override
- public void onClick(final View v) {
- mExpanded = !mExpanded;
- Animator inAnimator;
- if (mExpanded) {
- for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
- // Adding the items which are not coming from the default item.
- VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
- if (volumeItem.defaultItem) {
- // Set progress here due to the progress of seekbar may not be updated.
- volumeItem.listItem.setProgress(volumeItem.progress);
- } else {
- addSeekbarListItem(volumeItem, groupId, 0, null);
- }
- }
- inAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_in_rotate_up);
- } else {
- // Only keeping the default stream if it is not expended.
- Iterator itr = mVolumeLineItems.iterator();
- while (itr.hasNext()) {
- SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
- VolumeItem volumeItem = findVolumeItem(seekbarListItem);
- if (!volumeItem.defaultItem) {
- itr.remove();
- } else {
- // Set progress here due to the progress of seekbar may not be updated.
- seekbarListItem.setProgress(volumeItem.progress);
- }
- }
- inAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_in_rotate_down);
- }
-
- Animator outAnimator = AnimatorInflater.loadAnimator(
- mContext, R.anim.car_arrow_fade_out);
- inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
- AnimatorSet animators = new AnimatorSet();
- animators.playTogether(outAnimator, inAnimator);
- animators.setTarget(v);
- animators.start();
- mPagedListAdapter.notifyDataSetChanged();
- }
- }
-
- private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
- private final int mVolumeGroupId;
- private final CarAudioManager mCarAudioManager;
-
- private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
- mVolumeGroupId = volumeGroupId;
- mCarAudioManager = carAudioManager;
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (!fromUser) {
- // For instance, if this event is originated from AudioService,
- // we can ignore it as it has already been handled and doesn't need to be
- // sent back down again.
- return;
- }
- try {
- if (mCarAudioManager == null) {
- Log.w(TAG, "Ignoring volume change event because the car isn't connected");
- return;
- }
- mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
- mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {}
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {}
- }
-
- private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
- @Override
- public void onGroupVolumeChanged(int groupId, int flags) {
- VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
- int value = getSeekbarValue(mCarAudioManager, groupId);
- // Do not update the progress if it is the same as before. When car audio manager sets its
- // group volume caused by the seekbar progress changed, it also triggers this callback.
- // Updating the seekbar at the same time could block the continuous seeking.
- if (value != volumeItem.progress) {
- volumeItem.listItem.setProgress(value);
- volumeItem.progress = value;
- }
- if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
- show(Events.SHOW_REASON_VOLUME_CHANGED);
- }
- }
-
- @Override
- public void onMasterMuteChanged(int flags) {
- // ignored
- }
- };
-
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- mExpanded = false;
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
- int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
- // Populates volume slider items from volume groups to UI.
- for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
- VolumeItem volumeItem = getVolumeItemForUsages(
- mCarAudioManager.getUsagesForVolumeGroupId(groupId));
- mAvailableVolumeItems.add(volumeItem);
- // The first one is the default item.
- if (groupId == 0) {
- volumeItem.defaultItem = true;
- addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down,
- new ExpandIconListener());
- }
- }
-
- // If list is already initiated, update its content.
- if (mPagedListAdapter != null) {
- mPagedListAdapter.notifyDataSetChanged();
- }
- mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- }
-
- /**
- * This does not get called when service is properly disconnected.
- * So we need to also handle cleanups in destroy().
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- cleanupAudioManager();
- }
- };
-
- /**
- * Wrapper class which contains information of each volume group.
- */
- private static class VolumeItem {
- private @AudioAttributes.AttributeUsage int usage;
- private int rank;
- private boolean defaultItem = false;
- private @DrawableRes int icon;
- private SeekbarListItem listItem;
- private int progress;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 2861dff..0805677 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -19,12 +19,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.media.VolumePolicy;
import android.os.Bundle;
-import android.os.Handler;
import android.view.WindowManager.LayoutParams;
import com.android.settingslib.applications.InterestingConfigChanges;
@@ -57,7 +55,7 @@
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
private final SystemUI mSysui;
- private final Context mContext;
+ protected final Context mContext;
private final VolumeDialogControllerImpl mController;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
@@ -70,7 +68,7 @@
400 // vibrateToSilentDebounce
);
- public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) {
+ public VolumeDialogComponent(SystemUI sysui, Context context) {
mSysui = sysui;
mContext = context;
mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class);
@@ -81,7 +79,6 @@
Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
.withPlugin(VolumeDialog.class)
.withDefault(this::createDefault)
- .withFeature(PackageManager.FEATURE_AUTOMOTIVE, this::createCarDefault)
.withCallback(dialog -> {
if (mDialog != null) {
mDialog.destroy();
@@ -94,7 +91,7 @@
VOLUME_SILENT_DO_NOT_DISTURB);
}
- private VolumeDialog createDefault() {
+ protected VolumeDialog createDefault() {
VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
@@ -102,10 +99,6 @@
return impl;
}
- private VolumeDialog createCarDefault() {
- return new CarVolumeDialogImpl(mContext);
- }
-
@Override
public void onTuningChanged(String key, String newValue) {
if (VOLUME_DOWN_SILENT.equals(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e4f37de..f8cf793 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -22,6 +22,7 @@
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.qs.tiles.DndTile;
import java.io.FileDescriptor;
@@ -43,7 +44,9 @@
mContext.getResources().getBoolean(R.bool.enable_safety_warning);
mEnabled = enableVolumeUi || enableSafetyWarning;
if (!mEnabled) return;
- mVolumeComponent = new VolumeDialogComponent(this, mContext, null);
+
+ mVolumeComponent = SystemUIFactory.getInstance()
+ .createVolumeDialogComponent(this, mContext);
mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
putComponent(VolumeComponent.class, getVolumeComponent());
setDefaultVolumeController();
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index b32bf99..83ec33c 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -38,8 +38,6 @@
android.test.runner \
telephony-common \
android.test.base \
- android.car \
- android.car.userlib
LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index 6ac4462..ec2319d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -16,9 +16,8 @@
package com.android.systemui.doze;
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,8 +26,8 @@
import android.os.RemoteException;
import android.support.test.filters.SmallTest;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import org.junit.Before;
@@ -59,14 +58,14 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), anyLong());
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
// Make sure we're sending false when AoD is off
reset(mDozeParameters);
mDozeWallpaperState.transitionTo(DozeMachine.State.FINISH, DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
}
@Test
@@ -78,10 +77,12 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
}
@Test
@@ -93,24 +94,24 @@
mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
DozeMachine.State.DOZE_AOD);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(0L));
}
@Test
public void testTransitionTo_requestPulseIsAmbientMode() throws RemoteException {
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE,
DozeMachine.State.DOZE_REQUEST_PULSE);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
}
@Test
public void testTransitionTo_pulseIsAmbientMode() throws RemoteException {
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
- verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+ verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
}
@Test
@@ -120,6 +121,7 @@
reset(mIWallpaperManager);
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_PULSING,
DozeMachine.State.FINISH);
- verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+ verify(mIWallpaperManager).setInAmbientMode(eq(false),
+ eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
deleted file mode 100644
index f89a932..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.android.systemui.qs.car;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-
-import android.content.Context;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.LayoutInflaterBuilder;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.keyguard.CarrierText;
-import com.android.systemui.Dependency;
-import com.android.systemui.SysuiBaseFragmentTest;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.Clock;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link CarQSFragment}.
- */
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-@SmallTest
-@Ignore
-public class CarQsFragmentTest extends SysuiBaseFragmentTest {
- public CarQsFragmentTest() {
- super(CarQSFragment.class);
- }
-
- @Before
- public void initDependencies() {
- mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
- new LayoutInflaterBuilder(mContext)
- .replace("com.android.systemui.statusbar.policy.SplitClockView",
- FrameLayout.class)
- .replace("TextClock", View.class)
- .replace(CarrierText.class, View.class)
- .replace(Clock.class, View.class)
- .build());
- mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
- mDependency.injectTestDependency(Dependency.BG_LOOPER,
- TestableLooper.get(this).getLooper());
- }
-
- @Test
- @Ignore("Flaky")
- public void testLayoutInflation() {
- CarQSFragment fragment = (CarQSFragment) mFragment;
- mFragments.dispatchResume();
-
- assertNotNull(fragment.getHeader());
- assertNotNull(fragment.getFooter());
- }
-
- @Test
- @Ignore("Flaky")
- public void testListening() {
- CarQSFragment qs = (CarQSFragment) mFragment;
- mFragments.dispatchResume();
- processAllMessages();
-
- qs.setListening(true);
- processAllMessages();
-
- qs.setListening(false);
- processAllMessages();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index f63d236..26fa20d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -14,41 +14,76 @@
package com.android.systemui.qs.customize;
-import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.text.TextUtils;
+import android.util.ArraySet;
import com.android.systemui.Dependency;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class TileQueryHelperTest extends SysuiTestCase {
- @Mock private TileQueryHelper.TileStateListener mListener;
- @Mock private QSTileHost mQSTileHost;
+ private static final String CURRENT_TILES = "wifi,dnd,nfc";
+ private static final String ONLY_STOCK_TILES = "wifi,dnd";
+ private static final String WITH_OTHER_TILES = "wifi,dnd,other";
+ // Note no nfc in stock tiles
+ private static final String STOCK_TILES = "wifi,dnd,cell,battery";
+ private static final String ALL_TILES = "wifi,dnd,nfc,cell,battery";
+ private static final Set<String> FACTORY_TILES = new ArraySet<>();
+ static {
+ FACTORY_TILES.addAll(Arrays.asList(
+ new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work",
+ "rotation", "flashlight", "location", "cast", "hotspot", "user", "battery",
+ "saver", "night", "nfc"}));
+ }
+
+ @Mock
+ private TileQueryHelper.TileStateListener mListener;
+ @Mock
+ private QSTileHost mQSTileHost;
+ @Mock
+ private PackageManager mPackageManager;
+
+ private QSTile.State mState;
private TestableLooper mBGLooper;
-
private TileQueryHelper mTileQueryHelper;
@Before
@@ -56,6 +91,23 @@
MockitoAnnotations.initMocks(this);
mBGLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
+ mContext.setMockPackageManager(mPackageManager);
+
+ mState = new QSTile.State();
+ doAnswer(invocation -> {
+ String spec = (String) invocation.getArguments()[0];
+ if (FACTORY_TILES.contains(spec)) {
+ QSTile m = mock(QSTile.class);
+ when(m.isAvailable()).thenReturn(true);
+ when(m.getTileSpec()).thenReturn(spec);
+ when(m.getState()).thenReturn(mState);
+ return m;
+ } else {
+ return null;
+ }
+ }
+ ).when(mQSTileHost).createTile(anyString());
+
mTileQueryHelper = new TileQueryHelper(mContext, mListener);
}
@@ -98,4 +150,72 @@
assertTrue(mTileQueryHelper.isFinished());
}
+
+ @Test
+ public void testQueryTiles_correctTilesAndOrderOnlyStockTiles() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ ONLY_STOCK_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ String tiles = TextUtils.join(",", specs);
+ assertThat(tiles, is(equalTo(STOCK_TILES)));
+ }
+
+ @Test
+ public void testQueryTiles_correctTilesAndOrderOtherFactoryTiles() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ CURRENT_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ String tiles = TextUtils.join(",", specs);
+ assertThat(tiles, is(equalTo(ALL_TILES)));
+ }
+
+ @Test
+ public void testQueryTiles_otherTileNotIncluded() {
+ ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ WITH_OTHER_TILES);
+ mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+ STOCK_TILES);
+
+ mTileQueryHelper.queryTiles(mQSTileHost);
+
+ mBGLooper.processAllMessages();
+ waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+ verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+ List<String> specs = new ArrayList<>();
+ for (TileQueryHelper.TileInfo t : captor.getValue()) {
+ specs.add(t.spec);
+ }
+ assertFalse(specs.contains("other"));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 8ae3cd8..a4fe52d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -64,11 +66,12 @@
verify(mCallbacks).removeIcon(eq(slot));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testDisable() {
int state1 = 14;
int state2 = 42;
- mCommandQueue.disable(state1, state2);
+ mCommandQueue.disable(DEFAULT_DISPLAY, state1, state2);
waitForIdleSync();
verify(mCallbacks).disable(eq(state1), eq(state2), eq(true));
}
@@ -95,24 +98,27 @@
verify(mCallbacks).animateExpandSettingsPanel(eq(panel));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testSetSystemUiVisibility() {
Rect r = new Rect();
- mCommandQueue.setSystemUiVisibility(1, 2, 3, 4, null, r);
+ mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
waitForIdleSync();
verify(mCallbacks).setSystemUiVisibility(eq(1), eq(2), eq(3), eq(4), eq(null), eq(r));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testTopAppWindowChanged() {
- mCommandQueue.topAppWindowChanged(true);
+ mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
waitForIdleSync();
verify(mCallbacks).topAppWindowChanged(eq(true));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testShowImeButton() {
- mCommandQueue.setImeWindowStatus(null, 1, 2, true);
+ mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
waitForIdleSync();
verify(mCallbacks).setImeWindowStatus(eq(null), eq(1), eq(2), eq(true));
}
@@ -166,9 +172,10 @@
verify(mCallbacks).toggleKeyboardShortcutsMenu(eq(1));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testSetWindowState() {
- mCommandQueue.setWindowState(1, 2);
+ mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2);
waitForIdleSync();
verify(mCallbacks).setWindowState(eq(1), eq(2));
}
@@ -180,30 +187,34 @@
verify(mCallbacks).showScreenPinningRequest(eq(1));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testAppTransitionPending() {
- mCommandQueue.appTransitionPending();
+ mCommandQueue.appTransitionPending(DEFAULT_DISPLAY);
waitForIdleSync();
verify(mCallbacks).appTransitionPending(eq(false));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testAppTransitionCancelled() {
- mCommandQueue.appTransitionCancelled();
+ mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY);
waitForIdleSync();
verify(mCallbacks).appTransitionCancelled();
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testAppTransitionStarting() {
- mCommandQueue.appTransitionStarting(1, 2);
+ mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2);
waitForIdleSync();
verify(mCallbacks).appTransitionStarting(eq(1L), eq(2L), eq(false));
}
+ // TODO(b/117478341): add test case for multi-display
@Test
public void testAppTransitionFinished() {
- mCommandQueue.appTransitionFinished();
+ mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
waitForIdleSync();
verify(mCallbacks).appTransitionFinished();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index f64e84c..72d6cd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -74,7 +74,7 @@
@Mock private ShadeController mShadeController;
private NotificationViewHierarchyManager mViewHierarchyManager;
- private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);;
+ private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);
@Before
public void setUp() {
@@ -209,19 +209,19 @@
public void setChildTransferInProgress(boolean childTransferInProgress) {}
@Override
- public void changeViewPosition(View child, int newIndex) {
+ public void changeViewPosition(ExpandableView child, int newIndex) {
mRows.remove(child);
mRows.add(newIndex, child);
}
@Override
- public void notifyGroupChildAdded(View row) {}
+ public void notifyGroupChildAdded(ExpandableView row) {}
@Override
- public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {}
+ public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {}
@Override
- public void generateAddAnimation(View child, boolean fromMoreCard) {}
+ public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {}
@Override
public void generateChildOrderChangedEvent() {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index 435ede4..5558393 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -62,6 +62,7 @@
@Before
public void setUp() throws Exception {
+ when(mStatusBarWindowView.getResources()).thenReturn(mContext.getResources());
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
mLaunchAnimator = new ActivityLaunchAnimator(
mStatusBarWindowView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index b3b45eb..f94ba95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -121,9 +121,9 @@
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mNotificationData = new TestableNotificationData();
- Dependency.get(InitController.class).executePostInitTasks();
mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
mRow = new NotificationTestHelper(getContext()).createRow();
+ Dependency.get(InitController.class).executePostInitTasks();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 7fee0ee..f0fa788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 2797969..766c5d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -20,8 +20,7 @@
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@@ -54,7 +53,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
-import android.util.Log;
import android.view.View;
import com.android.systemui.SysuiTestCase;
@@ -63,8 +61,7 @@
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager
- .OnSettingsClickListener;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -298,8 +295,7 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -315,9 +311,7 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -329,8 +323,7 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -346,41 +339,7 @@
eq(false),
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
- }
-
- @Test
- public void testInitializeNotificationInfoView_noisy() throws Exception {
- NotificationInfo notificationInfoView = mock(NotificationInfo.class);
- ExpandableNotificationRow row = spy(mHelper.createRow());
- row.setBlockingHelperShowing(true);
- row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
- row.getEntry().noisy = true;
- when(row.getIsNonblockable()).thenReturn(false);
- StatusBarNotification statusBarNotification = row.getStatusBarNotification();
-
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
-
- verify(notificationInfoView).bindNotification(
- any(PackageManager.class),
- any(INotificationManager.class),
- eq(statusBarNotification.getPackageName()),
- any(NotificationChannel.class),
- anyInt(),
- eq(statusBarNotification),
- any(NotificationInfo.CheckSaveListener.class),
- any(NotificationInfo.OnSettingsClickListener.class),
- any(NotificationInfo.OnAppSettingsClickListener.class),
- eq(false),
- eq(false),
- eq(true) /* isForBlockingHelper */,
- eq(true) /* isUserSentimentNegative */,
- eq(true) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -393,8 +352,7 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -410,9 +368,7 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(IMPORTANCE_DEFAULT),
- eq(NotificationInfo.ACTION_NONE));
+ eq(IMPORTANCE_DEFAULT));
}
@Test
@@ -425,8 +381,7 @@
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_NONE);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -442,9 +397,7 @@
eq(false),
eq(false) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_NONE));
+ eq(0));
}
@Test
@@ -456,8 +409,7 @@
when(row.getIsNonblockable()).thenReturn(false);
StatusBarNotification statusBarNotification = row.getStatusBarNotification();
- mGutsManager.initializeNotificationInfo(row, notificationInfoView,
- NotificationInfo.ACTION_BLOCK);
+ mGutsManager.initializeNotificationInfo(row, notificationInfoView);
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
@@ -473,9 +425,7 @@
eq(false),
eq(true) /* isForBlockingHelper */,
eq(true) /* isUserSentimentNegative */,
- eq(false) /*isNoisy */,
- eq(0),
- eq(NotificationInfo.ACTION_BLOCK));
+ eq(0));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 985827a..d28f017 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -50,12 +50,9 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
import android.os.UserHandle;
@@ -86,8 +83,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
@SmallTest
@@ -187,7 +182,7 @@
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -200,7 +195,7 @@
.thenReturn(iconDrawable);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -209,7 +204,7 @@
public void testBindNotification_noDelegate() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
@@ -228,7 +223,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Other"));
@@ -240,7 +235,7 @@
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -257,7 +252,7 @@
.thenReturn(notificationChannelGroup);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -269,7 +264,7 @@
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@@ -278,7 +273,7 @@
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false, IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -291,7 +286,7 @@
eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
- false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ false, IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -300,7 +295,7 @@
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@@ -309,62 +304,104 @@
public void testBindNotification_BlockButton() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final View block = mNotificationInfo.findViewById(R.id.block);
- final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- final View minimize = mNotificationInfo.findViewById(R.id.minimize);
+ IMPORTANCE_DEFAULT);
+ final View block = mNotificationInfo.findViewById(R.id.int_block);
+ final View minimize = mNotificationInfo.findViewById(R.id.block_or_minimize);
assertEquals(VISIBLE, block.getVisibility());
- assertEquals(GONE, toggleSilent.getVisibility());
assertEquals(GONE, minimize.getVisibility());
}
@Test
- public void testBindNotification_SilenceButton() throws Exception {
+ public void testBindNotification_BlockButton_BlockHelper() throws Exception {
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT);
+ final View block = mNotificationInfo.findViewById(R.id.block);
+ final View interruptivenessSettings = mNotificationInfo.findViewById(
+ R.id.interruptiveness_settings);
+ assertEquals(VISIBLE, block.getVisibility());
+ assertEquals(GONE, interruptivenessSettings.getVisibility());
+ }
+
+ @Test
+ public void testBindNotification_SilenceButton_CurrentlyAlerting() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- assertEquals(VISIBLE, toggleSilent.getVisibility());
+ IMPORTANCE_DEFAULT);
+ final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+ assertEquals(VISIBLE, silent.getVisibility());
assertEquals(
- mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText());
+ mContext.getString(R.string.inline_silent_button_silent), silent.getText());
}
@Test
- public void testBindNotification_UnSilenceButton() throws Exception {
+ public void testBindNotification_SilenceButton_CurrentlySilent() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
- final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- assertEquals(VISIBLE, toggleSilent.getVisibility());
+ IMPORTANCE_LOW);
+ final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+ assertEquals(VISIBLE, silent.getVisibility());
assertEquals(
- mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText());
+ mContext.getString(R.string.inline_silent_button_stay_silent),
+ silent.getText());
}
@Test
- public void testBindNotification_SilenceButton_ChannelImportanceUnspecified() throws Exception {
- mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+ public void testBindNotification_AlertButton_CurrentlySilent() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- assertEquals(VISIBLE, toggleSilent.getVisibility());
+ IMPORTANCE_LOW);
+ final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+ assertEquals(VISIBLE, alert.getVisibility());
assertEquals(
- mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText());
+ mContext.getString(R.string.inline_silent_button_alert), alert.getText());
}
@Test
- public void testBindNotification_UnSilenceButton_ChannelImportanceUnspecified()
- throws Exception {
+ public void testBindNotification_UnSilenceButton_currentlyAlerting() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
+ final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+ assertEquals(VISIBLE, alert.getVisibility());
+ assertEquals(
+ mContext.getString(R.string.inline_silent_button_keep_alerting), alert.getText());
+ }
+
+ @Test
+ public void testBindNotification_ChannelImportanceUnspecified_NotifAlerting() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
- final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
- assertEquals(VISIBLE, toggleSilent.getVisibility());
+ IMPORTANCE_DEFAULT);
+ final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+ final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+ assertEquals(VISIBLE, silent.getVisibility());
+ assertEquals(VISIBLE, alert.getVisibility());
assertEquals(
- mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText());
+ mContext.getString(R.string.inline_silent_button_silent), silent.getText());
+ assertEquals(
+ mContext.getString(R.string.inline_silent_button_keep_alerting), alert.getText());
+ }
+
+ @Test
+ public void testBindNotification_ChannelImportanceUnspecified_NotifSilent() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_LOW);
+ final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+ final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+ assertEquals(VISIBLE, silent.getVisibility());
+ assertEquals(VISIBLE, alert.getVisibility());
+ assertEquals(
+ mContext.getString(R.string.inline_silent_button_stay_silent), silent.getText());
+ assertEquals(
+ mContext.getString(R.string.inline_silent_button_alert), alert.getText());
}
@Test
@@ -372,10 +409,13 @@
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final View block = mNotificationInfo.findViewById(R.id.block);
+ final View interruptivenessSettings = mNotificationInfo.findViewById(
+ R.id.interruptiveness_settings);
final View minimize = mNotificationInfo.findViewById(R.id.minimize);
assertEquals(GONE, block.getVisibility());
+ assertEquals(GONE, interruptivenessSettings.getVisibility());
assertEquals(VISIBLE, minimize.getVisibility());
}
@@ -387,7 +427,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, true, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -399,7 +439,7 @@
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -411,7 +451,7 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
- }, null, false, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, false, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -420,11 +460,11 @@
public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
(View v, NotificationChannel c, int appUid) -> {
- }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, true, false, IMPORTANCE_DEFAULT);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@@ -433,7 +473,7 @@
public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
}
@@ -442,7 +482,7 @@
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
- true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ true, true, IMPORTANCE_DEFAULT);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
}
@@ -455,7 +495,7 @@
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, true, true, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -468,7 +508,7 @@
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ null, true, true, IMPORTANCE_DEFAULT);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -479,7 +519,7 @@
public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
- null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ null, true, true, IMPORTANCE_DEFAULT);
final TextView blockView = mNotificationInfo.findViewById(R.id.block);
assertEquals(GONE, blockView.getVisibility());
}
@@ -488,7 +528,7 @@
public void testbindNotification_BlockingHelper() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
- true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ true, true, IMPORTANCE_DEFAULT);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -498,7 +538,7 @@
public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -509,7 +549,7 @@
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -520,9 +560,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -534,7 +574,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
mTestableLooper.processAllMessages();
@@ -548,9 +588,9 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_silent).performClick();
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -562,9 +602,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_alert).performClick();
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -576,7 +616,7 @@
int originalImportance = mNotificationChannel.getImportance();
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -591,7 +631,7 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
@@ -609,10 +649,10 @@
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
- true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+ );
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -631,10 +671,10 @@
TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */,
- true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+ );
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -653,8 +693,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -682,8 +721,7 @@
10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -711,8 +749,8 @@
10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
null /* onSettingsClick */, null /* onAppSettingsClick */ ,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true, true /* isUserSentimentNegative */, false /* isNoisy */,
- IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ true, true /* isUserSentimentNegative */, /* isNoisy */
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
@@ -731,8 +769,7 @@
null /* onSettingsClick */, null /* onAppSettingsClick */,
true /* provisioned */,
false /* isNonblockable */, true /* isForBlockingHelper */,
- true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
mTestableLooper.processAllMessages();
@@ -755,7 +792,7 @@
true /* isForBlockingHelper */,
true,
false /* isUserSentimentNegative */,
- false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
NotificationGuts guts = mock(NotificationGuts.class);
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
mNotificationInfo.setGutsParent(guts);
@@ -770,7 +807,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -784,9 +821,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -817,7 +854,7 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
true /* isUserSentimentNegative */,
- false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.block).performClick();
waitForUndoButton();
@@ -839,7 +876,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -854,7 +891,7 @@
mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -875,7 +912,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.handleCloseControls(true, false);
@@ -893,9 +930,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.findViewById(R.id.undo).performClick();
waitForStopButton();
@@ -914,8 +951,8 @@
public void testMinUndoDoesNotMinNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -937,9 +974,9 @@
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -958,9 +995,9 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -980,9 +1017,9 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -1002,9 +1039,9 @@
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_LOW);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -1019,11 +1056,11 @@
}
@Test
- public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception {
+ public void testCloseControlsDoesNotUpdateMinIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1038,10 +1075,10 @@
public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.handleCloseControls(false, false);
@@ -1056,9 +1093,9 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
- }, null, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ }, null, null, true, true, IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
ensureNoUndoButton();
mNotificationInfo.handleCloseControls(true, false);
@@ -1074,10 +1111,10 @@
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, false, IMPORTANCE_DEFAULT,
- NotificationInfo.ACTION_NONE);
+ }, null, null, true, false, IMPORTANCE_DEFAULT
+ );
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
@@ -1091,124 +1128,6 @@
}
@Test
- public void testDisplaySettingsLink() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- final String settingsText = "work chats";
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
- (View v, Intent intent) -> {
- latch.countDown();
- }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(View.VISIBLE, settingsLink.getVisibility());
- settingsLink.performClick();
- assertEquals(0, latch.getCount());
- }
-
- @Test
- public void testDisplaySettingsLink_multipleChannels() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- final String settingsText = "work chats";
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
- (View v, Intent intent) -> {
- latch.countDown();
- }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(View.VISIBLE, settingsLink.getVisibility());
- settingsLink.performClick();
- assertEquals(0, latch.getCount());
- }
-
- @Test
- public void testNoSettingsLink_noHandlingActivity() throws Exception {
- final String settingsText = "work chats";
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
- null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(GONE, settingsLink.getVisibility());
- }
-
- @Test
- public void testNoSettingsLink_noLinkText() throws Exception {
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(GONE, settingsLink.getVisibility());
- }
-
- @Test
- public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception {
- final String settingsText = "work chats";
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = TEST_PACKAGE_NAME;
- ri.activityInfo.name = "something";
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
- .setSettingsText(settingsText).build();
- StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
- 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
- true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
- final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
- assertEquals(GONE, settingsLink.getVisibility());
- }
-
-
- @Test
public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception {
assertFalse(mNotificationInfo.willBeRemoved());
}
@@ -1219,7 +1138,7 @@
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ IMPORTANCE_DEFAULT);
mNotificationInfo.findViewById(R.id.minimize).performClick();
waitForUndoButton();
@@ -1231,10 +1150,10 @@
public void testUndoText_block() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
assertTrue(confirmationText.getText().toString().contains("won't see"));
@@ -1244,10 +1163,10 @@
public void testUndoText_silence() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_silent).performClick();
waitForUndoButton();
TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
assertEquals(mContext.getString(R.string.notification_channel_silenced),
@@ -1258,10 +1177,10 @@
public void testUndoText_unsilence() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+ mNotificationInfo.findViewById(R.id.int_alert).performClick();
waitForUndoButton();
TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
assertEquals(mContext.getString(R.string.notification_channel_unsilenced),
@@ -1272,10 +1191,10 @@
public void testNoHeaderOnConfirmation() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
assertEquals(GONE, mNotificationInfo.findViewById(R.id.header).getVisibility());
}
@@ -1284,69 +1203,13 @@
public void testHeaderOnUndo() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+ IMPORTANCE_DEFAULT);
- mNotificationInfo.findViewById(R.id.block).performClick();
+ mNotificationInfo.findViewById(R.id.int_block).performClick();
waitForUndoButton();
mNotificationInfo.findViewById(R.id.undo).performClick();
waitForStopButton();
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
}
-
- @Test
- public void testBindNotificationWithInitialBlockAction() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_BLOCK);
- waitForUndoButton();
- mNotificationInfo.handleCloseControls(true, false);
-
- mTestableLooper.processAllMessages();
- ArgumentCaptor<NotificationChannel> updated =
- ArgumentCaptor.forClass(NotificationChannel.class);
- verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
- anyString(), eq(TEST_UID), updated.capture());
- assertTrue((updated.getValue().getUserLockedFields()
- & USER_LOCKED_IMPORTANCE) != 0);
- assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
- }
-
- @Test
- public void testBindNotificationWithInitialSilenceAction() throws Exception {
- mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_TOGGLE_SILENT);
- waitForUndoButton();
- mNotificationInfo.handleCloseControls(true, false);
-
- mTestableLooper.processAllMessages();
- ArgumentCaptor<NotificationChannel> updated =
- ArgumentCaptor.forClass(NotificationChannel.class);
- verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
- anyString(), eq(TEST_UID), updated.capture());
- assertTrue((updated.getValue().getUserLockedFields()
- & USER_LOCKED_IMPORTANCE) != 0);
- assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
- }
-
- @Test
- public void testBindNotificationWithInitialUnSilenceAction() throws Exception {
- mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
- true, IMPORTANCE_LOW, NotificationInfo.ACTION_TOGGLE_SILENT);
- waitForUndoButton();
- mNotificationInfo.handleCloseControls(true, false);
-
- mTestableLooper.processAllMessages();
- ArgumentCaptor<NotificationChannel> updated =
- ArgumentCaptor.forClass(NotificationChannel.class);
- verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
- anyString(), eq(TEST_UID), updated.capture());
- assertTrue((updated.getValue().getUserLockedFields()
- & USER_LOCKED_IMPORTANCE) != 0);
- assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 906e718..51492da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -25,7 +25,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Notification;
+import android.app.NotificationChannel;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -35,6 +35,7 @@
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
@@ -43,19 +44,26 @@
import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper()
+@RunWithLooper(setAsMainLooper = true)
@SmallTest
public class NotificationMenuRowTest extends LeakCheckedTest {
+ private ExpandableNotificationRow mRow;
+
@Before
public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+ mRow = mock(ExpandableNotificationRow.class);
+ NotificationData.Entry entry = new NotificationData.Entry(
+ mock(StatusBarNotification.class));
+ entry.channel = mock(NotificationChannel.class);
+ when(mRow.getEntry()).thenReturn(entry);
}
@Test
public void testAttachDetach() {
NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
- row.createMenu(null, null);
+ row.createMenu(mRow, null);
ViewUtils.attachView(row.getMenuView());
TestableLooper.get(this).processAllMessages();
ViewUtils.detachView(row.getMenuView());
@@ -65,9 +73,9 @@
@Test
public void testRecreateMenu() {
NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
- row.createMenu(null, null);
+ row.createMenu(mRow, null);
assertTrue(row.getMenuView() != null);
- row.createMenu(null, null);
+ row.createMenu(mRow, null);
assertTrue(row.getMenuView() != null);
}
@@ -81,12 +89,7 @@
@Test
public void testNoAppOpsInSlowSwipe() {
NotificationMenuRow row = new NotificationMenuRow(mContext);
- Notification n = mock(Notification.class);
- StatusBarNotification sbn = mock(StatusBarNotification.class);
- when(sbn.getNotification()).thenReturn(n);
- ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
- when(parent.getStatusBarNotification()).thenReturn(sbn);
- row.createMenu(parent, null);
+ row.createMenu(mRow, null);
ViewGroup container = (ViewGroup) row.getMenuView();
// one for snooze and one for noti blocking
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index a4a111a..662e016 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -26,11 +26,11 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import org.junit.Assert;
import org.junit.Before;
@@ -45,7 +45,7 @@
public class NotificationRoundnessManagerTest extends SysuiTestCase {
private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
- private HashSet<View> mAnimatedChildren = new HashSet<>();
+ private HashSet<ExpandableView> mAnimatedChildren = new HashSet<>();
private Runnable mRoundnessCallback = mock(Runnable.class);
private ExpandableNotificationRow mFirst;
private ExpandableNotificationRow mSecond;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 93d8aad..d99e46d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -40,7 +40,7 @@
import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper()
@SmallTest
public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index f8ad298..31014b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -37,7 +37,6 @@
private static final int EMPTY_MARGIN = 0;
private static final int EMPTY_HEIGHT = 0;
private static final boolean SECURE_LOCKED = false;
- private static final boolean PULSING_NO = false;
private static final float ZERO_DRAG = 0.f;
private static final float OPAQUE = 1.f;
private static final float TRANSPARENT = 0.f;
@@ -48,6 +47,7 @@
private float mPanelExpansion;
private int mKeyguardStatusHeight;
private float mDark;
+ private boolean mPulsing;
@Before
public void setUp() {
@@ -171,6 +171,156 @@
assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
}
+ @Test
+ public void notifPositionMiddleOfScreenOnAOD() {
+ // GIVEN on AOD and both stack scroll and clock have 0 height
+ givenAOD();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionIndependentOfKeyguardStatusHeightOnAOD() {
+ // GIVEN on AOD and clock has a nonzero height
+ givenAOD();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = 100;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionWithLargeClockOnAOD() {
+ // GIVEN on AOD and clock has a nonzero height
+ givenAOD();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = SCREEN_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is, unfortunately, the entire screen.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(SCREEN_HEIGHT);
+ }
+
+ @Test
+ public void notifPositionWhilePulsingOnAOD() {
+ // GIVEN on AOD and pulsing
+ givenAOD();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ mPulsing = true;
+ mClockPositionAlgorithm.setPulsingPadding(200);
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the notif padding doesn't adjust for pulsing.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionMiddleOfScreenOnLockScreen() {
+ // GIVEN on lock screen and both stack scroll and clock have 0 height
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionAdjustsForStackHeightOnLockScreen() {
+ // GIVEN on lock screen and stack scroller has a nonzero height
+ givenLockScreen();
+ mNotificationStackHeight = 500;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding adjusts for the expanded notif stack.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(750);
+ }
+
+ @Test
+ public void notifPositionAdjustsForClockHeightOnLockScreen() {
+ // GIVEN on lock screen and stack scroller has a nonzero height
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = 200;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding adjusts for both clock and notif stack.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionAdjustsForStackHeightAndClockHeightOnLockScreen() {
+ // GIVEN on lock screen and stack scroller has a nonzero height
+ givenLockScreen();
+ mNotificationStackHeight = 500;
+ mKeyguardStatusHeight = 200;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding adjusts for both clock and notif stack.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(810);
+ }
+
+ @Test
+ public void notifPositionWithLargeClockOnLockScreen() {
+ // GIVEN on lock screen and clock has a nonzero height
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = SCREEN_HEIGHT;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+ }
+
+ @Test
+ public void notifPositionWithFullDragOnLockScreen() {
+ // GIVEN the lock screen is dragged up
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ mPanelExpansion = 0.f;
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the notif padding is zero.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+ }
+
+ @Test
+ public void notifPositionWithLargeClockFullDragOnLockScreen() {
+ // GIVEN the lock screen is dragged up and a full screen clock
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = SCREEN_HEIGHT;
+ mPanelExpansion = 0.f;
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the notif padding is zero.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+ }
+
+ @Test
+ public void notifPositionWhilePulsingOnLockScreen() {
+ // GIVEN on lock screen and pulsing
+ givenLockScreen();
+ mNotificationStackHeight = EMPTY_HEIGHT;
+ mKeyguardStatusHeight = EMPTY_HEIGHT;
+ mPulsing = true;
+ mClockPositionAlgorithm.setPulsingPadding(200);
+ // WHEN the clock position algorithm is run
+ positionClock();
+ // THEN the notif padding adjusts for pulsing.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1200);
+ }
+
private void givenAOD() {
mPanelExpansion = 1.f;
mDark = 1.f;
@@ -184,7 +334,7 @@
private void positionClock() {
mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mDark, SECURE_LOCKED,
- PULSING_NO, ZERO_DRAG);
+ mPulsing, ZERO_DRAG);
mClockPositionAlgorithm.run(mClockPosition);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index 4177cd1..1783d0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -38,12 +38,12 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.content.Context;
import com.android.systemui.R;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.SysuiTestCase;
+import android.content.Context;
import android.content.res.Resources;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -78,6 +78,7 @@
mProxyService = mock(OverviewProxyService.class);
mProxy = mock(IOverviewProxy.Stub.class);
doReturn(mProxy).when(mProxyService).getProxy();
+ doReturn(true).when(mProxyService).shouldShowSwipeUpUI();
mDependency.injectTestDependency(OverviewProxyService.class, mProxyService);
mStatusBar = mock(StatusBar.class);
@@ -106,6 +107,18 @@
}
@Test
+ public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
+ doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
+ mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
+ null /* swipeLeftAction */, null /* swipeRightAction */);
+
+ MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
+ assertFalse(mController.onInterceptTouchEvent(ev));
+ verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
+ assertNull(mController.getCurrentAction());
+ }
+
+ @Test
public void testHasActionDetectGesturesTouchdown() throws Exception {
MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
@@ -395,6 +408,7 @@
verify(mProxy, times(1)).onQuickScrubStart();
verify(mProxyService, times(1)).notifyQuickScrubStarted();
verify(mNavigationBarView, times(1)).updateSlippery();
+ verify(mProxy, never()).onMotionEvent(moveEvent1);
// Move again for scrub
MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, 200, y);
@@ -402,6 +416,7 @@
assertEquals(action, mController.getCurrentAction());
verify(action, times(1)).onGestureMove(200, y);
verify(mProxy, times(1)).onQuickScrubProgress(1f / 2);
+ verify(mProxy, never()).onMotionEvent(moveEvent2);
// Action up
MotionEvent upEvent = event(MotionEvent.ACTION_UP, 1, y);
@@ -409,6 +424,7 @@
assertNull(mController.getCurrentAction());
verify(action, times(1)).onGestureEnd();
verify(mProxy, times(1)).onQuickScrubEnd();
+ verify(mProxy, never()).onMotionEvent(upEvent);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 2104721..59a4937 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.StatusBarManager;
@@ -63,9 +64,11 @@
mContext.putComponent(CommandQueue.class, mCommandQueue);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
+ StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
+ when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
mStatusBar = new StatusBarNotificationPresenter(mContext,
mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
- mock(StatusBarWindowView.class), mock(NotificationListContainerViewGroup.class),
+ statusBarWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.Callback.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index b6e3fc1..506fa97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -62,6 +62,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -430,12 +431,18 @@
private void setSmartActions(String[] actionTitles) {
mView.resetSmartSuggestions(mContainer);
- mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false));
+ mView.addSmartActions(
+ new SmartReplyView.SmartActions(createActions(actionTitles), false),
+ mLogger,
+ mEntry);
}
private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
setSmartReplies(choices);
- mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false));
+ mView.addSmartActions(
+ new SmartReplyView.SmartActions(createActions(actionTitles), false),
+ mLogger,
+ mEntry);
}
private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -553,7 +560,7 @@
mView.getChildAt(2).performClick();
- verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any());
+ verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
}
@Test
@@ -738,7 +745,9 @@
}
private Button inflateActionButton(Notification.Action action) {
- return mView.inflateActionButton(getContext(), mView, action);
+ return mView.inflateActionButton(getContext(), mView, 0,
+ new SmartReplyView.SmartActions(Collections.singletonList(action), false),
+ mLogger, mEntry);
}
@Test
@@ -792,4 +801,55 @@
assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
}
+
+ @Test
+ public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() {
+ String[] choices = new String[] {"Reply"};
+ String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new String[0], 2,
+ createActions(new String[] {
+ "Looooooong \nactioooon", "second \naction", "third \naction"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ // smart replies
+ assertReplyButtonHidden(mView.getChildAt(0));
+ // smart actions
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+ }
+
+ @Test
+ public void testMeasure_choicesAndActionsPrioritizeActions() {
+ String[] choices = new String[] {"Short", "longer reply"};
+ String[] actions = new String[] {"Looooooong actioooon", "second action"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2,
+ createActions(new String[] {"Looooooong \nactioooon", "second \naction"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ Button firstAction = ((Button) mView.getChildAt(1));
+
+ assertEqualMeasures(expectedView, mView);
+ // smart replies
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonHidden(mView.getChildAt(1));
+ // smart actions
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+ }
}
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/AdaptiveIconChangeOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/AdaptiveIconChangeOverlay/Android.mk
index e642a68..6e3b8cb 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/AdaptiveIconChangeOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := SquareIcon
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := SquareIconOverlay
LOCAL_SDK_VERSION := current
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml
similarity index 72%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml
index a1bd582..33da510 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml
@@ -16,12 +16,12 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
- android:versionCode="1"
- android:versionName="1.0">
+ package="com.android.theme.icon.square"
+ android:versionCode="1"
+ android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="android.theme.customization.adaptive_icon_shape"
android:priority="1"/>
- <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+ <application android:label="@string/square_icon_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/AdaptiveIconChangeOverlay/res/values/config.xml b/packages/overlays/AdaptiveIconChangeOverlay/res/values/config.xml
new file mode 100644
index 0000000..54623f5
--- /dev/null
+++ b/packages/overlays/AdaptiveIconChangeOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+ <string name="config_icon_mask" translatable="false">"M50,0L100,0 100,100 0,100 0,0z"</string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml
similarity index 84%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml
index 5ca9d15..64b7d0d 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+ <!-- Square icon overlay [DO NOT TRANSLATE] -->
+ <string name="square_icon_overlay">Square Icons</string>
+
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
index e642a68..ecad420 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarDefault
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarDefaultOverlay
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
similarity index 92%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
index a1bd582..1639fc5 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
+ package="com.android.internal.experiment.navbar.default"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="com.android.internal.experiment_navbar_default"
android:priority="1"/>
<application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
similarity index 70%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
index 4c3571a..d8b69cd 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
@@ -17,12 +17,8 @@
*/
-->
<resources>
- <!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">36dp</dimen>
- <!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">36dp</dimen>
<!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_width">36dp</dimen>
+ <dimen name="navigation_bar_frame_width">48dp</dimen>
<!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_height">36dp</dimen>
+ <dimen name="navigation_bar_frame_height">48dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
index 5ca9d15..c933290 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
@@ -18,5 +18,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+ <string name="experiment_navigationbar_overlay">Default Navigation Bar Experiment (48dp)</string>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
index b4b2b16..b4cc34f 100644
--- a/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
@@ -16,7 +16,7 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.floating"
+ package="com.android.internal.experiment.navbar.type.floating"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
index 6a58453..30bca3c 100644
--- a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
@@ -21,8 +21,4 @@
<dimen name="navigation_bar_height">0dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
<dimen name="navigation_bar_width">0dp</dimen>
- <!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_height">48dp</dimen>
- <!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_width">48dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
index e642a68..58cf134 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim24
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay24
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
similarity index 93%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
index a1bd582..aee543a 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
+ package="com.android.internal.experiment.navbar.slim24"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="com.android.internal.experiment_navbar_slim24"
android:priority="1"/>
<application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
similarity index 82%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
index 4c3571a..58c653d 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">36dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">36dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_width">36dp</dimen>
+ <dimen name="navigation_bar_frame_width">24dp</dimen>
<!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_height">36dp</dimen>
+ <dimen name="navigation_bar_frame_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
similarity index 95%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
index 5ca9d15..670bc55 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+ <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (24dp)</string>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
index e642a68..7ebbb74 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim32
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay32
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
similarity index 93%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
rename to packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
index a1bd582..10cf6a1 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
+ package="com.android.internal.experiment.navbar.slim32"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="com.android.internal.experiment_navbar_slim32"
android:priority="1"/>
<application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
similarity index 82%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
index 4c3571a..00dd8fe 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">36dp</dimen>
+ <dimen name="navigation_bar_height">32dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">36dp</dimen>
+ <dimen name="navigation_bar_width">32dp</dimen>
<!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_width">36dp</dimen>
+ <dimen name="navigation_bar_frame_width">32dp</dimen>
<!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_height">36dp</dimen>
+ <dimen name="navigation_bar_frame_height">32dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
similarity index 95%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
index 5ca9d15..b48661c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+ <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (32dp)</string>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
similarity index 88%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
rename to packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
index e642a68..28354e3 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
@@ -17,14 +17,14 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim40
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay40
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
similarity index 93%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
index a1bd582..ce8133f 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.internal.experiment.navbar.slim"
+ package="com.android.internal.experiment.navbar.slim40"
android:versionCode="1"
android:versionName="1.0">
<overlay android:targetPackage="android"
- android:category="com.android.internal.experiment_navbar_slim"
+ android:category="com.android.internal.experiment_navbar_slim40"
android:priority="1"/>
<application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
similarity index 82%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
index 4c3571a..4e65f33 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">36dp</dimen>
+ <dimen name="navigation_bar_height">40dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">36dp</dimen>
+ <dimen name="navigation_bar_width">40dp</dimen>
<!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
- <dimen name="navigation_bar_frame_width">36dp</dimen>
+ <dimen name="navigation_bar_frame_width">40dp</dimen>
<!-- Width of the navigation bar frame when it is placed vertically on the screen -->
- <dimen name="navigation_bar_frame_height">36dp</dimen>
+ <dimen name="navigation_bar_frame_height">40dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
similarity index 95%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
index 5ca9d15..8fe3a5c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+ <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (40dp)</string>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml
deleted file mode 100644
index 8cce570..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"লাহী নেভিগে’শ্বন বাৰ সম্পৰীক্ষা"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml
deleted file mode 100644
index c0ab3b1..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"স্লিম নেভিগেশন বার সম্পর্কিত পরীক্ষা"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml
deleted file mode 100644
index 96418ae..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"સ્લિમ નૅવિગેશન બારનો પ્રયોગ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml
deleted file mode 100644
index ccdddea..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ಸ್ಲಿಮ್ ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್ ಪ್ರಯೋಗ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml
deleted file mode 100644
index b65afe3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"സ്ലിം നാവിഗേഷൻ ബാർ പരീക്ഷണം"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml
deleted file mode 100644
index 6022b7f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"पातलो नेभिगेसन पट्टीको परीक्षण"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml
deleted file mode 100644
index 1db9783..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ସ୍ଲିମ୍ ନାଭିଗେସନ୍ ବାର୍ର ପ୍ରୟୋଗ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml
deleted file mode 100644
index a782f46..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ਸਲਿਮ ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ ਪੱਟੀ ਪ੍ਰਯੋਗ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml
deleted file mode 100644
index a1abb64..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"සිහින් සංචාලන තීරු අත්දැකීම"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml
deleted file mode 100644
index 9e95c38..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"மெலிதான வழிசெலுத்துதல் பட்டி சோதனை"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml
deleted file mode 100644
index d273ab7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"సన్నని నావిగేషన్ పట్టీ ప్రయోగం"</string>
-</resources>
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 8d691ff..12e7376 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -273,33 +273,6 @@
private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- if (DEBUG_ALL) Slog.i(LOG_TAG, "dispatchTransformedEvent(event = " + event + ")");
-
- // If the touchscreen event is within the magnified portion of the screen we have
- // to change its location to be where the user thinks he is poking the
- // UI which may have been magnified and panned.
- if (mMagnificationController.isMagnifying()
- && event.isFromSource(SOURCE_TOUCHSCREEN)
- && mMagnificationController.magnificationRegionContains(
- event.getX(), event.getY())) {
- final float scale = mMagnificationController.getScale();
- final float scaledOffsetX = mMagnificationController.getOffsetX();
- final float scaledOffsetY = mMagnificationController.getOffsetY();
- final int pointerCount = event.getPointerCount();
- PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
- PointerProperties[] properties = getTempPointerPropertiesWithMinSize(
- pointerCount);
- for (int i = 0; i < pointerCount; i++) {
- event.getPointerCoords(i, coords[i]);
- coords[i].x = (coords[i].x - scaledOffsetX) / scale;
- coords[i].y = (coords[i].y - scaledOffsetY) / scale;
- event.getPointerProperties(i, properties[i]);
- }
- event = MotionEvent.obtain(event.getDownTime(),
- event.getEventTime(), event.getAction(), pointerCount, properties,
- coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
- event.getFlags());
- }
if (DEBUG_EVENT_STREAM) {
storeEventInto(mDebugOutputEventHistory, event);
try {
diff --git a/services/art-profile b/services/art-profile
index bdd49de..af9d7a9 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -18849,7 +18849,7 @@
PLcom/android/server/wm/WindowManagerService;->onSystemUiStarted()V
PLcom/android/server/wm/WindowManagerService;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransition(Ljava/lang/String;IILandroid/os/IRemoteCallback;)V
-PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
PLcom/android/server/wm/WindowManagerService;->performBootTimeout()V
PLcom/android/server/wm/WindowManagerService;->performEnableScreen()V
PLcom/android/server/wm/WindowManagerService;->postWindowRemoveCleanupLocked(Lcom/android/server/wm/WindowState;)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c56f31e..0da07ae 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -175,10 +175,6 @@
}
};
- // TODO(b/117779333): move to superclass / create super-class for ShellCommand
- @GuardedBy("mLock")
- private boolean mAllowInstantService;
-
/**
* Supported modes for Augmented Autofill Smart Suggestions.
*/
@@ -271,6 +267,11 @@
addCompatibilityModeRequestsLocked(service, userId);
}
+ @Override // from AbstractMasterSystemService
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ }
+
@Override // from SystemService
public void onStart() {
publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
@@ -290,7 +291,7 @@
// Called by Shell command.
void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
if (userId != UserHandle.USER_ALL) {
@@ -313,7 +314,7 @@
// Called by Shell command.
void listSessions(int userId, IResultReceiver receiver) {
Slog.i(TAG, "listSessions() for userId " + userId);
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
final Bundle resultData = new Bundle();
final ArrayList<String> sessions = new ArrayList<>();
@@ -340,7 +341,7 @@
// Called by Shell command.
void reset() {
Slog.i(TAG, "reset()");
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
visitServicesLocked((s) -> s.destroyLocked());
@@ -351,7 +352,7 @@
// Called by Shell command.
void setLogLevel(int level) {
Slog.i(TAG, "setLogLevel(): " + level);
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -388,7 +389,7 @@
// Called by Shell command.
int getLogLevel() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
@@ -399,7 +400,7 @@
// Called by Shell command.
int getMaxPartitions() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (mLock) {
return sPartitionMaxCount;
@@ -408,8 +409,8 @@
// Called by Shell command.
void setMaxPartitions(int max) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxPartitions(): " + max);
+ enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -433,7 +434,7 @@
// Called by Shell command.
int getMaxVisibleDatasets() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
synchronized (sLock) {
return sVisibleDatasetsMaxCount;
@@ -442,8 +443,8 @@
// Called by Shell command.
void setMaxVisibleDatasets(int max) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
+ enforceCallingPermissionForManagement();
final long token = Binder.clearCallingIdentity();
try {
@@ -480,7 +481,7 @@
// Called by Shell command.
void getScore(@Nullable String algorithmName, @NonNull String value1,
@NonNull String value2, @NonNull RemoteCallback callback) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
final FieldClassificationStrategy strategy =
new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
@@ -491,33 +492,16 @@
// Called by Shell command.
Boolean getFullScreenMode() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
return sFullScreenMode;
}
// Called by Shell command.
void setFullScreenMode(@Nullable Boolean mode) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ enforceCallingPermissionForManagement();
sFullScreenMode = mode;
}
- // Called by Shell command.
- boolean getAllowInstantService() {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
- synchronized (mLock) {
- return mAllowInstantService;
- }
- }
-
- // Called by Shell command.
- void setAllowInstantService(boolean mode) {
- getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
- Slog.i(TAG, "setAllowInstantService(): " + mode);
- synchronized (mLock) {
- mAllowInstantService = mode;
- }
- }
-
private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
com.android.server.autofill.Helper.sDebug = debug;
android.view.autofill.Helper.sDebug = debug;
@@ -1218,7 +1202,6 @@
mAutofillCompatState.dump(prefix, pw);
pw.print("from settings: ");
pw.println(getWhitelistedCompatModePackagesFromSettings());
- pw.print("Allow instant service: "); pw.println(mAllowInstantService);
if (mSupportedSmartSuggestionModes != 0) {
pw.print("Smart Suggestion modes: ");
pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes));
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0df99d4..18bc856 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -190,6 +190,11 @@
return mInfo.getServiceInfo();
}
+ @Override // from PerUserSystemService
+ protected String getDefaultComponentName() {
+ return getComponentNameFromSettings();
+ }
+
@Nullable
String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
@@ -369,7 +374,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- final String autoFillService = getComponentNameFromSettings();
+ final String autoFillService = getComponentNameLocked();
final ComponentName componentName = serviceInfo.getComponentName();
if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 9aa9d7c..af65759 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -40,9 +40,9 @@
import android.text.format.DateUtils;
import android.util.Slog;
-import com.android.server.AbstractRemoteService;
+import com.android.server.AbstractSinglePendingRequestRemoteService;
-final class RemoteFillService extends AbstractRemoteService {
+final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService> {
private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
@@ -69,8 +69,8 @@
mCallbacks = callbacks;
}
- @Override
- protected void onConnectedStateChanged(boolean state) {
+ @Override // from AbstractRemoteService
+ protected void handleOnConnectedStateChanged(boolean state) {
if (mAutoFillService == null) {
Slog.w(mTag, "onConnectedStateChanged(): null service");
return;
@@ -82,18 +82,18 @@
}
}
- @Override
+ @Override // from AbstractRemoteService
protected IInterface getServiceInterface(IBinder service) {
mAutoFillService = IAutoFillService.Stub.asInterface(service);
return mAutoFillService;
}
- @Override
+ @Override // from AbstractRemoteService
protected long getTimeoutIdleBindMillis() {
return TIMEOUT_IDLE_BIND_MILLIS;
}
- @Override
+ @Override // from AbstractRemoteService
protected long getRemoteRequestMillis() {
return TIMEOUT_REMOTE_REQUEST_MILLIS;
}
@@ -136,6 +136,19 @@
scheduleRequest(new PendingSaveRequest(request, this));
}
+ private boolean handleResponseCallbackCommon(
+ @NonNull PendingRequest<RemoteFillService> pendingRequest) {
+ if (isDestroyed()) return false;
+
+ if (mPendingRequest == pendingRequest) {
+ mPendingRequest = null;
+ }
+ if (mPendingRequest == null) {
+ scheduleUnbind();
+ }
+ return true;
+ }
+
private void dispatchOnFillRequestSuccess(@NonNull PendingFillRequest pendingRequest,
@Nullable FillResponse response, int requestFlags) {
mHandler.post(() -> {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8676f7f..4c64507 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -902,7 +902,7 @@
// VultureCallback
@Override
- public void onServiceDied(AbstractRemoteService service) {
+ public void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service) {
Slog.w(TAG, "removing session because service died");
forceRemoveSelfLocked();
}
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 645723e..d1dbbff 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,7 +1,5 @@
anniemeng@google.com
-artikz@google.com
brufino@google.com
bryanmawhinney@google.com
ctate@google.com
jorlow@google.com
-mkarpinski@google.com
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index df46d260b..2bca34d 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -16,12 +16,15 @@
package com.android.server.backup;
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
import android.content.ContentResolver;
import android.os.Handler;
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.KeyValueSettingObserver;
import android.util.Slog;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -137,7 +140,7 @@
public long getKvBackupAgentTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "getKvBackupAgentTimeoutMillis(): " + mKvBackupAgentTimeoutMillis);
}
return mKvBackupAgentTimeoutMillis;
@@ -146,7 +149,7 @@
public long getFullBackupAgentTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupAgentTimeoutMillis(): " + mFullBackupAgentTimeoutMillis);
}
return mFullBackupAgentTimeoutMillis;
@@ -155,7 +158,7 @@
public long getSharedBackupAgentTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getSharedBackupAgentTimeoutMillis(): " + mSharedBackupAgentTimeoutMillis);
@@ -166,7 +169,7 @@
public long getRestoreAgentTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "getRestoreAgentTimeoutMillis(): " + mRestoreAgentTimeoutMillis);
}
return mRestoreAgentTimeoutMillis;
@@ -175,7 +178,7 @@
public long getRestoreAgentFinishedTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getRestoreAgentFinishedTimeoutMillis(): "
@@ -187,7 +190,7 @@
public long getQuotaExceededTimeoutMillis() {
synchronized (mLock) {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getQuotaExceededTimeoutMillis(): "
diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index ec21961..785d3ca 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -16,6 +16,8 @@
package com.android.server.backup;
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
import android.app.AlarmManager;
import android.content.ContentResolver;
import android.os.Handler;
@@ -24,6 +26,7 @@
import android.util.KeyValueListParser;
import android.util.KeyValueSettingObserver;
import android.util.Slog;
+
import com.android.internal.annotations.VisibleForTesting;
/**
@@ -151,7 +154,7 @@
// group the calls of these methods in a block syncrhonized on
// a reference of this object.
public synchronized long getKeyValueBackupIntervalMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getKeyValueBackupIntervalMilliseconds(...) returns "
@@ -161,7 +164,7 @@
}
public synchronized long getKeyValueBackupFuzzMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getKeyValueBackupFuzzMilliseconds(...) returns "
@@ -171,7 +174,7 @@
}
public synchronized boolean getKeyValueBackupRequireCharging() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getKeyValueBackupRequireCharging(...) returns "
@@ -181,7 +184,7 @@
}
public synchronized int getKeyValueBackupRequiredNetworkType() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getKeyValueBackupRequiredNetworkType(...) returns "
@@ -191,7 +194,7 @@
}
public synchronized long getFullBackupIntervalMilliseconds() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getFullBackupIntervalMilliseconds(...) returns "
@@ -201,14 +204,14 @@
}
public synchronized boolean getFullBackupRequireCharging() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
}
return mFullBackupRequireCharging;
}
public synchronized int getFullBackupRequiredNetworkType() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getFullBackupRequiredNetworkType(...) returns "
@@ -219,7 +222,7 @@
/** Returns an array of package names that should be notified whenever a backup finishes. */
public synchronized String[] getBackupFinishedNotificationReceivers() {
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(
TAG,
"getBackupFinishedNotificationReceivers(...) returns "
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e4b4bc5..0b06f28 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,239 +16,62 @@
package com.android.server.backup;
-import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
-
-import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
-import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKUP;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
-
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.app.AppGlobals;
-import android.app.IActivityManager;
-import android.app.IBackupAgent;
-import android.app.PendingIntent;
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupManager;
-import android.app.backup.BackupManagerMonitor;
-import android.app.backup.FullBackup;
-import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.Message;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.PowerManager.ServiceType;
-import android.os.PowerSaveState;
-import android.os.Process;
import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.WorkSource;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.EventLog;
-import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.server.AppWidgetBackupBridge;
-import com.android.server.EventLogTags;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import com.android.server.backup.fullbackup.FullBackupEntry;
-import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
-import com.android.server.backup.internal.BackupHandler;
-import com.android.server.backup.internal.ClearDataObserver;
-import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
-import com.android.server.backup.internal.PerformInitializeTask;
-import com.android.server.backup.internal.ProvisionedObserver;
-import com.android.server.backup.internal.RunBackupReceiver;
-import com.android.server.backup.internal.RunInitializeReceiver;
-import com.android.server.backup.keyvalue.BackupRequest;
-import com.android.server.backup.params.AdbBackupParams;
-import com.android.server.backup.params.AdbParams;
-import com.android.server.backup.params.AdbRestoreParams;
-import com.android.server.backup.params.BackupParams;
-import com.android.server.backup.params.ClearParams;
-import com.android.server.backup.params.ClearRetryParams;
-import com.android.server.backup.params.RestoreParams;
-import com.android.server.backup.restore.ActiveRestoreSession;
-import com.android.server.backup.restore.PerformUnifiedRestoreTask;
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.backup.utils.AppBackupUtils;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
-import com.android.server.backup.utils.BackupObserverUtils;
-import com.android.server.backup.utils.SparseArrayUtils;
-import com.google.android.collect.Sets;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.io.RandomAccessFile;
-import java.security.SecureRandom;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.Random;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
-/** System service that performs backup/restore operations. */
+/**
+ * Definition of the system service that performs backup/restore operations.
+ *
+ * <p>This class is responsible for handling user-aware operations and acts as a delegator, routing
+ * incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the
+ * corresponding backup/restore operation.
+ */
public class BackupManagerService {
public static final String TAG = "BackupManagerService";
public static final boolean DEBUG = true;
public static final boolean MORE_DEBUG = false;
public static final boolean DEBUG_SCHEDULING = true;
- // File containing backup-enabled state. Contains a single byte;
- // nonzero == enabled. File missing or contains a zero byte == disabled.
+ // File containing backup-enabled state. Contains a single byte to denote enabled status.
+ // Nonzero is enabled; file missing or a zero byte is disabled.
private static final String BACKUP_ENABLE_FILE = "backup_enabled";
- // Persistently track the need to do a full init.
- private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
-
- // System-private key used for backing up an app's widget state. Must
- // begin with U+FFxx by convention (we reserve all keys starting
- // with U+FF00 or higher for system use).
- public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
-
- // Name and current contents version of the full-backup manifest file
- //
- // Manifest version history:
- //
- // 1 : initial release
- public static final String BACKUP_MANIFEST_FILENAME = "_manifest";
- public static final int BACKUP_MANIFEST_VERSION = 1;
-
- // External archive format version history:
- //
- // 1 : initial release
- // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
- // 3 : introduced "_meta" metadata file; no other format change per se
- // 4 : added support for new device-encrypted storage locations
- // 5 : added support for key-value packages
- public static final int BACKUP_FILE_VERSION = 5;
- public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
- public static final String BACKUP_METADATA_FILENAME = "_meta";
- public static final int BACKUP_METADATA_VERSION = 1;
- public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
-
- private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
-
- // Round-robin queue for scheduling full backup passes.
- private static final int SCHEDULE_FILE_VERSION = 1;
-
- public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
- public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
-
- // Pseudoname that we use for the Package Manager metadata "package".
- public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
-
- // Retry interval for clear/init when the transport is unavailable
- private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
-
- public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
- public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
- public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
- public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
-
- // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
- // pending operations list.
- public static final int OP_PENDING = 0;
- private static final int OP_ACKNOWLEDGED = 1;
- private static final int OP_TIMEOUT = -1;
-
- // Waiting for backup agent to respond during backup operation.
- public static final int OP_TYPE_BACKUP_WAIT = 0;
-
- // Waiting for backup agent to respond during restore operation.
- public static final int OP_TYPE_RESTORE_WAIT = 1;
-
- // An entire backup operation spanning multiple packages.
- public static final int OP_TYPE_BACKUP = 2;
-
- // Time delay for initialization operations that can be delayed so as not to consume too much
- // CPU on bring-up and increase time-to-UI.
- private static final long INITIALIZATION_DELAY_MILLIS = 3000;
-
- // Timeout interval for deciding that a bind or clear-data has taken too long
- private static final long TIMEOUT_INTERVAL = 10 * 1000;
-
- // User confirmation timeout for a full backup/restore operation. It's this long in
- // order to give them time to enter the backup password.
- private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
-
- // If an app is busy when we want to do a full-data backup, how long to defer the retry.
- // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
- private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
- private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
-
// The published binder is a singleton Trampoline object that calls through to the proper code.
// This indirection lets us turn down the heavy implementation object on the fly without
// disturbing binders that have been cached elsewhere in the system.
@@ -302,483 +125,25 @@
transportManager);
}
- private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- private final TransportManager mTransportManager;
+ private UserBackupManagerService mUserBackupManagerService;
- private Context mContext;
- private PackageManager mPackageManager;
- private IPackageManager mPackageManagerBinder;
- private IActivityManager mActivityManager;
- private PowerManager mPowerManager;
- private AlarmManager mAlarmManager;
- private IStorageManager mStorageManager;
- private BackupManagerConstants mConstants;
- private PowerManager.WakeLock mWakelock;
- private BackupHandler mBackupHandler;
-
- private IBackupManager mBackupManagerBinder;
-
- private boolean mEnabled; // access to this is synchronized on 'this'
- private boolean mProvisioned;
- private boolean mAutoRestore;
-
- private PendingIntent mRunBackupIntent;
- private PendingIntent mRunInitIntent;
-
- private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
-
- // map UIDs to the set of participating packages under that UID
- private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
-
- // Backups that we haven't started yet. Keys are package names.
- private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
-
- // locking around the pending-backup management
- private final Object mQueueLock = new Object();
-
- // The thread performing the sequence of queued backups binds to each app's agent
- // in succession. Bind notifications are asynchronously delivered through the
- // Activity Manager; use this lock object to signal when a requested binding has
- // completed.
- private final Object mAgentConnectLock = new Object();
- private IBackupAgent mConnectedAgent;
- private volatile boolean mConnecting;
-
- private volatile boolean mBackupRunning;
- private volatile long mLastBackupPass;
-
- // A similar synchronization mechanism around clearing apps' data for restore
- private final Object mClearDataLock = new Object();
- private volatile boolean mClearingData;
-
- // Used by ADB.
- private final BackupPasswordManager mBackupPasswordManager;
- private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
- private final SecureRandom mRng = new SecureRandom();
-
- // Time when we post the transport registration operation
- private final long mRegisterTransportsRequestedTime;
-
- @GuardedBy("mQueueLock")
- private PerformFullTransportBackupTask mRunningFullBackupTask;
-
- @GuardedBy("mQueueLock")
- private ArrayList<FullBackupEntry> mFullBackupQueue;
-
- @GuardedBy("mPendingRestores")
- private boolean mIsRestoreInProgress;
-
- @GuardedBy("mPendingRestores")
- private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
-
- private ActiveRestoreSession mActiveRestoreSession;
-
- // Watch the device provisioning operation during setup
- private ContentObserver mProvisionedObserver;
-
- /**
- * mCurrentOperations contains the list of currently active operations.
- *
- * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
- * An operation wraps a BackupRestoreTask within it.
- * It's the responsibility of this task to remove the operation from this array.
- *
- * A BackupRestore task gets notified of ack/timeout for the operation via
- * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
- * on the mCurrentOpLock.
- * {@link BackupManagerService#waitUntilOperationComplete(int)} is
- * used in various places to 'wait' for notifyAll and detect change of pending state of an
- * operation. So typically, an operation will be removed from this array by:
- * - BackupRestoreTask#handleCancel and
- * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
- * these places because waitUntilOperationComplete relies on the operation being present to
- * determine its completion status.
- *
- * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
- * cancel backup tasks.
- */
- @GuardedBy("mCurrentOpLock")
- private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
- private final Object mCurrentOpLock = new Object();
- private final Random mTokenGenerator = new Random();
- final AtomicInteger mNextToken = new AtomicInteger();
-
- // Where we keep our journal files and other bookkeeping.
- private File mBaseStateDir;
- private File mDataDir;
- private File mJournalDir;
- @Nullable
- private DataChangedJournal mJournal;
- private File mFullBackupScheduleFile;
-
- // Keep a log of all the apps we've ever backed up.
- private ProcessedPackagesJournal mProcessedPackagesJournal;
-
- private File mTokenFile;
- private Set<String> mAncestralPackages = null;
- private long mAncestralToken = 0;
- private long mCurrentToken = 0;
-
- @VisibleForTesting
+ /** Instantiate a new instance of {@link BackupManagerService}. */
public BackupManagerService(
Context context,
- Trampoline parent,
+ Trampoline trampoline,
HandlerThread backupThread,
File baseStateDir,
File dataDir,
TransportManager transportManager) {
- mContext = context;
- mPackageManager = context.getPackageManager();
- mPackageManagerBinder = AppGlobals.getPackageManager();
- mActivityManager = ActivityManager.getService();
-
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
-
- mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
-
- mAgentTimeoutParameters = new
- BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
- mAgentTimeoutParameters.start();
-
- // spin up the backup/restore handler thread
- mBackupHandler = new BackupHandler(this, backupThread.getLooper());
-
- // Set up our bookkeeping
- final ContentResolver resolver = context.getContentResolver();
- mProvisioned = Settings.Global.getInt(resolver,
- Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- mAutoRestore = Settings.Secure.getInt(resolver,
- Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
-
- mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
- resolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
- false, mProvisionedObserver);
-
- mBaseStateDir = baseStateDir;
- mBaseStateDir.mkdirs();
- if (!SELinux.restorecon(mBaseStateDir)) {
- Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
- }
-
- mDataDir = dataDir;
-
- mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
-
- // Alarm receivers for scheduled backups & initialization operations
- BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
- IntentFilter filter = new IntentFilter();
- filter.addAction(RUN_BACKUP_ACTION);
- context.registerReceiver(mRunBackupReceiver, filter,
- android.Manifest.permission.BACKUP, null);
-
- BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
- filter = new IntentFilter();
- filter.addAction(RUN_INITIALIZE_ACTION);
- context.registerReceiver(mRunInitReceiver, filter,
- android.Manifest.permission.BACKUP, null);
-
- Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
- backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
-
- Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
- initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
-
- // Set up the backup-request journaling
- mJournalDir = new File(mBaseStateDir, "pending");
- mJournalDir.mkdirs(); // creates mBaseStateDir along the way
- mJournal = null; // will be created on first use
-
- mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
- // We are observing changes to the constants throughout the lifecycle of BMS. This is
- // because we reference the constants in multiple areas of BMS, which otherwise would
- // require frequent starting and stopping.
- mConstants.start();
-
- // Set up the various sorts of package tracking we do
- mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
- initPackageTracking();
-
- // Build our mapping of uid to backup client services. This implicitly
- // schedules a backup pass on the Package Manager metadata the first
- // time anything needs to be backed up.
- synchronized (mBackupParticipants) {
- addPackageParticipantsLocked(null);
- }
-
- mTransportManager = transportManager;
- mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
- mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
- mBackupHandler.postDelayed(
- mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
-
- // Now that we know about valid backup participants, parse any leftover journal files into
- // the pending backup set
- mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
-
- // Power management
- mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+ mUserBackupManagerService =
+ new UserBackupManagerService(
+ context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
}
-
- public BackupManagerConstants getConstants() {
- return mConstants;
- }
-
- public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
- return mAgentTimeoutParameters;
- }
-
- public Context getContext() {
- return mContext;
- }
-
- public void setContext(Context context) {
- mContext = context;
- }
-
- public PackageManager getPackageManager() {
- return mPackageManager;
- }
-
- public void setPackageManager(PackageManager packageManager) {
- mPackageManager = packageManager;
- }
-
- public IPackageManager getPackageManagerBinder() {
- return mPackageManagerBinder;
- }
-
- public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
- mPackageManagerBinder = packageManagerBinder;
- }
-
- public IActivityManager getActivityManager() {
- return mActivityManager;
- }
-
- public void setActivityManager(IActivityManager activityManager) {
- mActivityManager = activityManager;
- }
-
- public AlarmManager getAlarmManager() {
- return mAlarmManager;
- }
-
- public void setAlarmManager(AlarmManager alarmManager) {
- mAlarmManager = alarmManager;
- }
-
+ // TODO(b/118520567): Remove when tests are modified to use per-user instance.
@VisibleForTesting
- void setPowerManager(PowerManager powerManager) {
- mPowerManager = powerManager;
- }
-
- public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
- mBackupManagerBinder = backupManagerBinder;
- }
-
- public TransportManager getTransportManager() {
- return mTransportManager;
- }
-
- public boolean isEnabled() {
- return mEnabled;
- }
-
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- }
-
- public boolean isProvisioned() {
- return mProvisioned;
- }
-
- public void setProvisioned(boolean provisioned) {
- mProvisioned = provisioned;
- }
-
- public PowerManager.WakeLock getWakelock() {
- return mWakelock;
- }
-
- /**
- * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
- * #getWakelock()}.
- */
- @VisibleForTesting
- public void setWorkSource(@Nullable WorkSource workSource) {
- // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
- mWakelock.setWorkSource(workSource);
- }
-
- public void setWakelock(PowerManager.WakeLock wakelock) {
- mWakelock = wakelock;
- }
-
- public Handler getBackupHandler() {
- return mBackupHandler;
- }
-
- public void setBackupHandler(BackupHandler backupHandler) {
- mBackupHandler = backupHandler;
- }
-
- public PendingIntent getRunInitIntent() {
- return mRunInitIntent;
- }
-
- public void setRunInitIntent(PendingIntent runInitIntent) {
- mRunInitIntent = runInitIntent;
- }
-
- public HashMap<String, BackupRequest> getPendingBackups() {
- return mPendingBackups;
- }
-
- public void setPendingBackups(
- HashMap<String, BackupRequest> pendingBackups) {
- mPendingBackups = pendingBackups;
- }
-
- public Object getQueueLock() {
- return mQueueLock;
- }
-
- public boolean isBackupRunning() {
- return mBackupRunning;
- }
-
- public void setBackupRunning(boolean backupRunning) {
- mBackupRunning = backupRunning;
- }
-
- public long getLastBackupPass() {
- return mLastBackupPass;
- }
-
- public void setLastBackupPass(long lastBackupPass) {
- mLastBackupPass = lastBackupPass;
- }
-
- public Object getClearDataLock() {
- return mClearDataLock;
- }
-
- public boolean isClearingData() {
- return mClearingData;
- }
-
- public void setClearingData(boolean clearingData) {
- mClearingData = clearingData;
- }
-
- public boolean isRestoreInProgress() {
- return mIsRestoreInProgress;
- }
-
- public void setRestoreInProgress(boolean restoreInProgress) {
- mIsRestoreInProgress = restoreInProgress;
- }
-
- public Queue<PerformUnifiedRestoreTask> getPendingRestores() {
- return mPendingRestores;
- }
-
- public ActiveRestoreSession getActiveRestoreSession() {
- return mActiveRestoreSession;
- }
-
- public void setActiveRestoreSession(
- ActiveRestoreSession activeRestoreSession) {
- mActiveRestoreSession = activeRestoreSession;
- }
-
- public SparseArray<Operation> getCurrentOperations() {
- return mCurrentOperations;
- }
-
- public Object getCurrentOpLock() {
- return mCurrentOpLock;
- }
-
- public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
- return mAdbBackupRestoreConfirmations;
- }
-
- public File getBaseStateDir() {
- return mBaseStateDir;
- }
-
- public void setBaseStateDir(File baseStateDir) {
- mBaseStateDir = baseStateDir;
- }
-
- public File getDataDir() {
- return mDataDir;
- }
-
- public void setDataDir(File dataDir) {
- mDataDir = dataDir;
- }
-
- @Nullable
- public DataChangedJournal getJournal() {
- return mJournal;
- }
-
- public void setJournal(@Nullable DataChangedJournal journal) {
- mJournal = journal;
- }
-
- public SecureRandom getRng() {
- return mRng;
- }
-
- public Set<String> getAncestralPackages() {
- return mAncestralPackages;
- }
-
- public void setAncestralPackages(Set<String> ancestralPackages) {
- mAncestralPackages = ancestralPackages;
- }
-
- public long getAncestralToken() {
- return mAncestralToken;
- }
-
- public void setAncestralToken(long ancestralToken) {
- mAncestralToken = ancestralToken;
- }
-
- public long getCurrentToken() {
- return mCurrentToken;
- }
-
- public void setCurrentToken(long currentToken) {
- mCurrentToken = currentToken;
- }
-
- public ArraySet<String> getPendingInits() {
- return mPendingInits;
- }
-
- /** Clear all pending transport initializations. */
- public void clearPendingInits() {
- mPendingInits.clear();
- }
-
- public PerformFullTransportBackupTask getRunningFullBackupTask() {
- return mRunningFullBackupTask;
- }
-
- public void setRunningFullBackupTask(
- PerformFullTransportBackupTask runningFullBackupTask) {
- mRunningFullBackupTask = runningFullBackupTask;
+ void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
+ mUserBackupManagerService = userBackupManagerService;
}
/**
@@ -819,2092 +184,93 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- /**
- * Utility: build a new random integer token. The low bits are the ordinal of the operation for
- * near-time uniqueness, and the upper bits are random for app-side unpredictability.
+ /*
+ * The following methods are implementations of IBackupManager methods called from Trampoline.
+ * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
+ * action on the passed in user. Currently this is a straight redirection (see TODO).
*/
- public int generateRandomIntegerToken() {
- int token = mTokenGenerator.nextInt();
- if (token < 0) token = -token;
- token &= ~0xFF;
- token |= (mNextToken.incrementAndGet() & 0xFF);
- return token;
+ // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+
+ // ---------------------------------------------
+ // BACKUP AGENT OPERATIONS
+ // ---------------------------------------------
+
+ /**
+ * An app's backup agent calls this method to let the service know that there's new data to
+ * backup for their app {@code packageName}. Only used for apps participating in key-value
+ * backup.
+ */
+ public void dataChanged(String packageName) {
+ mUserBackupManagerService.dataChanged(packageName);
}
/**
- * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
- * non-lifecycle agent instance, so we manually set up the context topology for it.
+ * Callback: a requested backup agent has been instantiated. This should only be called from the
+ * {@link ActivityManager}.
*/
- public BackupAgent makeMetadataAgent() {
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
- pmAgent.attach(mContext);
- pmAgent.onCreate();
- return pmAgent;
+ public void agentConnected(String packageName, IBinder agentBinder) {
+ mUserBackupManagerService.agentConnected(packageName, agentBinder);
}
/**
- * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
+ * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
+ * called from the {@link ActivityManager}.
*/
- public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
- PackageManagerBackupAgent pmAgent =
- new PackageManagerBackupAgent(mPackageManager, packages);
- pmAgent.attach(mContext);
- pmAgent.onCreate();
- return pmAgent;
- }
-
- private void initPackageTracking() {
- if (MORE_DEBUG) Slog.v(TAG, "` tracking");
-
- // Remember our ancestral dataset
- mTokenFile = new File(mBaseStateDir, "ancestral");
- try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
- new FileInputStream(mTokenFile)))) {
- int version = tokenStream.readInt();
- if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
- mAncestralToken = tokenStream.readLong();
- mCurrentToken = tokenStream.readLong();
-
- int numPackages = tokenStream.readInt();
- if (numPackages >= 0) {
- mAncestralPackages = new HashSet<>();
- for (int i = 0; i < numPackages; i++) {
- String pkgName = tokenStream.readUTF();
- mAncestralPackages.add(pkgName);
- }
- }
- }
- } catch (FileNotFoundException fnf) {
- // Probably innocuous
- Slog.v(TAG, "No ancestral data");
- } catch (IOException e) {
- Slog.w(TAG, "Unable to read token file", e);
- }
-
- mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
- mProcessedPackagesJournal.init();
-
- synchronized (mQueueLock) {
- // Resume the full-data backup queue
- mFullBackupQueue = readFullBackupSchedule();
- }
-
- // Register for broadcasts about package install, etc., so we can
- // update the provider list.
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
- // Register for events related to sdcard installation.
- IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
- }
-
- private ArrayList<FullBackupEntry> readFullBackupSchedule() {
- boolean changed = false;
- ArrayList<FullBackupEntry> schedule = null;
- List<PackageInfo> apps =
- PackageManagerBackupAgent.getStorableApplications(mPackageManager);
-
- if (mFullBackupScheduleFile.exists()) {
- try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
- BufferedInputStream bufStream = new BufferedInputStream(fstream);
- DataInputStream in = new DataInputStream(bufStream)) {
- int version = in.readInt();
- if (version != SCHEDULE_FILE_VERSION) {
- Slog.e(TAG, "Unknown backup schedule version " + version);
- return null;
- }
-
- final int numPackages = in.readInt();
- schedule = new ArrayList<>(numPackages);
-
- // HashSet instead of ArraySet specifically because we want the eventual
- // lookups against O(hundreds) of entries to be as fast as possible, and
- // we discard the set immediately after the scan so the extra memory
- // overhead is transient.
- HashSet<String> foundApps = new HashSet<>(numPackages);
-
- for (int i = 0; i < numPackages; i++) {
- String pkgName = in.readUTF();
- long lastBackup = in.readLong();
- foundApps.add(pkgName); // all apps that we've addressed already
- try {
- PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
- if (AppBackupUtils.appGetsFullBackup(pkg)
- && AppBackupUtils.appIsEligibleForBackup(
- pkg.applicationInfo, mPackageManager)) {
- schedule.add(new FullBackupEntry(pkgName, lastBackup));
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Package " + pkgName
- + " no longer eligible for full backup");
- }
- }
- } catch (NameNotFoundException e) {
- if (DEBUG) {
- Slog.i(TAG, "Package " + pkgName
- + " not installed; dropping from full backup");
- }
- }
- }
-
- // New apps can arrive "out of band" via OTA and similar, so we also need to
- // scan to make sure that we're tracking all full-backup candidates properly
- for (PackageInfo app : apps) {
- if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mPackageManager)) {
- if (!foundApps.contains(app.packageName)) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "New full backup app " + app.packageName + " found");
- }
- schedule.add(new FullBackupEntry(app.packageName, 0));
- changed = true;
- }
- }
- }
-
- Collections.sort(schedule);
- } catch (Exception e) {
- Slog.e(TAG, "Unable to read backup schedule", e);
- mFullBackupScheduleFile.delete();
- schedule = null;
- }
- }
-
- if (schedule == null) {
- // no prior queue record, or unable to read it. Set up the queue
- // from scratch.
- changed = true;
- schedule = new ArrayList<>(apps.size());
- for (PackageInfo info : apps) {
- if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
- info.applicationInfo, mPackageManager)) {
- schedule.add(new FullBackupEntry(info.packageName, 0));
- }
- }
- }
-
- if (changed) {
- writeFullBackupScheduleAsync();
- }
- return schedule;
- }
-
- private Runnable mFullBackupScheduleWriter = new Runnable() {
- @Override
- public void run() {
- synchronized (mQueueLock) {
- try {
- ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
- DataOutputStream bufOut = new DataOutputStream(bufStream);
- bufOut.writeInt(SCHEDULE_FILE_VERSION);
-
- // version 1:
- //
- // [int] # of packages in the queue = N
- // N * {
- // [utf8] package name
- // [long] last backup time for this package
- // }
- int numPackages = mFullBackupQueue.size();
- bufOut.writeInt(numPackages);
-
- for (int i = 0; i < numPackages; i++) {
- FullBackupEntry entry = mFullBackupQueue.get(i);
- bufOut.writeUTF(entry.packageName);
- bufOut.writeLong(entry.lastBackup);
- }
- bufOut.flush();
-
- AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
- FileOutputStream out = af.startWrite();
- out.write(bufStream.toByteArray());
- af.finishWrite(out);
- } catch (Exception e) {
- Slog.e(TAG, "Unable to write backup schedule!", e);
- }
- }
- }
- };
-
- private void writeFullBackupScheduleAsync() {
- mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
- mBackupHandler.post(mFullBackupScheduleWriter);
- }
-
- private void parseLeftoverJournals() {
- ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
- for (DataChangedJournal journal : journals) {
- if (!journal.equals(mJournal)) {
- try {
- journal.forEach(packageName -> {
- Slog.i(TAG, "Found stale backup journal, scheduling");
- if (MORE_DEBUG) Slog.i(TAG, " " + packageName);
- dataChangedImpl(packageName);
- });
- } catch (IOException e) {
- Slog.e(TAG, "Can't read " + journal, e);
- }
- }
- }
- }
-
- /** Used for generating random salts or passwords. */
- public byte[] randomBytes(int bits) {
- byte[] array = new byte[bits / 8];
- mRng.nextBytes(array);
- return array;
- }
-
- /** For adb backup/restore. */
- public boolean setBackupPassword(String currentPw, String newPw) {
- return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
- }
-
- /** For adb backup/restore. */
- public boolean hasBackupPassword() {
- return mBackupPasswordManager.hasBackupPassword();
- }
-
- /** For adb backup/restore. */
- public boolean backupPasswordMatches(String currentPw) {
- return mBackupPasswordManager.backupPasswordMatches(currentPw);
+ public void agentDisconnected(String packageName) {
+ mUserBackupManagerService.agentDisconnected(packageName);
}
/**
- * Maintain persistent state around whether need to do an initialize operation. This will lock
- * on {@link #getQueueLock()}.
+ * Used by a currently-active backup agent to notify the service that it has completed its given
+ * outstanding asynchronous backup/restore operation.
*/
- public void recordInitPending(
- boolean isPending, String transportName, String transportDirName) {
- synchronized (mQueueLock) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
- }
-
- File stateDir = new File(mBaseStateDir, transportDirName);
- File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
-
- if (isPending) {
- // We need an init before we can proceed with sending backup data.
- // Record that with an entry in our set of pending inits, as well as
- // journaling it via creation of a sentinel file.
- mPendingInits.add(transportName);
- try {
- (new FileOutputStream(initPendingFile)).close();
- } catch (IOException ioe) {
- // Something is badly wrong with our permissions; just try to move on
- }
- } else {
- // No more initialization needed; wipe the journal and reset our state.
- initPendingFile.delete();
- mPendingInits.remove(transportName);
- }
- }
+ public void opComplete(int token, long result) {
+ mUserBackupManagerService.opComplete(token, result);
}
- /**
- * Reset all of our bookkeeping because the backend data has been wiped (for example due to idle
- * expiry), so we must re-upload all saved settings.
- */
- public void resetBackupState(File stateFileDir) {
- synchronized (mQueueLock) {
- mProcessedPackagesJournal.reset();
+ // ---------------------------------------------
+ // TRANSPORT OPERATIONS
+ // ---------------------------------------------
- mCurrentToken = 0;
- writeRestoreTokens();
-
- // Remove all the state files
- for (File sf : stateFileDir.listFiles()) {
- // ... but don't touch the needs-init sentinel
- if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
- sf.delete();
- }
- }
- }
-
- // Enqueue a new backup of every participant
- synchronized (mBackupParticipants) {
- final int numParticipants = mBackupParticipants.size();
- for (int i = 0; i < numParticipants; i++) {
- HashSet<String> participants = mBackupParticipants.valueAt(i);
- if (participants != null) {
- for (String packageName : participants) {
- dataChangedImpl(packageName);
- }
- }
- }
- }
- }
-
- private void onTransportRegistered(String transportName, String transportDirName) {
- if (DEBUG) {
- long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
- Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
- + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
- }
-
- File stateDir = new File(mBaseStateDir, transportDirName);
- stateDir.mkdirs();
-
- File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
- if (initSentinel.exists()) {
- synchronized (mQueueLock) {
- mPendingInits.add(transportName);
-
- // TODO: pick a better starting time than now + 1 minute
- long delay = 1000 * 60; // one minute, in milliseconds
- mAlarmManager.set(AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + delay, mRunInitIntent);
- }
- }
- }
-
- // ----- Track installation/removal of packages -----
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
-
- String action = intent.getAction();
- boolean replacing = false;
- boolean added = false;
- boolean changed = false;
- Bundle extras = intent.getExtras();
- String[] pkgList = null;
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_REMOVED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
- final String pkgName = uri.getSchemeSpecificPart();
- if (pkgName != null) {
- pkgList = new String[]{pkgName};
- }
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
-
- // At package-changed we only care about looking at new transport states
- if (changed) {
- final String[] components =
- intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
-
- if (MORE_DEBUG) {
- Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
- for (int i = 0; i < components.length; i++) {
- Slog.i(TAG, " * " + components[i]);
- }
- }
-
- mBackupHandler.post(
- () -> mTransportManager.onPackageChanged(pkgName, components));
- return; // nothing more to do in the PACKAGE_CHANGED case
- }
-
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
- replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- added = true;
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- added = false;
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- }
-
- if (pkgList == null || pkgList.length == 0) {
- return;
- }
-
- final int uid = extras.getInt(Intent.EXTRA_UID);
- if (added) {
- synchronized (mBackupParticipants) {
- if (replacing) {
- // This is the package-replaced case; we just remove the entry
- // under the old uid and fall through to re-add. If an app
- // just added key/value backup participation, this picks it up
- // as a known participant.
- removePackageParticipantsLocked(pkgList, uid);
- }
- addPackageParticipantsLocked(pkgList);
- }
- // If they're full-backup candidates, add them there instead
- final long now = System.currentTimeMillis();
- for (final String packageName : pkgList) {
- try {
- PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
- if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(
- app.applicationInfo, mPackageManager)) {
- enqueueFullBackup(packageName, now);
- scheduleNextFullBackupJob(0);
- } else {
- // The app might have just transitioned out of full-data into
- // doing key/value backups, or might have just disabled backups
- // entirely. Make sure it is no longer in the full-data queue.
- synchronized (mQueueLock) {
- dequeueFullBackupLocked(packageName);
- }
- writeFullBackupScheduleAsync();
- }
-
- mBackupHandler.post(
- () -> mTransportManager.onPackageAdded(packageName));
-
- } catch (NameNotFoundException e) {
- // doesn't really exist; ignore it
- if (DEBUG) {
- Slog.w(TAG, "Can't resolve new app " + packageName);
- }
- }
- }
-
- // Whenever a package is added or updated we need to update
- // the package metadata bookkeeping.
- dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
- } else {
- if (replacing) {
- // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
- } else {
- // Outright removal. In the full-data case, the app will be dropped
- // from the queue when its (now obsolete) name comes up again for
- // backup.
- synchronized (mBackupParticipants) {
- removePackageParticipantsLocked(pkgList, uid);
- }
- }
- for (final String pkgName : pkgList) {
- mBackupHandler.post(
- () -> mTransportManager.onPackageRemoved(pkgName));
- }
- }
- }
- };
-
- // Add the backup agents in the given packages to our set of known backup participants.
- // If 'packageNames' is null, adds all backup agents in the whole system.
- private void addPackageParticipantsLocked(String[] packageNames) {
- // Look for apps that define the android:backupAgent attribute
- List<PackageInfo> targetApps = allAgentPackages();
- if (packageNames != null) {
- if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
- for (String packageName : packageNames) {
- addPackageParticipantsLockedInner(packageName, targetApps);
- }
- } else {
- if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
- addPackageParticipantsLockedInner(null, targetApps);
- }
- }
-
- private void addPackageParticipantsLockedInner(String packageName,
- List<PackageInfo> targetPkgs) {
- if (MORE_DEBUG) {
- Slog.v(TAG, "Examining " + packageName + " for backup agent");
- }
-
- for (PackageInfo pkg : targetPkgs) {
- if (packageName == null || pkg.packageName.equals(packageName)) {
- int uid = pkg.applicationInfo.uid;
- HashSet<String> set = mBackupParticipants.get(uid);
- if (set == null) {
- set = new HashSet<>();
- mBackupParticipants.put(uid, set);
- }
- set.add(pkg.packageName);
- if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
-
- // Schedule a backup for it on general principles
- if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
- Message msg = mBackupHandler
- .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
- mBackupHandler.sendMessage(msg);
- }
- }
- }
-
- // Remove the given packages' entries from our known active set.
- private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
- if (packageNames == null) {
- Slog.w(TAG, "removePackageParticipants with null list");
- return;
- }
-
- if (MORE_DEBUG) {
- Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
- + " #" + packageNames.length);
- }
- for (String pkg : packageNames) {
- // Known previous UID, so we know which package set to check
- HashSet<String> set = mBackupParticipants.get(oldUid);
- if (set != null && set.contains(pkg)) {
- removePackageFromSetLocked(set, pkg);
- if (set.isEmpty()) {
- if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set");
- mBackupParticipants.remove(oldUid);
- }
- }
- }
- }
-
- private void removePackageFromSetLocked(final HashSet<String> set,
- final String packageName) {
- if (set.contains(packageName)) {
- // Found it. Remove this one package from the bookkeeping, and
- // if it's the last participating app under this uid we drop the
- // (now-empty) set as well.
- // Note that we deliberately leave it 'known' in the "ever backed up"
- // bookkeeping so that its current-dataset data will be retrieved
- // if the app is subsequently reinstalled
- if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
- set.remove(packageName);
- mPendingBackups.remove(packageName);
- }
- }
-
- // Returns the set of all applications that define an android:backupAgent attribute
- private List<PackageInfo> allAgentPackages() {
- // !!! TODO: cache this and regenerate only when necessary
- int flags = PackageManager.GET_SIGNING_CERTIFICATES;
- List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
- int numPackages = packages.size();
- for (int a = numPackages - 1; a >= 0; a--) {
- PackageInfo pkg = packages.get(a);
- try {
- ApplicationInfo app = pkg.applicationInfo;
- if (((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
- || app.backupAgentName == null
- || (app.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
- packages.remove(a);
- } else {
- // we will need the shared library path, so look that up and store it here.
- // This is used implicitly when we pass the PackageInfo object off to
- // the Activity Manager to launch the app for backup/restore purposes.
- app = mPackageManager.getApplicationInfo(pkg.packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES);
- pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
- pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
- }
- } catch (NameNotFoundException e) {
- packages.remove(a);
- }
- }
- return packages;
- }
-
- /**
- * Called from the backup tasks: record that the given app has been successfully backed up at
- * least once. This includes both key/value and full-data backups through the transport.
- */
- public void logBackupComplete(String packageName) {
- if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
-
- for (String receiver : mConstants.getBackupFinishedNotificationReceivers()) {
- final Intent notification = new Intent();
- notification.setAction(BACKUP_FINISHED_ACTION);
- notification.setPackage(receiver);
- notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
- | Intent.FLAG_RECEIVER_FOREGROUND);
- notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
- mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
- }
-
- mProcessedPackagesJournal.addPackage(packageName);
- }
-
- /**
- * Persistently record the current and ancestral backup tokens, as well as the set of packages
- * with data available in the ancestral dataset.
- */
- public void writeRestoreTokens() {
- try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
- // First, the version number of this record, for futureproofing
- af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
-
- // Write the ancestral and current tokens
- af.writeLong(mAncestralToken);
- af.writeLong(mCurrentToken);
-
- // Now write the set of ancestral packages
- if (mAncestralPackages == null) {
- af.writeInt(-1);
- } else {
- af.writeInt(mAncestralPackages.size());
- if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
- for (String pkgName : mAncestralPackages) {
- af.writeUTF(pkgName);
- if (MORE_DEBUG) Slog.v(TAG, " " + pkgName);
- }
- }
- } catch (IOException e) {
- Slog.w(TAG, "Unable to write token file:", e);
- }
- }
-
- /** Fires off a backup agent, blocking until it attaches or times out. */
- @Nullable
- public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
- IBackupAgent agent = null;
- synchronized (mAgentConnectLock) {
- mConnecting = true;
- mConnectedAgent = null;
- try {
- if (mActivityManager.bindBackupAgent(app.packageName, mode,
- UserHandle.USER_OWNER)) {
- Slog.d(TAG, "awaiting agent for " + app);
-
- // success; wait for the agent to arrive
- // only wait 10 seconds for the bind to happen
- long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
- while (mConnecting && mConnectedAgent == null
- && (System.currentTimeMillis() < timeoutMark)) {
- try {
- mAgentConnectLock.wait(5000);
- } catch (InterruptedException e) {
- // just bail
- Slog.w(TAG, "Interrupted: " + e);
- mConnecting = false;
- mConnectedAgent = null;
- }
- }
-
- // if we timed out with no connect, abort and move on
- if (mConnecting) {
- Slog.w(TAG, "Timeout waiting for agent " + app);
- mConnectedAgent = null;
- }
- if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
- agent = mConnectedAgent;
- }
- } catch (RemoteException e) {
- // can't happen - ActivityManager is local
- }
- }
- if (agent == null) {
- try {
- mActivityManager.clearPendingBackup();
- } catch (RemoteException e) {
- // can't happen - ActivityManager is local
- }
- }
- return agent;
- }
-
- /** Unbind from a backup agent. */
- public void unbindAgent(ApplicationInfo app) {
- try {
- mActivityManager.unbindBackupAgent(app);
- } catch (RemoteException e) {
- // Can't happen - activity manager is local
- }
- }
-
- /**
- * Clear an application's data, blocking until the operation completes or times out. If {@code
- * keepSystemState} is {@code true}, we intentionally do not clear system state that would
- * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
- * bringing it into the actual expected state related to the already-restored notification state
- * etc.
- */
- public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
- // Don't wipe packages marked allowClearUserData=false
- try {
- PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
- if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "allowClearUserData=false so not wiping "
- + packageName);
- }
- return;
- }
- } catch (NameNotFoundException e) {
- Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
- return;
- }
-
- ClearDataObserver observer = new ClearDataObserver(this);
-
- synchronized (mClearDataLock) {
- mClearingData = true;
- try {
- mActivityManager.clearApplicationUserData(
- packageName, keepSystemState, observer, 0);
- } catch (RemoteException e) {
- // can't happen because the activity manager is in this process
- }
-
- // only wait 10 seconds for the clear data to happen
- long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
- while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
- try {
- mClearDataLock.wait(5000);
- } catch (InterruptedException e) {
- // won't happen, but still.
- mClearingData = false;
- }
- }
- }
- }
-
- /**
- * Get the restore-set token for the best-available restore set for this {@code packageName}:
- * the active set if possible, else the ancestral one. Returns zero if none available.
- */
- public long getAvailableRestoreToken(String packageName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getAvailableRestoreToken");
-
- long token = mAncestralToken;
- synchronized (mQueueLock) {
- if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "App in ever-stored, so using current token");
- }
- token = mCurrentToken;
- }
- }
- if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
- return token;
- }
-
- /**
- * Requests a backup for the inputted {@code packages}.
- *
- * @see #requestBackup(String[], IBackupObserver, IBackupManagerMonitor, int).
- */
- public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
- return requestBackup(packages, observer, null, flags);
- }
-
- /**
- * Requests a backup for the inputted {@code packages} with a specified {@link
- * IBackupManagerMonitor}.
- */
- public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
-
- if (packages == null || packages.length < 1) {
- Slog.e(TAG, "No packages named for backup request");
- BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
- BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
- null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
- throw new IllegalArgumentException("No packages are provided for backup");
- }
-
- if (!mEnabled || !mProvisioned) {
- Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
- BackupObserverUtils.sendBackupFinished(observer,
- BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- final int logTag = mProvisioned
- ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
- : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
- return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
- }
-
- final TransportClient transportClient;
- final String transportDirName;
- try {
- transportDirName =
- mTransportManager.getTransportDirName(
- mTransportManager.getCurrentTransportName());
- transportClient =
- mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
- } catch (TransportNotRegisteredException e) {
- BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
- BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
- null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
- return BackupManager.ERROR_TRANSPORT_ABORTED;
- }
-
- OnTaskFinishedListener listener =
- caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
-
- ArrayList<String> fullBackupList = new ArrayList<>();
- ArrayList<String> kvBackupList = new ArrayList<>();
- for (String packageName : packages) {
- if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
- kvBackupList.add(packageName);
- continue;
- }
- try {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
- if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
- mPackageManager)) {
- BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
- BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- continue;
- }
- if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
- fullBackupList.add(packageInfo.packageName);
- } else {
- kvBackupList.add(packageInfo.packageName);
- }
- } catch (NameNotFoundException e) {
- BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
- BackupManager.ERROR_PACKAGE_NOT_FOUND);
- }
- }
- EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
- fullBackupList.size());
- if (MORE_DEBUG) {
- Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
- + fullBackupList.size() + " full backups, " + kvBackupList.size()
- + " k/v backups");
- }
-
- boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
-
- Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
- msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
- observer, monitor, listener, true, nonIncrementalBackup);
- mBackupHandler.sendMessage(msg);
- return BackupManager.SUCCESS;
- }
-
- /** Cancel all running backups. */
- public void cancelBackups() {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
- if (MORE_DEBUG) {
- Slog.i(TAG, "cancelBackups() called.");
- }
- final long oldToken = Binder.clearCallingIdentity();
- try {
- List<Integer> operationsToCancel = new ArrayList<>();
- synchronized (mCurrentOpLock) {
- for (int i = 0; i < mCurrentOperations.size(); i++) {
- Operation op = mCurrentOperations.valueAt(i);
- int token = mCurrentOperations.keyAt(i);
- if (op.type == OP_TYPE_BACKUP) {
- operationsToCancel.add(token);
- }
- }
- }
- for (Integer token : operationsToCancel) {
- handleCancel(token, true /* cancelAll */);
- }
- // We don't want the backup jobs to kick in any time soon.
- // Reschedules them to run in the distant future.
- KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
- FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
- } finally {
- Binder.restoreCallingIdentity(oldToken);
- }
- }
-
- /** Schedule a timeout message for the operation identified by {@code token}. */
- public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
- int operationType) {
- if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
- Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
- + Integer.toHexString(token) + " of type " + operationType);
- return;
- }
- if (MORE_DEBUG) {
- Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
- + " interval=" + interval + " callback=" + callback);
- }
-
- synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
- Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
- token, 0, callback);
- mBackupHandler.sendMessageDelayed(msg, interval);
- }
- }
-
- private int getMessageIdForOperationType(int operationType) {
- switch (operationType) {
- case OP_TYPE_BACKUP_WAIT:
- return MSG_BACKUP_OPERATION_TIMEOUT;
- case OP_TYPE_RESTORE_WAIT:
- return MSG_RESTORE_OPERATION_TIMEOUT;
- default:
- Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
- + operationType);
- return -1;
- }
- }
-
- /**
- * Add an operation to the list of currently running operations. Used for cancellation,
- * completion and timeout callbacks that act on the operation via the {@code token}.
- */
- public void putOperation(int token, Operation operation) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
- + operation.type);
- }
- synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, operation);
- }
- }
-
- /**
- * Remove an operation from the list of currently running operations. An operation is removed
- * when it is completed, cancelled, or timed out, and thus no longer running.
- */
- public void removeOperation(int token) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
- }
- synchronized (mCurrentOpLock) {
- if (mCurrentOperations.get(token) == null) {
- Slog.w(TAG, "Duplicate remove for operation. token="
- + Integer.toHexString(token));
- }
- mCurrentOperations.remove(token);
- }
- }
-
- /** Block until we received an operation complete message (from the agent or cancellation). */
- public boolean waitUntilOperationComplete(int token) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "Blocking until operation complete for "
- + Integer.toHexString(token));
- }
- int finalState = OP_PENDING;
- Operation op = null;
- synchronized (mCurrentOpLock) {
- while (true) {
- op = mCurrentOperations.get(token);
- if (op == null) {
- // mysterious disappearance: treat as success with no callback
- break;
- } else {
- if (op.state == OP_PENDING) {
- try {
- mCurrentOpLock.wait();
- } catch (InterruptedException e) {
- }
- // When the wait is notified we loop around and recheck the current state
- } else {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Unblocked waiting for operation token="
- + Integer.toHexString(token));
- }
- // No longer pending; we're done
- finalState = op.state;
- break;
- }
- }
- }
- }
-
- removeOperation(token);
- if (op != null) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
- }
- if (MORE_DEBUG) {
- Slog.v(TAG, "operation " + Integer.toHexString(token)
- + " complete: finalState=" + finalState);
- }
- return finalState == OP_ACKNOWLEDGED;
- }
-
- /** Cancel the operation associated with {@code token}. */
- public void handleCancel(int token, boolean cancelAll) {
- // Notify any synchronous waiters
- Operation op = null;
- synchronized (mCurrentOpLock) {
- op = mCurrentOperations.get(token);
- if (MORE_DEBUG) {
- if (op == null) {
- Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
- + " but no op found");
- }
- }
- int state = (op != null) ? op.state : OP_TIMEOUT;
- if (state == OP_ACKNOWLEDGED) {
- // The operation finished cleanly, so we have nothing more to do.
- if (DEBUG) {
- Slog.w(TAG, "Operation already got an ack."
- + "Should have been removed from mCurrentOperations.");
- }
- op = null;
- mCurrentOperations.delete(token);
- } else if (state == OP_PENDING) {
- if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
- op.state = OP_TIMEOUT;
- // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
- // called after we receive cancel here. We need this op's state there.
-
- // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
- // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
- // doesn't require cancellation.
- if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
- }
- }
- mCurrentOpLock.notifyAll();
- }
-
- // If there's a TimeoutHandler for this event, call it
- if (op != null && op.callback != null) {
- if (MORE_DEBUG) {
- Slog.v(TAG, " Invoking cancel on " + op.callback);
- }
- op.callback.handleCancel(cancelAll);
- }
- }
-
- /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
- public boolean isBackupOperationInProgress() {
- synchronized (mCurrentOpLock) {
- for (int i = 0; i < mCurrentOperations.size(); i++) {
- Operation op = mCurrentOperations.valueAt(i);
- if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
- return true;
- }
- }
- }
- return false;
- }
-
- /** Unbind the backup agent and kill the app if it's a non-system app. */
- public void tearDownAgentAndKill(ApplicationInfo app) {
- if (app == null) {
- // Null means the system package, so just quietly move on. :)
- return;
- }
-
- try {
- // unbind and tidy up even on timeout or failure, just in case
- mActivityManager.unbindBackupAgent(app);
-
- // The agent was running with a stub Application object, so shut it down.
- // !!! We hardcode the confirmation UI's package name here rather than use a
- // manifest flag! TODO something less direct.
- if (app.uid >= Process.FIRST_APPLICATION_UID
- && !app.packageName.equals("com.android.backupconfirm")) {
- if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
- mActivityManager.killApplicationProcess(app.processName, app.uid);
- } else {
- if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
- }
- } catch (RemoteException e) {
- Slog.d(TAG, "Lost app trying to shut down");
- }
- }
-
- /** For adb backup/restore. */
- public boolean deviceIsEncrypted() {
- try {
- return mStorageManager.getEncryptionState()
- != StorageManager.ENCRYPTION_STATE_NONE
- && mStorageManager.getPasswordType()
- != StorageManager.CRYPT_TYPE_DEFAULT;
- } catch (Exception e) {
- // If we can't talk to the storagemanager service we have a serious problem; fail
- // "secure" i.e. assuming that the device is encrypted.
- Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
- return true;
- }
- }
-
- // ----- Full-data backup scheduling -----
-
- /**
- * Schedule a job to tell us when it's a good time to run a full backup
- */
- public void scheduleNextFullBackupJob(long transportMinLatency) {
- synchronized (mQueueLock) {
- if (mFullBackupQueue.size() > 0) {
- // schedule the next job at the point in the future when the least-recently
- // backed up app comes due for backup again; or immediately if it's already
- // due.
- final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
- final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
- final long interval = mConstants.getFullBackupIntervalMilliseconds();
- final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
- final long latency = Math.max(transportMinLatency, appLatency);
- Runnable r = new Runnable() {
- @Override
- public void run() {
- FullBackupJob.schedule(mContext, latency, mConstants);
- }
- };
- mBackupHandler.postDelayed(r, 2500);
- } else {
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Full backup queue empty; not scheduling");
- }
- }
- }
- }
-
- /**
- * Remove a package from the full-data queue.
- */
- @GuardedBy("mQueueLock")
- private void dequeueFullBackupLocked(String packageName) {
- final int numPackages = mFullBackupQueue.size();
- for (int i = numPackages - 1; i >= 0; i--) {
- final FullBackupEntry e = mFullBackupQueue.get(i);
- if (packageName.equals(e.packageName)) {
- mFullBackupQueue.remove(i);
- }
- }
- }
-
- /**
- * Enqueue full backup for the given app, with a note about when it last ran.
- */
- public void enqueueFullBackup(String packageName, long lastBackedUp) {
- FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
- synchronized (mQueueLock) {
- // First, sanity check that we aren't adding a duplicate. Slow but
- // straightforward; we'll have at most on the order of a few hundred
- // items in this list.
- dequeueFullBackupLocked(packageName);
-
- // This is also slow but easy for modest numbers of apps: work backwards
- // from the end of the queue until we find an item whose last backup
- // time was before this one, then insert this new entry after it. If we're
- // adding something new we don't bother scanning, and just prepend.
- int which = -1;
- if (lastBackedUp > 0) {
- for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
- final FullBackupEntry entry = mFullBackupQueue.get(which);
- if (entry.lastBackup <= lastBackedUp) {
- mFullBackupQueue.add(which + 1, newEntry);
- break;
- }
- }
- }
- if (which < 0) {
- // this one is earlier than any existing one, so prepend
- mFullBackupQueue.add(0, newEntry);
- }
- }
- writeFullBackupScheduleAsync();
- }
-
- private boolean fullBackupAllowable(String transportName) {
- if (!mTransportManager.isTransportRegistered(transportName)) {
- Slog.w(TAG, "Transport not registered; full data backup not performed");
- return false;
- }
-
- // Don't proceed unless we have already established package metadata
- // for the current dataset via a key/value backup pass.
- try {
- String transportDirName = mTransportManager.getTransportDirName(transportName);
- File stateDir = new File(mBaseStateDir, transportDirName);
- File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
- if (pmState.length() <= 0) {
- if (DEBUG) {
- Slog.i(TAG, "Full backup requested but dataset not yet initialized");
- }
- return false;
- }
- } catch (Exception e) {
- Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
- return false;
- }
-
- return true;
- }
-
- /**
- * Conditions are right for a full backup operation, so run one. The model we use is
- * to perform one app backup per scheduled job execution, and to reschedule the job
- * with zero latency as long as conditions remain right and we still have work to do.
- *
- * <p>This is the "start a full backup operation" entry point called by the scheduled job.
- *
- * @return Whether ongoing work will continue. The return value here will be passed
- * along as the return value to the scheduled job's onStartJob() callback.
- */
- public boolean beginFullBackup(FullBackupJob scheduledJob) {
- final long now = System.currentTimeMillis();
- final long fullBackupInterval;
- final long keyValueBackupInterval;
- synchronized (mConstants) {
- fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
- keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
- }
- FullBackupEntry entry = null;
- long latency = fullBackupInterval;
-
- if (!mEnabled || !mProvisioned) {
- // Backups are globally disabled, so don't proceed. We also don't reschedule
- // the job driving automatic backups; that job will be scheduled again when
- // the user enables backup.
- if (MORE_DEBUG) {
- Slog.i(TAG, "beginFullBackup but e=" + mEnabled
- + " p=" + mProvisioned + "; ignoring");
- }
- return false;
- }
-
- // Don't run the backup if we're in battery saver mode, but reschedule
- // to try again in the not-so-distant future.
- final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
- if (result.batterySaverEnabled) {
- if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
- FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
- return false;
- }
-
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Beginning scheduled full backup operation");
- }
-
- // Great; we're able to run full backup jobs now. See if we have any work to do.
- synchronized (mQueueLock) {
- if (mRunningFullBackupTask != null) {
- Slog.e(TAG, "Backup triggered but one already/still running!");
- return false;
- }
-
- // At this point we think that we have work to do, but possibly not right now.
- // Any exit without actually running backups will also require that we
- // reschedule the job.
- boolean runBackup = true;
- boolean headBusy;
-
- do {
- // Recheck each time, because culling due to ineligibility may
- // have emptied the queue.
- if (mFullBackupQueue.size() == 0) {
- // no work to do so just bow out
- if (DEBUG) {
- Slog.i(TAG, "Backup queue empty; doing nothing");
- }
- runBackup = false;
- break;
- }
-
- headBusy = false;
-
- String transportName = mTransportManager.getCurrentTransportName();
- if (!fullBackupAllowable(transportName)) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "Preconditions not met; not running full backup");
- }
- runBackup = false;
- // Typically this means we haven't run a key/value backup yet. Back off
- // full-backup operations by the key/value job's run interval so that
- // next time we run, we are likely to be able to make progress.
- latency = keyValueBackupInterval;
- }
-
- if (runBackup) {
- entry = mFullBackupQueue.get(0);
- long timeSinceRun = now - entry.lastBackup;
- runBackup = (timeSinceRun >= fullBackupInterval);
- if (!runBackup) {
- // It's too early to back up the next thing in the queue, so bow out
- if (MORE_DEBUG) {
- Slog.i(TAG, "Device ready but too early to back up next app");
- }
- // Wait until the next app in the queue falls due for a full data backup
- latency = fullBackupInterval - timeSinceRun;
- break; // we know we aren't doing work yet, so bail.
- }
-
- try {
- PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
- if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
- // The head app isn't supposed to get full-data backups [any more];
- // so we cull it and force a loop around to consider the new head
- // app.
- if (MORE_DEBUG) {
- Slog.i(TAG, "Culling package " + entry.packageName
- + " in full-backup queue but not eligible");
- }
- mFullBackupQueue.remove(0);
- headBusy = true; // force the while() condition
- continue;
- }
-
- final int privFlags = appInfo.applicationInfo.privateFlags;
- headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
- && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
-
- if (headBusy) {
- final long nextEligible = System.currentTimeMillis()
- + BUSY_BACKOFF_MIN_MILLIS
- + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
- if (DEBUG_SCHEDULING) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Slog.i(TAG, "Full backup time but " + entry.packageName
- + " is busy; deferring to "
- + sdf.format(new Date(nextEligible)));
- }
- // This relocates the app's entry from the head of the queue to
- // its order-appropriate position further down, so upon looping
- // a new candidate will be considered at the head.
- enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
- }
- } catch (NameNotFoundException nnf) {
- // So, we think we want to back this up, but it turns out the package
- // in question is no longer installed. We want to drop it from the
- // queue entirely and move on, but if there's nothing else in the queue
- // we should bail entirely. headBusy cannot have been set to true yet.
- runBackup = (mFullBackupQueue.size() > 1);
- } catch (RemoteException e) {
- // Cannot happen; the Activity Manager is in the same process
- }
- }
- } while (headBusy);
-
- if (!runBackup) {
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
- }
- final long deferTime = latency; // pin for the closure
- mBackupHandler.post(new Runnable() {
- @Override
- public void run() {
- FullBackupJob.schedule(mContext, deferTime, mConstants);
- }
- });
- return false;
- }
-
- // Okay, the top thing is ready for backup now. Do it.
- mFullBackupQueue.remove(0);
- CountDownLatch latch = new CountDownLatch(1);
- String[] pkg = new String[]{entry.packageName};
- mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
- this,
- /* observer */ null,
- pkg,
- /* updateSchedule */ true,
- scheduledJob,
- latch,
- /* backupObserver */ null,
- /* monitor */ null,
- /* userInitiated */ false,
- "BMS.beginFullBackup()");
- // Acquiring wakelock for PerformFullTransportBackupTask before its start.
- mWakelock.acquire();
- (new Thread(mRunningFullBackupTask)).start();
- }
-
- return true;
- }
-
- /**
- * The job scheduler says our constraints don't hold anymore, so tear down any ongoing backup
- * task right away.
- */
- public void endFullBackup() {
- // offload the mRunningFullBackupTask.handleCancel() call to another thread,
- // as we might have to wait for mCancelLock
- Runnable endFullBackupRunnable = new Runnable() {
- @Override
- public void run() {
- PerformFullTransportBackupTask pftbt = null;
- synchronized (mQueueLock) {
- if (mRunningFullBackupTask != null) {
- pftbt = mRunningFullBackupTask;
- }
- }
- if (pftbt != null) {
- if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Telling running backup to stop");
- }
- pftbt.handleCancel(true);
- }
- }
- };
- new Thread(endFullBackupRunnable, "end-full-backup").start();
- }
-
- /** Used by both incremental and full restore to restore widget data. */
- public void restoreWidgetData(String packageName, byte[] widgetData) {
- // Apply the restored widget state and generate the ID update for the app
- // TODO: http://b/22388012
- if (MORE_DEBUG) {
- Slog.i(TAG, "Incorporating restored widget data");
- }
- AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
- }
-
- // *****************************
- // NEW UNIFIED RESTORE IMPLEMENTATION
- // *****************************
-
- /** Schedule a backup pass for {@code packageName}. */
- public void dataChangedImpl(String packageName) {
- HashSet<String> targets = dataChangedTargets(packageName);
- dataChangedImpl(packageName, targets);
- }
-
- private void dataChangedImpl(String packageName, HashSet<String> targets) {
- // Record that we need a backup pass for the caller. Since multiple callers
- // may share a uid, we need to note all candidates within that uid and schedule
- // a backup pass for each of them.
- if (targets == null) {
- Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
- + " uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mQueueLock) {
- // Note that this client has made data changes that need to be backed up
- if (targets.contains(packageName)) {
- // Add the caller to the set of pending backups. If there is
- // one already there, then overwrite it, but no harm done.
- BackupRequest req = new BackupRequest(packageName);
- if (mPendingBackups.put(packageName, req) == null) {
- if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
-
- // Journal this request in case of crash. The put()
- // operation returned null when this package was not already
- // in the set; we want to avoid touching the disk redundantly.
- writeToJournalLocked(packageName);
- }
- }
- }
-
- // ...and schedule a backup pass if necessary
- KeyValueBackupJob.schedule(mContext, mConstants);
- }
-
- // Note: packageName is currently unused, but may be in the future
- private HashSet<String> dataChangedTargets(String packageName) {
- // If the caller does not hold the BACKUP permission, it can only request a
- // backup of its own data.
- if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
- Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
- synchronized (mBackupParticipants) {
- return mBackupParticipants.get(Binder.getCallingUid());
- }
- }
-
- // a caller with full permission can ask to back up any participating app
- if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
- return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
- } else {
- synchronized (mBackupParticipants) {
- return SparseArrayUtils.union(mBackupParticipants);
- }
- }
- }
-
- private void writeToJournalLocked(String str) {
- try {
- if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
- mJournal.addPackage(str);
- } catch (IOException e) {
- Slog.e(TAG, "Can't write " + str + " to backup journal", e);
- mJournal = null;
- }
- }
-
- // ----- IBackupManager binder interface -----
-
- /** Sent from an app's backup agent to let the service know that there's new data to backup. */
- public void dataChanged(final String packageName) {
- final int callingUserHandle = UserHandle.getCallingUserId();
- if (callingUserHandle != UserHandle.USER_SYSTEM) {
- // TODO: http://b/22388012
- // App is running under a non-owner user profile. For now, we do not back
- // up data from secondary user profiles.
- // TODO: backups for all user profiles although don't add backup for profiles
- // without adding admin control in DevicePolicyManager.
- if (MORE_DEBUG) {
- Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
- + callingUserHandle);
- }
- return;
- }
-
- final HashSet<String> targets = dataChangedTargets(packageName);
- if (targets == null) {
- Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
- + " uid=" + Binder.getCallingUid());
- return;
- }
-
- mBackupHandler.post(new Runnable() {
- public void run() {
- dataChangedImpl(packageName, targets);
- }
- });
- }
-
- /** Run an initialize operation for the given transport. */
+ /** Run an initialize operation for the given transports {@code transportNames}. */
public void initializeTransports(String[] transportNames, IBackupObserver observer) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "initializeTransport");
- Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- mWakelock.acquire();
- OnTaskFinishedListener listener = caller -> mWakelock.release();
- mBackupHandler.post(
- new PerformInitializeTask(this, transportNames, observer, listener));
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ mUserBackupManagerService.initializeTransports(transportNames, observer);
}
- /** Clear the given package's backup data from the current transport. */
+ /**
+ * Clear the given package {@code packageName}'s backup data from the transport {@code
+ * transportName}.
+ */
public void clearBackupData(String transportName, String packageName) {
- if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
- PackageInfo info;
- try {
- info = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNING_CERTIFICATES);
- } catch (NameNotFoundException e) {
- Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
- return;
- }
-
- // If the caller does not hold the BACKUP permission, it can only request a
- // wipe of its own backed-up data.
- Set<String> apps;
- if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
- Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
- apps = mBackupParticipants.get(Binder.getCallingUid());
- } else {
- // a caller with full permission can ask to back up any participating app
- // !!! TODO: allow data-clear of ANY app?
- if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
- apps = mProcessedPackagesJournal.getPackagesCopy();
- }
-
- if (apps.contains(packageName)) {
- // found it; fire off the clear request
- if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
- mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
- synchronized (mQueueLock) {
- TransportClient transportClient =
- mTransportManager
- .getTransportClient(transportName, "BMS.clearBackupData()");
- if (transportClient == null) {
- // transport is currently unregistered -- make sure to retry
- Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
- new ClearRetryParams(transportName, packageName));
- mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
- return;
- }
- long oldId = Binder.clearCallingIdentity();
- OnTaskFinishedListener listener =
- caller ->
- mTransportManager.disposeOfTransportClient(transportClient, caller);
- mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(
- MSG_RUN_CLEAR,
- new ClearParams(transportClient, info, listener));
- mBackupHandler.sendMessage(msg);
- Binder.restoreCallingIdentity(oldId);
- }
- }
+ mUserBackupManagerService.clearBackupData(transportName, packageName);
}
- /**
- * Run a backup pass immediately for any applications that have declared that they have pending
- * updates.
- */
- public void backupNow() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
-
- long oldId = Binder.clearCallingIdentity();
- try {
- final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
- if (result.batterySaverEnabled) {
- if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
- KeyValueBackupJob.schedule(mContext, mConstants); // try again in several hours
- } else {
- if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
- synchronized (mQueueLock) {
- // Fire the intent that kicks off the whole shebang...
- try {
- mRunBackupIntent.send();
- } catch (PendingIntent.CanceledException e) {
- // should never happen
- Slog.e(TAG, "run-backup intent cancelled!");
- }
-
- // ...and cancel any pending scheduled job, because we've just superseded it
- KeyValueBackupJob.cancel(mContext);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- /** Returns {@code true} if the system user has gone through SUW. */
- public boolean deviceIsProvisioned() {
- final ContentResolver resolver = mContext.getContentResolver();
- return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
- }
-
- /**
- * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
- * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
- * return to the caller until the backup has been completed. It requires on-screen confirmation
- * by the user.
- */
- public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
- boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
- boolean compress, boolean doKeyValue, String[] pkgList) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
-
- final int callingUserHandle = UserHandle.getCallingUserId();
- // TODO: http://b/22388012
- if (callingUserHandle != UserHandle.USER_SYSTEM) {
- throw new IllegalStateException("Backup supported only for the device owner");
- }
-
- // Validate
- if (!doAllApps) {
- if (!includeShared) {
- // If we're backing up shared data (sdcard or equivalent), then we can run
- // without any supplied app names. Otherwise, we'd be doing no work, so
- // report the error.
- if (pkgList == null || pkgList.length == 0) {
- throw new IllegalArgumentException(
- "Backup requested but neither shared nor any apps named");
- }
- }
- }
-
- long oldId = Binder.clearCallingIdentity();
- try {
- // Doesn't make sense to do a full backup prior to setup
- if (!deviceIsProvisioned()) {
- Slog.i(TAG, "Backup not supported before setup");
- return;
- }
-
- if (DEBUG) {
- Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
- + " shared=" + includeShared + " all=" + doAllApps + " system="
- + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
- }
- Slog.i(TAG, "Beginning adb backup...");
-
- AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
- includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
- pkgList);
- final int token = generateRandomIntegerToken();
- synchronized (mAdbBackupRestoreConfirmations) {
- mAdbBackupRestoreConfirmations.put(token, params);
- }
-
- // start up the confirmation UI
- if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
- if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
- Slog.e(TAG, "Unable to launch backup confirmation UI");
- mAdbBackupRestoreConfirmations.delete(token);
- return;
- }
-
- // make sure the screen is lit for the user interaction
- mPowerManager.userActivity(SystemClock.uptimeMillis(),
- PowerManager.USER_ACTIVITY_EVENT_OTHER,
- 0);
-
- // start the confirmation countdown
- startConfirmationTimeout(token, params);
-
- // wait for the backup to be performed
- if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
- waitForCompletion(params);
- } finally {
- try {
- fd.close();
- } catch (IOException e) {
- Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
- }
- Binder.restoreCallingIdentity(oldId);
- Slog.d(TAG, "Adb backup processing complete.");
- }
- }
-
- /** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
- public void fullTransportBackup(String[] pkgNames) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "fullTransportBackup");
-
- final int callingUserHandle = UserHandle.getCallingUserId();
- // TODO: http://b/22388012
- if (callingUserHandle != UserHandle.USER_SYSTEM) {
- throw new IllegalStateException("Restore supported only for the device owner");
- }
-
- String transportName = mTransportManager.getCurrentTransportName();
- if (!fullBackupAllowable(transportName)) {
- Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
- } else {
- if (DEBUG) {
- Slog.d(TAG, "fullTransportBackup()");
- }
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- CountDownLatch latch = new CountDownLatch(1);
- Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
- this,
- /* observer */ null,
- pkgNames,
- /* updateSchedule */ false,
- /* runningJob */ null,
- latch,
- /* backupObserver */ null,
- /* monitor */ null,
- /* userInitiated */ false,
- "BMS.fullTransportBackup()");
- // Acquiring wakelock for PerformFullTransportBackupTask before its start.
- mWakelock.acquire();
- (new Thread(task, "full-transport-master")).start();
- do {
- try {
- latch.await();
- break;
- } catch (InterruptedException e) {
- // Just go back to waiting for the latch to indicate completion
- }
- } while (true);
-
- // We just ran a backup on these packages, so kick them to the end of the queue
- final long now = System.currentTimeMillis();
- for (String pkg : pkgNames) {
- enqueueFullBackup(pkg, now);
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Done with full transport backup.");
- }
- }
-
- /**
- * Used by 'adb restore' to run a restore pass, blocking until completion. Requires user
- * confirmation.
- */
- public void adbRestore(ParcelFileDescriptor fd) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
-
- final int callingUserHandle = UserHandle.getCallingUserId();
- // TODO: http://b/22388012
- if (callingUserHandle != UserHandle.USER_SYSTEM) {
- throw new IllegalStateException("Restore supported only for the device owner");
- }
-
- long oldId = Binder.clearCallingIdentity();
-
- try {
- // Check whether the device has been provisioned -- we don't handle
- // full restores prior to completing the setup process.
- if (!deviceIsProvisioned()) {
- Slog.i(TAG, "Full restore not permitted before setup");
- return;
- }
-
- Slog.i(TAG, "Beginning restore...");
-
- AdbRestoreParams params = new AdbRestoreParams(fd);
- final int token = generateRandomIntegerToken();
- synchronized (mAdbBackupRestoreConfirmations) {
- mAdbBackupRestoreConfirmations.put(token, params);
- }
-
- // start up the confirmation UI
- if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
- if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
- Slog.e(TAG, "Unable to launch restore confirmation");
- mAdbBackupRestoreConfirmations.delete(token);
- return;
- }
-
- // make sure the screen is lit for the user interaction
- mPowerManager.userActivity(SystemClock.uptimeMillis(),
- PowerManager.USER_ACTIVITY_EVENT_OTHER,
- 0);
-
- // start the confirmation countdown
- startConfirmationTimeout(token, params);
-
- // wait for the restore to be performed
- if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
- waitForCompletion(params);
- } finally {
- try {
- fd.close();
- } catch (IOException e) {
- Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
- }
- Binder.restoreCallingIdentity(oldId);
- Slog.i(TAG, "adb restore processing complete.");
- }
- }
-
- private boolean startConfirmationUi(int token, String action) {
- try {
- Intent confIntent = new Intent(action);
- confIntent.setClassName("com.android.backupconfirm",
- "com.android.backupconfirm.BackupRestoreConfirmation");
- confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
- confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
- } catch (ActivityNotFoundException e) {
- return false;
- }
- return true;
- }
-
- private void startConfirmationTimeout(int token, AdbParams params) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Posting conf timeout msg after "
- + TIMEOUT_FULL_CONFIRMATION + " millis");
- }
- Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
- token, 0, params);
- mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
- }
-
- private void waitForCompletion(AdbParams params) {
- synchronized (params.latch) {
- while (!params.latch.get()) {
- try {
- params.latch.wait();
- } catch (InterruptedException e) { /* never interrupted */ }
- }
- }
- }
-
- /** Called when adb backup/restore has completed. */
- public void signalAdbBackupRestoreCompletion(AdbParams params) {
- synchronized (params.latch) {
- params.latch.set(true);
- params.latch.notifyAll();
- }
- }
-
- /**
- * Confirm that the previously-requested full backup/restore operation can proceed. This is used
- * to require a user-facing disclosure about the operation.
- */
- public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
- String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
- if (DEBUG) {
- Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
- + " allow=" + allow);
- }
-
- // TODO: possibly require not just this signature-only permission, but even
- // require that the specific designated confirmation-UI app uid is the caller?
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "acknowledgeAdbBackupOrRestore");
-
- long oldId = Binder.clearCallingIdentity();
- try {
-
- AdbParams params;
- synchronized (mAdbBackupRestoreConfirmations) {
- params = mAdbBackupRestoreConfirmations.get(token);
- if (params != null) {
- mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
- mAdbBackupRestoreConfirmations.delete(token);
-
- if (allow) {
- final int verb = params instanceof AdbBackupParams
- ? MSG_RUN_ADB_BACKUP
- : MSG_RUN_ADB_RESTORE;
-
- params.observer = observer;
- params.curPassword = curPassword;
-
- params.encryptPassword = encPpassword;
-
- if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
- mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(verb, params);
- mBackupHandler.sendMessage(msg);
- } else {
- Slog.w(TAG, "User rejected full backup/restore operation");
- // indicate completion without having actually transferred any data
- signalAdbBackupRestoreCompletion(params);
- }
- } else {
- Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
- }
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- /** User-configurable enabling/disabling of backups. */
- public void setBackupEnabled(boolean enable) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupEnabled");
-
- Slog.i(TAG, "Backup enabled => " + enable);
-
- long oldId = Binder.clearCallingIdentity();
- try {
- boolean wasEnabled = mEnabled;
- synchronized (this) {
- writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
- mEnabled = enable;
- }
-
- synchronized (mQueueLock) {
- if (enable && !wasEnabled && mProvisioned) {
- // if we've just been enabled, start scheduling backup passes
- KeyValueBackupJob.schedule(mContext, mConstants);
- scheduleNextFullBackupJob(0);
- } else if (!enable) {
- // No longer enabled, so stop running backups
- if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
-
- KeyValueBackupJob.cancel(mContext);
-
- // This also constitutes an opt-out, so we wipe any data for
- // this device from the backend. We start that process with
- // an alarm in order to guarantee wakelock states.
- if (wasEnabled && mProvisioned) {
- // NOTE: we currently flush every registered transport, not just
- // the currently-active one.
- List<String> transportNames = new ArrayList<>();
- List<String> transportDirNames = new ArrayList<>();
- mTransportManager.forEachRegisteredTransport(
- name -> {
- final String dirName;
- try {
- dirName =
- mTransportManager
- .getTransportDirName(name);
- } catch (TransportNotRegisteredException e) {
- // Should never happen
- Slog.e(TAG, "Unexpected unregistered transport", e);
- return;
- }
- transportNames.add(name);
- transportDirNames.add(dirName);
- });
-
- // build the set of transports for which we are posting an init
- for (int i = 0; i < transportNames.size(); i++) {
- recordInitPending(
- true,
- transportNames.get(i),
- transportDirNames.get(i));
- }
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
- mRunInitIntent);
- }
- }
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- /** Enable/disable automatic restore of app data at install time. */
- public void setAutoRestore(boolean doAutoRestore) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setAutoRestore");
-
- Slog.i(TAG, "Auto restore => " + doAutoRestore);
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
- mAutoRestore = doAutoRestore;
- }
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- /** Mark the backup service as having been provisioned. */
- public void setBackupProvisioned(boolean available) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupProvisioned");
- /*
- * This is now a no-op; provisioning is simply the device's own setup state.
- */
- }
-
- /** Report whether the backup mechanism is currently enabled. */
- public boolean isBackupEnabled() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "isBackupEnabled");
- return mEnabled; // no need to synchronize just to read it
- }
-
- /** Report the name of the currently active transport. */
+ /** Return the name of the currently active transport. */
public String getCurrentTransport() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getCurrentTransport");
- String currentTransport = mTransportManager.getCurrentTransportName();
- if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
- return currentTransport;
+ return mUserBackupManagerService.getCurrentTransport();
}
/**
* Returns the {@link ComponentName} of the host service of the selected transport or {@code
* null} if no transport selected or if the transport selected is not registered.
*/
- @Nullable
public ComponentName getCurrentTransportComponent() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
- long oldId = Binder.clearCallingIdentity();
- try {
- return mTransportManager.getCurrentTransportComponent();
- } catch (TransportNotRegisteredException e) {
- return null;
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ return mUserBackupManagerService.getCurrentTransportComponent();
}
/** Report all known, available backup transports by name. */
public String[] listAllTransports() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "listAllTransports");
-
- return mTransportManager.getRegisteredTransportNames();
+ return mUserBackupManagerService.listAllTransports();
}
- /** Report all known, available backup transports by component. */
+ /** Report all known, available backup transports by {@link ComponentName}. */
public ComponentName[] listAllTransportComponents() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "listAllTransportComponents");
- return mTransportManager.getRegisteredTransportComponents();
+ return mUserBackupManagerService.listAllTransportComponents();
}
/** Report all system whitelisted transports. */
public String[] getTransportWhitelist() {
- // No permission check, intentionally.
- Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
- String[] whitelistedTransports = new String[whitelistedComponents.size()];
- int i = 0;
- for (ComponentName component : whitelistedComponents) {
- whitelistedTransports[i] = component.flattenToShortString();
- i++;
- }
- return whitelistedTransports;
+ return mUserBackupManagerService.getTransportWhitelist();
}
/**
@@ -2916,18 +282,17 @@
* @param transportComponent The identity of the transport being described.
* @param name A {@link String} with the new name for the transport. This is NOT for
* identification. MUST NOT be {@code null}.
- * @param configurationIntent An {@link Intent} that can be passed to
- * {@link Context#startActivity} in order to launch the transport's configuration UI. It may
- * be {@code null} if the transport does not offer any user-facing configuration UI.
+ * @param configurationIntent An {@link Intent} that can be passed to {@link
+ * Context#startActivity} in order to launch the transport's configuration UI. It may be
+ * {@code null} if the transport does not offer any user-facing configuration UI.
* @param currentDestinationString A {@link String} describing the destination to which the
* transport is currently sending data. MUST NOT be {@code null}.
- * @param dataManagementIntent An {@link Intent} that can be passed to
- * {@link Context#startActivity} in order to launch the transport's data-management UI. It
- * may be {@code null} if the transport does not offer any user-facing data
- * management UI.
+ * @param dataManagementIntent An {@link Intent} that can be passed to {@link
+ * Context#startActivity} in order to launch the transport's data-management UI. It may be
+ * {@code null} if the transport does not offer any user-facing data management UI.
* @param dataManagementLabel A {@link String} to be used as the label for the transport's data
- * management affordance. This MUST be {@code null} when dataManagementIntent is
- * {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+ * management affordance. This MUST be {@code null} when dataManagementIntent is {@code
+ * null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
* @throws SecurityException If the UID of the calling process differs from the package UID of
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
@@ -2937,9 +302,8 @@
@Nullable Intent configurationIntent,
String currentDestinationString,
@Nullable Intent dataManagementIntent,
- @Nullable String dataManagementLabel) {
- updateTransportAttributes(
- Binder.getCallingUid(),
+ String dataManagementLabel) {
+ mUserBackupManagerService.updateTransportAttributes(
transportComponent,
name,
configurationIntent,
@@ -2948,73 +312,16 @@
dataManagementLabel);
}
- @VisibleForTesting
- void updateTransportAttributes(
- int callingUid,
- ComponentName transportComponent,
- String name,
- @Nullable Intent configurationIntent,
- String currentDestinationString,
- @Nullable Intent dataManagementIntent,
- @Nullable String dataManagementLabel) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "updateTransportAttributes");
-
- Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
- Preconditions.checkNotNull(name, "name can't be null");
- Preconditions.checkNotNull(
- currentDestinationString, "currentDestinationString can't be null");
- Preconditions.checkArgument(
- (dataManagementIntent == null) == (dataManagementLabel == null),
- "dataManagementLabel should be null iff dataManagementIntent is null");
-
- try {
- int transportUid =
- mContext.getPackageManager()
- .getPackageUid(transportComponent.getPackageName(), 0);
- if (callingUid != transportUid) {
- throw new SecurityException("Only the transport can change its description");
- }
- } catch (NameNotFoundException e) {
- throw new SecurityException("Transport package not found", e);
- }
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- mTransportManager.updateTransportAttributes(
- transportComponent,
- name,
- configurationIntent,
- currentDestinationString,
- dataManagementIntent,
- dataManagementLabel);
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
/**
- * Selects transport {@code transportName} and returns previously selected transport.
+ * Selects transport {@code transportName} and returns the previously selected transport.
*
* @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} instead.
+ * ISelectBackupTransportCallback)} instead.
*/
@Deprecated
@Nullable
public String selectBackupTransport(String transportName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "selectBackupTransport");
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- String previousTransportName = mTransportManager.selectTransport(transportName);
- updateStateForTransport(transportName);
- Slog.v(TAG, "selectBackupTransport(transport = " + transportName
- + "): previous transport = " + previousTransportName);
- return previousTransportName;
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
+ return mUserBackupManagerService.selectBackupTransport(transportName);
}
/**
@@ -3023,70 +330,7 @@
*/
public void selectBackupTransportAsync(
ComponentName transportComponent, ISelectBackupTransportCallback listener) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
-
- final long oldId = Binder.clearCallingIdentity();
- try {
- String transportString = transportComponent.flattenToShortString();
- Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
- mBackupHandler.post(
- () -> {
- String transportName = null;
- int result =
- mTransportManager.registerAndSelectTransport(transportComponent);
- if (result == BackupManager.SUCCESS) {
- try {
- transportName =
- mTransportManager.getTransportName(transportComponent);
- updateStateForTransport(transportName);
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Transport got unregistered");
- result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
- }
- }
-
- try {
- if (transportName != null) {
- listener.onSuccess(transportName);
- } else {
- listener.onFailure(result);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
- }
- });
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
-
- private void updateStateForTransport(String newTransportName) {
- // Publish the name change
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.BACKUP_TRANSPORT, newTransportName);
-
- // And update our current-dataset bookkeeping
- String callerLogString = "BMS.updateStateForTransport()";
- TransportClient transportClient =
- mTransportManager.getTransportClient(newTransportName, callerLogString);
- if (transportClient != null) {
- try {
- IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
- mCurrentToken = transport.getCurrentRestoreSet();
- } catch (Exception e) {
- // Oops. We can't know the current dataset token, so reset and figure it out
- // when we do the next k/v backup operation on this transport.
- mCurrentToken = 0;
- Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
- }
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
- } else {
- Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
- // The named transport isn't registered, so we can't know what its current dataset token
- // is. Reset as above.
- mCurrentToken = 0;
- }
+ mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
}
/**
@@ -3095,18 +339,7 @@
* returns {@code null}.
*/
public Intent getConfigurationIntent(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getConfigurationIntent");
- try {
- Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
- if (MORE_DEBUG) {
- Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
- }
- return intent;
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
- return null;
- }
+ return mUserBackupManagerService.getConfigurationIntent(transportName);
}
/**
@@ -3119,36 +352,12 @@
* @return The current destination string or null if the transport is not registered.
*/
public String getDestinationString(String transportName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "getDestinationString");
-
- try {
- String string = mTransportManager.getTransportCurrentDestinationString(transportName);
- if (MORE_DEBUG) {
- Slog.d(TAG, "getDestinationString() returning " + string);
- }
- return string;
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
- return null;
- }
+ return mUserBackupManagerService.getDestinationString(transportName);
}
/** Supply the manage-data intent for the given transport. */
public Intent getDataManagementIntent(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getDataManagementIntent");
-
- try {
- Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
- if (MORE_DEBUG) {
- Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
- }
- return intent;
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
- return null;
- }
+ return mUserBackupManagerService.getDataManagementIntent(transportName);
}
/**
@@ -3156,443 +365,202 @@
* transport.
*/
public String getDataManagementLabel(String transportName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "getDataManagementLabel");
+ return mUserBackupManagerService.getDataManagementLabel(transportName);
+ }
- try {
- String label = mTransportManager.getTransportDataManagementLabel(transportName);
- if (MORE_DEBUG) {
- Slog.d(TAG, "getDataManagementLabel() returning " + label);
- }
- return label;
- } catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
- return null;
- }
+ // ---------------------------------------------
+ // SETTINGS OPERATIONS
+ // ---------------------------------------------
+
+ /** Enable/disable the backup service. This is user-configurable via backup settings. */
+ public void setBackupEnabled(boolean enable) {
+ mUserBackupManagerService.setBackupEnabled(enable);
+ }
+
+ /** Enable/disable automatic restore of app data at install time. */
+ public void setAutoRestore(boolean autoRestore) {
+ mUserBackupManagerService.setAutoRestore(autoRestore);
+ }
+
+ /** Mark the backup service as having been provisioned (device has gone through SUW). */
+ public void setBackupProvisioned(boolean provisioned) {
+ mUserBackupManagerService.setBackupProvisioned(provisioned);
}
/**
- * Callback: a requested backup agent has been instantiated. This should only be called from the
- * {@link ActivityManager}.
+ * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
*/
- public void agentConnected(String packageName, IBinder agentBinder) {
- synchronized (mAgentConnectLock) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
- IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
- mConnectedAgent = agent;
- mConnecting = false;
- } else {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " claiming agent connected");
- }
- mAgentConnectLock.notifyAll();
- }
+ public boolean isBackupEnabled() {
+ return mUserBackupManagerService.isBackupEnabled();
+ }
+
+ // ---------------------------------------------
+ // BACKUP OPERATIONS
+ // ---------------------------------------------
+
+ /** Checks if the given package {@code packageName} is eligible for backup. */
+ public boolean isAppEligibleForBackup(String packageName) {
+ return mUserBackupManagerService.isAppEligibleForBackup(packageName);
}
/**
- * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
- * to come up in the first place, the agentBinder argument will be {@code null}. This should
- * only be called from the {@link ActivityManager}.
+ * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
*/
- public void agentDisconnected(String packageName) {
- // TODO: handle backup being interrupted
- synchronized (mAgentConnectLock) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- mConnectedAgent = null;
- mConnecting = false;
- } else {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " claiming agent disconnected");
- }
- mAgentConnectLock.notifyAll();
- }
+ public String[] filterAppsEligibleForBackup(String[] packages) {
+ return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
}
/**
- * An application being installed will need a restore pass, then the {@link PackageManager} will
- * need to be told when the restore is finished.
+ * Run a backup pass immediately for any key-value backup applications that have declared that
+ * they have pending updates.
+ */
+ public void backupNow() {
+ mUserBackupManagerService.backupNow();
+ }
+
+ /**
+ * Requests a backup for the inputted {@code packages} with a specified callback {@link
+ * IBackupManagerMonitor} for receiving events during the operation.
+ */
+ public int requestBackup(
+ String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) {
+ return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+ }
+
+ /** Cancel all running backup operations. */
+ public void cancelBackups() {
+ mUserBackupManagerService.cancelBackups();
+ }
+
+ /**
+ * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
+ * use is to perform one app backup per scheduled job execution, and to reschedule the job with
+ * zero latency as long as conditions remain right and we still have work to do.
+ *
+ * @return Whether ongoing work will continue. The return value here will be passed along as the
+ * return value to the callback {@link JobService#onStartJob(JobParameters)}.
+ */
+ public boolean beginFullBackup(FullBackupJob scheduledJob) {
+ return mUserBackupManagerService.beginFullBackup(scheduledJob);
+ }
+
+ /**
+ * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
+ * longer met for running the full backup job.
+ */
+ public void endFullBackup() {
+ mUserBackupManagerService.endFullBackup();
+ }
+
+ /**
+ * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
+ */
+ public void fullTransportBackup(String[] packageNames) {
+ mUserBackupManagerService.fullTransportBackup(packageNames);
+ }
+
+ // ---------------------------------------------
+ // RESTORE OPERATIONS
+ // ---------------------------------------------
+
+ /**
+ * Used to run a restore pass for an application that is being installed. This should only be
+ * called from the {@link PackageManager}.
*/
public void restoreAtInstall(String packageName, int token) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " attemping install-time restore");
- return;
- }
-
- boolean skip = false;
-
- long restoreSet = getAvailableRestoreToken(packageName);
- if (DEBUG) {
- Slog.v(TAG, "restoreAtInstall pkg=" + packageName
- + " token=" + Integer.toHexString(token)
- + " restoreSet=" + Long.toHexString(restoreSet));
- }
- if (restoreSet == 0) {
- if (MORE_DEBUG) Slog.i(TAG, "No restore set");
- skip = true;
- }
-
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
- if (transportClient == null) {
- if (DEBUG) Slog.w(TAG, "No transport client");
- skip = true;
- }
-
- if (!mAutoRestore) {
- if (DEBUG) {
- Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
- }
- skip = true;
- }
-
- if (!skip) {
- try {
- // okay, we're going to attempt a restore of this package from this restore set.
- // The eventual message back into the Package Manager to run the post-install
- // steps for 'token' will be issued from the restore handling code.
-
- mWakelock.acquire();
-
- OnTaskFinishedListener listener = caller -> {
- mTransportManager.disposeOfTransportClient(transportClient, caller);
- mWakelock.release();
- };
-
- if (MORE_DEBUG) {
- Slog.d(TAG, "Restore at install of " + packageName);
- }
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
- msg.obj =
- RestoreParams.createForRestoreAtInstall(
- transportClient,
- /* observer */ null,
- /* monitor */ null,
- restoreSet,
- packageName,
- token,
- listener);
- mBackupHandler.sendMessage(msg);
- } catch (Exception e) {
- // Calling into the transport broke; back off and proceed with the installation.
- Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
- skip = true;
- }
- }
-
- if (skip) {
- // Auto-restore disabled or no way to attempt a restore
-
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(
- transportClient, "BMS.restoreAtInstall()");
- }
-
- // Tell the PackageManager to proceed with the post-install handling for this package.
- if (DEBUG) Slog.v(TAG, "Finishing install immediately");
- try {
- mPackageManagerBinder.finishPackageInstall(token, false);
- } catch (RemoteException e) { /* can't happen */ }
- }
- }
-
- /** Hand off a restore session. */
- public IRestoreSession beginRestoreSession(String packageName, String transport) {
- if (DEBUG) {
- Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
- + " transport=" + transport);
- }
-
- boolean needPermission = true;
- if (transport == null) {
- transport = mTransportManager.getCurrentTransportName();
-
- if (packageName != null) {
- PackageInfo app = null;
- try {
- app = mPackageManager.getPackageInfo(packageName, 0);
- } catch (NameNotFoundException nnf) {
- Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
- throw new IllegalArgumentException("Package " + packageName + " not found");
- }
-
- if (app.applicationInfo.uid == Binder.getCallingUid()) {
- // So: using the current active transport, and the caller has asked
- // that its own package will be restored. In this narrow use case
- // we do not require the caller to hold the permission.
- needPermission = false;
- }
- }
- }
-
- if (needPermission) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "beginRestoreSession");
- } else {
- if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
- }
-
- synchronized (this) {
- if (mActiveRestoreSession != null) {
- Slog.i(TAG, "Restore session requested but one already active");
- return null;
- }
- if (mBackupRunning) {
- Slog.i(TAG, "Restore session requested but currently running backups");
- return null;
- }
- mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
- mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
- mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
- }
- return mActiveRestoreSession;
- }
-
- /** Clear the specified restore session. */
- public void clearRestoreSession(ActiveRestoreSession currentSession) {
- synchronized (this) {
- if (currentSession != mActiveRestoreSession) {
- Slog.e(TAG, "ending non-current restore session");
- } else {
- if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
- mActiveRestoreSession = null;
- mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
- }
- }
+ mUserBackupManagerService.restoreAtInstall(packageName, token);
}
/**
- * Note that a currently-active backup agent has notified us that it has completed the given
- * outstanding asynchronous backup/restore operation.
+ * Begin a restore for the specified package {@code packageName} using the specified transport
+ * {@code transportName}.
*/
- public void opComplete(int token, long result) {
- if (MORE_DEBUG) {
- Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
- }
- Operation op = null;
- synchronized (mCurrentOpLock) {
- op = mCurrentOperations.get(token);
- if (op != null) {
- if (op.state == OP_TIMEOUT) {
- // The operation already timed out, and this is a late response. Tidy up
- // and ignore it; we've already dealt with the timeout.
- op = null;
- mCurrentOperations.delete(token);
- } else if (op.state == OP_ACKNOWLEDGED) {
- if (DEBUG) {
- Slog.w(TAG, "Received duplicate ack for token="
- + Integer.toHexString(token));
- }
- op = null;
- mCurrentOperations.remove(token);
- } else if (op.state == OP_PENDING) {
- // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
- // called after we we receive this call.
- op.state = OP_ACKNOWLEDGED;
- }
- }
- mCurrentOpLock.notifyAll();
- }
-
- // The completion callback, if any, is invoked on the handler
- if (op != null && op.callback != null) {
- Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
- Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
- mBackupHandler.sendMessage(msg);
- }
+ public IRestoreSession beginRestoreSession(String packageName, String transportName) {
+ return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
}
- /** Checks if the package is eligible for backup. */
- public boolean isAppEligibleForBackup(String packageName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
-
- long oldToken = Binder.clearCallingIdentity();
- try {
- String callerLogString = "BMS.isAppEligibleForBackup";
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient(callerLogString);
- boolean eligible =
- AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager);
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
- }
- return eligible;
- } finally {
- Binder.restoreCallingIdentity(oldToken);
- }
+ /**
+ * Get the restore-set token for the best-available restore set for this {@code packageName}:
+ * the active set if possible, else the ancestral one. Returns zero if none available.
+ */
+ public long getAvailableRestoreToken(String packageName) {
+ return mUserBackupManagerService.getAvailableRestoreToken(packageName);
}
- /** Returns the inputted packages that are eligible for backup. */
- public String[] filterAppsEligibleForBackup(String[] packages) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
+ // ---------------------------------------------
+ // ADB BACKUP/RESTORE OPERATIONS
+ // ---------------------------------------------
- long oldToken = Binder.clearCallingIdentity();
- try {
- String callerLogString = "BMS.filterAppsEligibleForBackup";
- TransportClient transportClient =
- mTransportManager.getCurrentTransportClient(callerLogString);
- List<String> eligibleApps = new LinkedList<>();
- for (String packageName : packages) {
- if (AppBackupUtils
- .appIsRunningAndEligibleForBackupWithTransport(
- transportClient, packageName, mPackageManager)) {
- eligibleApps.add(packageName);
- }
- }
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
- }
- return eligibleApps.toArray(new String[eligibleApps.size()]);
- } finally {
- Binder.restoreCallingIdentity(oldToken);
- }
+ /** Sets the backup password used when running adb backup. */
+ public boolean setBackupPassword(String currentPassword, String newPassword) {
+ return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
}
+ /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
+ public boolean hasBackupPassword() {
+ return mUserBackupManagerService.hasBackupPassword();
+ }
+
+ /**
+ * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
+ * command line, writing the resulting data stream to the supplied {@code fd}. This method is
+ * synchronous and does not return to the caller until the backup has been completed. It
+ * requires on-screen confirmation by the user.
+ */
+ public void adbBackup(
+ ParcelFileDescriptor fd,
+ boolean includeApks,
+ boolean includeObbs,
+ boolean includeShared,
+ boolean doWidgets,
+ boolean doAllApps,
+ boolean includeSystem,
+ boolean doCompress,
+ boolean doKeyValue,
+ String[] packageNames) {
+ mUserBackupManagerService.adbBackup(
+ fd,
+ includeApks,
+ includeObbs,
+ includeShared,
+ doWidgets,
+ doAllApps,
+ includeSystem,
+ doCompress,
+ doKeyValue,
+ packageNames);
+ }
+
+ /**
+ * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
+ * is synchronous and does not return to the caller until the restore has been completed. It
+ * requires on-screen confirmation by the user.
+ */
+ public void adbRestore(ParcelFileDescriptor fd) {
+ mUserBackupManagerService.adbRestore(fd);
+ }
+
+ /**
+ * Confirm that the previously requested adb backup/restore operation can proceed. This is used
+ * to require a user-facing disclosure about the operation.
+ */
+ public void acknowledgeAdbBackupOrRestore(
+ int token,
+ boolean allow,
+ String currentPassword,
+ String encryptionPassword,
+ IFullBackupRestoreObserver observer) {
+ mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
+ token, allow, currentPassword, encryptionPassword, observer);
+ }
+
+ // ---------------------------------------------
+ // SERVICE OPERATIONS
+ // ---------------------------------------------
+
/** Prints service state for 'dumpsys backup'. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
-
- long identityToken = Binder.clearCallingIdentity();
- try {
- if (args != null) {
- for (String arg : args) {
- if ("-h".equals(arg)) {
- pw.println("'dumpsys backup' optional arguments:");
- pw.println(" -h : this help text");
- pw.println(" a[gents] : dump information about defined backup agents");
- return;
- } else if ("agents".startsWith(arg)) {
- dumpAgents(pw);
- return;
- } else if ("transportclients".equals(arg.toLowerCase())) {
- mTransportManager.dumpTransportClients(pw);
- return;
- } else if ("transportstats".equals(arg.toLowerCase())) {
- mTransportManager.dumpTransportStats(pw);
- return;
- }
- }
- }
- dumpInternal(pw);
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- private void dumpAgents(PrintWriter pw) {
- List<PackageInfo> agentPackages = allAgentPackages();
- pw.println("Defined backup agents:");
- for (PackageInfo pkg : agentPackages) {
- pw.print(" ");
- pw.print(pkg.packageName);
- pw.println(':');
- pw.print(" ");
- pw.println(pkg.applicationInfo.backupAgentName);
- }
- }
-
- private void dumpInternal(PrintWriter pw) {
- synchronized (mQueueLock) {
- pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
- + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
- + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
- pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
- if (mBackupRunning) pw.println("Backup currently running");
- pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
- pw.println("Last backup pass started: " + mLastBackupPass
- + " (now = " + System.currentTimeMillis() + ')');
- pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled());
-
- pw.println("Transport whitelist:");
- for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
- pw.print(" ");
- pw.println(transport.flattenToShortString());
- }
-
- pw.println("Available transports:");
- final String[] transports = listAllTransports();
- if (transports != null) {
- for (String t : transports) {
- pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * "
- : " ") + t);
- try {
- File dir = new File(mBaseStateDir,
- mTransportManager.getTransportDirName(t));
- pw.println(" destination: "
- + mTransportManager.getTransportCurrentDestinationString(t));
- pw.println(" intent: "
- + mTransportManager.getTransportConfigurationIntent(t));
- for (File f : dir.listFiles()) {
- pw.println(
- " " + f.getName() + " - " + f.length() + " state bytes");
- }
- } catch (Exception e) {
- Slog.e(TAG, "Error in transport", e);
- pw.println(" Error: " + e);
- }
- }
- }
-
- mTransportManager.dumpTransportClients(pw);
-
- pw.println("Pending init: " + mPendingInits.size());
- for (String s : mPendingInits) {
- pw.println(" " + s);
- }
-
- pw.print("Ancestral: ");
- pw.println(Long.toHexString(mAncestralToken));
- pw.print("Current: ");
- pw.println(Long.toHexString(mCurrentToken));
-
- int numPackages = mBackupParticipants.size();
- pw.println("Participants:");
- for (int i = 0; i < numPackages; i++) {
- int uid = mBackupParticipants.keyAt(i);
- pw.print(" uid: ");
- pw.println(uid);
- HashSet<String> participants = mBackupParticipants.valueAt(i);
- for (String app : participants) {
- pw.println(" " + app);
- }
- }
-
- pw.println("Ancestral packages: "
- + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
- if (mAncestralPackages != null) {
- for (String pkg : mAncestralPackages) {
- pw.println(" " + pkg);
- }
- }
-
- Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
- pw.println("Ever backed up: " + processedPackages.size());
- for (String pkg : processedPackages) {
- pw.println(" " + pkg);
- }
-
- pw.println("Pending key/value backup: " + mPendingBackups.size());
- for (BackupRequest req : mPendingBackups.values()) {
- pw.println(" " + req);
- }
-
- pw.println("Full backup queue:" + mFullBackupQueue.size());
- for (FullBackupEntry entry : mFullBackupQueue) {
- pw.print(" ");
- pw.print(entry.lastBackup);
- pw.print(" : ");
- pw.println(entry.packageName);
- }
- }
- }
-
-
- public IBackupManager getBackupManagerBinder() {
- return mBackupManagerBinder;
+ mUserBackupManagerService.dump(fd, pw, args);
}
private static boolean backupSettingMigrated(int userId) {
@@ -3620,7 +588,7 @@
return false;
}
- private static void writeBackupEnableState(boolean enable, int userId) {
+ static void writeBackupEnableState(boolean enable, int userId) {
File base = new File(Environment.getDataDirectory(), "backup");
File enableFile = new File(base, BACKUP_ENABLE_FILE);
File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 125c225..92c2ee4 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -5,7 +5,8 @@
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -50,7 +51,7 @@
private static final String BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX = ".data";
private static final String BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX = ".new";
- private BackupManagerService mBackupManagerService;
+ private UserBackupManagerService mBackupManagerService;
private final PackageManager mPackageManager;
private final OutputStream mOutput;
private final PackageInfo mCurrentPackage;
@@ -66,7 +67,7 @@
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
- BackupManagerService backupManagerService, PackageManager packageManager,
+ UserBackupManagerService backupManagerService, PackageManager packageManager,
File baseStateDir, File dataDir) {
mOutput = output;
mCurrentPackage = packageInfo;
@@ -85,7 +86,7 @@
mNewStateName = new File(mStateDir,
pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
- mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+ mManifestFile = new File(mDataDir, BACKUP_MANIFEST_FILENAME);
mAgentTimeoutParameters = Preconditions.checkNotNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index bb14576..bed520e 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -39,7 +39,7 @@
private static final String TAG = "KeyValueAdbRestoreEngine";
private static final boolean DEBUG = false;
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final File mDataDir;
FileMetadata mInfo;
@@ -48,7 +48,7 @@
IBackupAgent mAgent;
int mToken;
- public KeyValueAdbRestoreEngine(BackupManagerService backupManagerService,
+ public KeyValueAdbRestoreEngine(UserBackupManagerService backupManagerService,
File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
int token) {
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index c805783..f2e7435 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -16,6 +16,8 @@
package com.android.server.backup;
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -71,7 +73,7 @@
if (delay <= 0) {
delay = interval + new Random().nextInt((int) fuzz);
}
- if (BackupManagerService.DEBUG_SCHEDULING) {
+ if (DEBUG_SCHEDULING) {
Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
}
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
diff --git a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
index b5db5e2..edc2379 100644
--- a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
+++ b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
@@ -23,8 +23,8 @@
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
-import java.io.FileInputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashSet;
@@ -46,7 +46,7 @@
final class ProcessedPackagesJournal {
private static final String TAG = "ProcessedPackagesJournal";
private static final String JOURNAL_FILE_NAME = "processed";
- private static final boolean DEBUG = BackupManagerService.DEBUG || false;
+ private static final boolean DEBUG = BackupManagerService.DEBUG;
// using HashSet instead of ArraySet since we expect 100-500 elements range
@GuardedBy("mProcessedPackages")
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 32fd7e0..59629aa 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -42,18 +42,20 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
/**
- * A proxy to BackupManagerService implementation.
+ * A proxy to the {@link BackupManagerService} implementation.
*
- * <p>This is an external interface to the BackupManagerService which is being accessed via
- * published binder (see BackupManagerService$Lifecycle). This lets us turn down the heavy
+ * <p>This is an external interface to the {@link BackupManagerService} which is being accessed via
+ * published binder {@link BackupManagerService.Lifecycle}. This lets us turn down the heavy
* implementation object on the fly without disturbing binders that have been cached somewhere in
* the system.
*
@@ -143,7 +145,7 @@
}
/**
- * Called from {@link BackupManagerService$Lifecycle} when the system user is unlocked. Attempts
+ * Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts
* to initialize {@link BackupManagerService} and set backup state for the system user.
*
* @see BackupManagerService#unlockSystemUser()
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
new file mode 100644
index 0000000..fe16afe
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -0,0 +1,3501 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
+
+import static com.android.server.backup.BackupManagerService.DEBUG;
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
+import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKUP;
+import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import android.app.IBackupAgent;
+import android.app.PendingIntent;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.FullBackup;
+import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.PowerManager.ServiceType;
+import android.os.PowerSaveState;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.EventLog;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppWidgetBackupBridge;
+import com.android.server.EventLogTags;
+import com.android.server.backup.fullbackup.FullBackupEntry;
+import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.ClearDataObserver;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.internal.Operation;
+import com.android.server.backup.internal.PerformInitializeTask;
+import com.android.server.backup.internal.ProvisionedObserver;
+import com.android.server.backup.internal.RunBackupReceiver;
+import com.android.server.backup.internal.RunInitializeReceiver;
+import com.android.server.backup.keyvalue.BackupRequest;
+import com.android.server.backup.params.AdbBackupParams;
+import com.android.server.backup.params.AdbParams;
+import com.android.server.backup.params.AdbRestoreParams;
+import com.android.server.backup.params.BackupParams;
+import com.android.server.backup.params.ClearParams;
+import com.android.server.backup.params.ClearRetryParams;
+import com.android.server.backup.params.RestoreParams;
+import com.android.server.backup.restore.ActiveRestoreSession;
+import com.android.server.backup.restore.PerformUnifiedRestoreTask;
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupObserverUtils;
+import com.android.server.backup.utils.SparseArrayUtils;
+
+import com.google.android.collect.Sets;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.RandomAccessFile;
+import java.security.SecureRandom;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** System service that performs backup/restore operations. */
+public class UserBackupManagerService {
+ // File containing backup-enabled state. Contains a single byte;
+ // nonzero == enabled. File missing or contains a zero byte == disabled.
+ private static final String BACKUP_ENABLE_FILE = "backup_enabled";
+
+ // Persistently track the need to do a full init.
+ private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
+
+ // System-private key used for backing up an app's widget state. Must
+ // begin with U+FFxx by convention (we reserve all keys starting
+ // with U+FF00 or higher for system use).
+ public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
+
+ // Name and current contents version of the full-backup manifest file
+ //
+ // Manifest version history:
+ //
+ // 1 : initial release
+ public static final String BACKUP_MANIFEST_FILENAME = "_manifest";
+ public static final int BACKUP_MANIFEST_VERSION = 1;
+
+ // External archive format version history:
+ //
+ // 1 : initial release
+ // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
+ // 3 : introduced "_meta" metadata file; no other format change per se
+ // 4 : added support for new device-encrypted storage locations
+ // 5 : added support for key-value packages
+ public static final int BACKUP_FILE_VERSION = 5;
+ public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
+ public static final String BACKUP_METADATA_FILENAME = "_meta";
+ public static final int BACKUP_METADATA_VERSION = 1;
+ public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
+
+ private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
+
+ // Round-robin queue for scheduling full backup passes.
+ private static final int SCHEDULE_FILE_VERSION = 1;
+
+ public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
+ public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
+
+ // Pseudoname that we use for the Package Manager metadata "package".
+ public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+
+ // Retry interval for clear/init when the transport is unavailable
+ private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
+
+ public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
+ public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
+ public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+ public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+
+ // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
+ // pending operations list.
+ public static final int OP_PENDING = 0;
+ private static final int OP_ACKNOWLEDGED = 1;
+ private static final int OP_TIMEOUT = -1;
+
+ // Waiting for backup agent to respond during backup operation.
+ public static final int OP_TYPE_BACKUP_WAIT = 0;
+
+ // Waiting for backup agent to respond during restore operation.
+ public static final int OP_TYPE_RESTORE_WAIT = 1;
+
+ // An entire backup operation spanning multiple packages.
+ public static final int OP_TYPE_BACKUP = 2;
+
+ // Time delay for initialization operations that can be delayed so as not to consume too much
+ // CPU on bring-up and increase time-to-UI.
+ private static final long INITIALIZATION_DELAY_MILLIS = 3000;
+
+ // Timeout interval for deciding that a bind or clear-data has taken too long
+ private static final long TIMEOUT_INTERVAL = 10 * 1000;
+
+ // User confirmation timeout for a full backup/restore operation. It's this long in
+ // order to give them time to enter the backup password.
+ private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
+
+ // If an app is busy when we want to do a full-data backup, how long to defer the retry.
+ // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
+ private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
+ private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
+
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+ private final TransportManager mTransportManager;
+
+ private Context mContext;
+ private PackageManager mPackageManager;
+ private IPackageManager mPackageManagerBinder;
+ private IActivityManager mActivityManager;
+ private PowerManager mPowerManager;
+ private AlarmManager mAlarmManager;
+ private IStorageManager mStorageManager;
+ private BackupManagerConstants mConstants;
+ private PowerManager.WakeLock mWakelock;
+ private BackupHandler mBackupHandler;
+
+ private IBackupManager mBackupManagerBinder;
+
+ private boolean mEnabled; // access to this is synchronized on 'this'
+ private boolean mProvisioned;
+ private boolean mAutoRestore;
+
+ private PendingIntent mRunBackupIntent;
+ private PendingIntent mRunInitIntent;
+
+ private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
+
+ // map UIDs to the set of participating packages under that UID
+ private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
+
+ // Backups that we haven't started yet. Keys are package names.
+ private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+
+ // locking around the pending-backup management
+ private final Object mQueueLock = new Object();
+
+ // The thread performing the sequence of queued backups binds to each app's agent
+ // in succession. Bind notifications are asynchronously delivered through the
+ // Activity Manager; use this lock object to signal when a requested binding has
+ // completed.
+ private final Object mAgentConnectLock = new Object();
+ private IBackupAgent mConnectedAgent;
+ private volatile boolean mConnecting;
+
+ private volatile boolean mBackupRunning;
+ private volatile long mLastBackupPass;
+
+ // A similar synchronization mechanism around clearing apps' data for restore
+ private final Object mClearDataLock = new Object();
+ private volatile boolean mClearingData;
+
+ // Used by ADB.
+ private final BackupPasswordManager mBackupPasswordManager;
+ private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
+ private final SecureRandom mRng = new SecureRandom();
+
+ // Time when we post the transport registration operation
+ private final long mRegisterTransportsRequestedTime;
+
+ @GuardedBy("mQueueLock")
+ private PerformFullTransportBackupTask mRunningFullBackupTask;
+
+ @GuardedBy("mQueueLock")
+ private ArrayList<FullBackupEntry> mFullBackupQueue;
+
+ @GuardedBy("mPendingRestores")
+ private boolean mIsRestoreInProgress;
+
+ @GuardedBy("mPendingRestores")
+ private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
+
+ private ActiveRestoreSession mActiveRestoreSession;
+
+ // Watch the device provisioning operation during setup
+ private ContentObserver mProvisionedObserver;
+
+ /**
+ * mCurrentOperations contains the list of currently active operations.
+ *
+ * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
+ * An operation wraps a BackupRestoreTask within it.
+ * It's the responsibility of this task to remove the operation from this array.
+ *
+ * A BackupRestore task gets notified of ack/timeout for the operation via
+ * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
+ * on the mCurrentOpLock.
+ * {@link UserBackupManagerService#waitUntilOperationComplete(int)} is
+ * used in various places to 'wait' for notifyAll and detect change of pending state of an
+ * operation. So typically, an operation will be removed from this array by:
+ * - BackupRestoreTask#handleCancel and
+ * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
+ * these places because waitUntilOperationComplete relies on the operation being present to
+ * determine its completion status.
+ *
+ * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
+ * cancel backup tasks.
+ */
+ @GuardedBy("mCurrentOpLock")
+ private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
+ private final Object mCurrentOpLock = new Object();
+ private final Random mTokenGenerator = new Random();
+ final AtomicInteger mNextToken = new AtomicInteger();
+
+ // Where we keep our journal files and other bookkeeping.
+ private File mBaseStateDir;
+ private File mDataDir;
+ private File mJournalDir;
+ @Nullable
+ private DataChangedJournal mJournal;
+ private File mFullBackupScheduleFile;
+
+ // Keep a log of all the apps we've ever backed up.
+ private ProcessedPackagesJournal mProcessedPackagesJournal;
+
+ private File mTokenFile;
+ private Set<String> mAncestralPackages = null;
+ private long mAncestralToken = 0;
+ private long mCurrentToken = 0;
+
+ @VisibleForTesting
+ public UserBackupManagerService(
+ Context context,
+ Trampoline parent,
+ HandlerThread backupThread,
+ File baseStateDir,
+ File dataDir,
+ TransportManager transportManager) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mPackageManagerBinder = AppGlobals.getPackageManager();
+ mActivityManager = ActivityManager.getService();
+
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
+
+ mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+
+ mAgentTimeoutParameters = new
+ BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+ mAgentTimeoutParameters.start();
+
+ // spin up the backup/restore handler thread
+ mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+
+ // Set up our bookkeeping
+ final ContentResolver resolver = context.getContentResolver();
+ mProvisioned = Settings.Global.getInt(resolver,
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ mAutoRestore = Settings.Secure.getInt(resolver,
+ Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+
+ mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mProvisionedObserver);
+
+ mBaseStateDir = baseStateDir;
+ mBaseStateDir.mkdirs();
+ if (!SELinux.restorecon(mBaseStateDir)) {
+ Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+ }
+
+ mDataDir = dataDir;
+
+ mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
+
+ // Alarm receivers for scheduled backups & initialization operations
+ BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(RUN_BACKUP_ACTION);
+ context.registerReceiver(mRunBackupReceiver, filter,
+ android.Manifest.permission.BACKUP, null);
+
+ BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+ filter = new IntentFilter();
+ filter.addAction(RUN_INITIALIZE_ACTION);
+ context.registerReceiver(mRunInitReceiver, filter,
+ android.Manifest.permission.BACKUP, null);
+
+ Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
+ backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+
+ Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
+ initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+
+ // Set up the backup-request journaling
+ mJournalDir = new File(mBaseStateDir, "pending");
+ mJournalDir.mkdirs(); // creates mBaseStateDir along the way
+ mJournal = null; // will be created on first use
+
+ mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
+ // We are observing changes to the constants throughout the lifecycle of BMS. This is
+ // because we reference the constants in multiple areas of BMS, which otherwise would
+ // require frequent starting and stopping.
+ mConstants.start();
+
+ // Set up the various sorts of package tracking we do
+ mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
+ initPackageTracking();
+
+ // Build our mapping of uid to backup client services. This implicitly
+ // schedules a backup pass on the Package Manager metadata the first
+ // time anything needs to be backed up.
+ synchronized (mBackupParticipants) {
+ addPackageParticipantsLocked(null);
+ }
+
+ mTransportManager = transportManager;
+ mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
+ mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
+ mBackupHandler.postDelayed(
+ mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
+
+ // Now that we know about valid backup participants, parse any leftover journal files into
+ // the pending backup set
+ mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+
+ // Power management
+ mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+ }
+
+
+ public BackupManagerConstants getConstants() {
+ return mConstants;
+ }
+
+ public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+ return mAgentTimeoutParameters;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ public void setPackageManager(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ public IPackageManager getPackageManagerBinder() {
+ return mPackageManagerBinder;
+ }
+
+ public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
+ mPackageManagerBinder = packageManagerBinder;
+ }
+
+ public IActivityManager getActivityManager() {
+ return mActivityManager;
+ }
+
+ public void setActivityManager(IActivityManager activityManager) {
+ mActivityManager = activityManager;
+ }
+
+ public AlarmManager getAlarmManager() {
+ return mAlarmManager;
+ }
+
+ public void setAlarmManager(AlarmManager alarmManager) {
+ mAlarmManager = alarmManager;
+ }
+
+ @VisibleForTesting
+ void setPowerManager(PowerManager powerManager) {
+ mPowerManager = powerManager;
+ }
+
+ public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
+ mBackupManagerBinder = backupManagerBinder;
+ }
+
+ public TransportManager getTransportManager() {
+ return mTransportManager;
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ }
+
+ public boolean isProvisioned() {
+ return mProvisioned;
+ }
+
+ public void setProvisioned(boolean provisioned) {
+ mProvisioned = provisioned;
+ }
+
+ public PowerManager.WakeLock getWakelock() {
+ return mWakelock;
+ }
+
+ /**
+ * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
+ * #getWakelock()}.
+ */
+ @VisibleForTesting
+ public void setWorkSource(@Nullable WorkSource workSource) {
+ // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
+ mWakelock.setWorkSource(workSource);
+ }
+
+ public void setWakelock(PowerManager.WakeLock wakelock) {
+ mWakelock = wakelock;
+ }
+
+ public Handler getBackupHandler() {
+ return mBackupHandler;
+ }
+
+ public void setBackupHandler(BackupHandler backupHandler) {
+ mBackupHandler = backupHandler;
+ }
+
+ public PendingIntent getRunInitIntent() {
+ return mRunInitIntent;
+ }
+
+ public void setRunInitIntent(PendingIntent runInitIntent) {
+ mRunInitIntent = runInitIntent;
+ }
+
+ public HashMap<String, BackupRequest> getPendingBackups() {
+ return mPendingBackups;
+ }
+
+ public void setPendingBackups(
+ HashMap<String, BackupRequest> pendingBackups) {
+ mPendingBackups = pendingBackups;
+ }
+
+ public Object getQueueLock() {
+ return mQueueLock;
+ }
+
+ public boolean isBackupRunning() {
+ return mBackupRunning;
+ }
+
+ public void setBackupRunning(boolean backupRunning) {
+ mBackupRunning = backupRunning;
+ }
+
+ public long getLastBackupPass() {
+ return mLastBackupPass;
+ }
+
+ public void setLastBackupPass(long lastBackupPass) {
+ mLastBackupPass = lastBackupPass;
+ }
+
+ public Object getClearDataLock() {
+ return mClearDataLock;
+ }
+
+ public boolean isClearingData() {
+ return mClearingData;
+ }
+
+ public void setClearingData(boolean clearingData) {
+ mClearingData = clearingData;
+ }
+
+ public boolean isRestoreInProgress() {
+ return mIsRestoreInProgress;
+ }
+
+ public void setRestoreInProgress(boolean restoreInProgress) {
+ mIsRestoreInProgress = restoreInProgress;
+ }
+
+ public Queue<PerformUnifiedRestoreTask> getPendingRestores() {
+ return mPendingRestores;
+ }
+
+ public ActiveRestoreSession getActiveRestoreSession() {
+ return mActiveRestoreSession;
+ }
+
+ public void setActiveRestoreSession(
+ ActiveRestoreSession activeRestoreSession) {
+ mActiveRestoreSession = activeRestoreSession;
+ }
+
+ public SparseArray<Operation> getCurrentOperations() {
+ return mCurrentOperations;
+ }
+
+ public Object getCurrentOpLock() {
+ return mCurrentOpLock;
+ }
+
+ public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
+ return mAdbBackupRestoreConfirmations;
+ }
+
+ public File getBaseStateDir() {
+ return mBaseStateDir;
+ }
+
+ public void setBaseStateDir(File baseStateDir) {
+ mBaseStateDir = baseStateDir;
+ }
+
+ public File getDataDir() {
+ return mDataDir;
+ }
+
+ public void setDataDir(File dataDir) {
+ mDataDir = dataDir;
+ }
+
+ @Nullable
+ public DataChangedJournal getJournal() {
+ return mJournal;
+ }
+
+ public void setJournal(@Nullable DataChangedJournal journal) {
+ mJournal = journal;
+ }
+
+ public SecureRandom getRng() {
+ return mRng;
+ }
+
+ public Set<String> getAncestralPackages() {
+ return mAncestralPackages;
+ }
+
+ public void setAncestralPackages(Set<String> ancestralPackages) {
+ mAncestralPackages = ancestralPackages;
+ }
+
+ public long getAncestralToken() {
+ return mAncestralToken;
+ }
+
+ public void setAncestralToken(long ancestralToken) {
+ mAncestralToken = ancestralToken;
+ }
+
+ public long getCurrentToken() {
+ return mCurrentToken;
+ }
+
+ public void setCurrentToken(long currentToken) {
+ mCurrentToken = currentToken;
+ }
+
+ public ArraySet<String> getPendingInits() {
+ return mPendingInits;
+ }
+
+ /** Clear all pending transport initializations. */
+ public void clearPendingInits() {
+ mPendingInits.clear();
+ }
+
+ public PerformFullTransportBackupTask getRunningFullBackupTask() {
+ return mRunningFullBackupTask;
+ }
+
+ public void setRunningFullBackupTask(
+ PerformFullTransportBackupTask runningFullBackupTask) {
+ mRunningFullBackupTask = runningFullBackupTask;
+ }
+
+ /**
+ * Utility: build a new random integer token. The low bits are the ordinal of the operation for
+ * near-time uniqueness, and the upper bits are random for app-side unpredictability.
+ */
+ public int generateRandomIntegerToken() {
+ int token = mTokenGenerator.nextInt();
+ if (token < 0) token = -token;
+ token &= ~0xFF;
+ token |= (mNextToken.incrementAndGet() & 0xFF);
+ return token;
+ }
+
+ /**
+ * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
+ * non-lifecycle agent instance, so we manually set up the context topology for it.
+ */
+ public BackupAgent makeMetadataAgent() {
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+ pmAgent.attach(mContext);
+ pmAgent.onCreate();
+ return pmAgent;
+ }
+
+ /**
+ * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
+ */
+ public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
+ PackageManagerBackupAgent pmAgent =
+ new PackageManagerBackupAgent(mPackageManager, packages);
+ pmAgent.attach(mContext);
+ pmAgent.onCreate();
+ return pmAgent;
+ }
+
+ private void initPackageTracking() {
+ if (MORE_DEBUG) Slog.v(TAG, "` tracking");
+
+ // Remember our ancestral dataset
+ mTokenFile = new File(mBaseStateDir, "ancestral");
+ try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
+ new FileInputStream(mTokenFile)))) {
+ int version = tokenStream.readInt();
+ if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
+ mAncestralToken = tokenStream.readLong();
+ mCurrentToken = tokenStream.readLong();
+
+ int numPackages = tokenStream.readInt();
+ if (numPackages >= 0) {
+ mAncestralPackages = new HashSet<>();
+ for (int i = 0; i < numPackages; i++) {
+ String pkgName = tokenStream.readUTF();
+ mAncestralPackages.add(pkgName);
+ }
+ }
+ }
+ } catch (FileNotFoundException fnf) {
+ // Probably innocuous
+ Slog.v(TAG, "No ancestral data");
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to read token file", e);
+ }
+
+ mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
+ mProcessedPackagesJournal.init();
+
+ synchronized (mQueueLock) {
+ // Resume the full-data backup queue
+ mFullBackupQueue = readFullBackupSchedule();
+ }
+
+ // Register for broadcasts about package install, etc., so we can
+ // update the provider list.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ // Register for events related to sdcard installation.
+ IntentFilter sdFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+ }
+
+ private ArrayList<FullBackupEntry> readFullBackupSchedule() {
+ boolean changed = false;
+ ArrayList<FullBackupEntry> schedule = null;
+ List<PackageInfo> apps =
+ PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+
+ if (mFullBackupScheduleFile.exists()) {
+ try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
+ BufferedInputStream bufStream = new BufferedInputStream(fstream);
+ DataInputStream in = new DataInputStream(bufStream)) {
+ int version = in.readInt();
+ if (version != SCHEDULE_FILE_VERSION) {
+ Slog.e(TAG, "Unknown backup schedule version " + version);
+ return null;
+ }
+
+ final int numPackages = in.readInt();
+ schedule = new ArrayList<>(numPackages);
+
+ // HashSet instead of ArraySet specifically because we want the eventual
+ // lookups against O(hundreds) of entries to be as fast as possible, and
+ // we discard the set immediately after the scan so the extra memory
+ // overhead is transient.
+ HashSet<String> foundApps = new HashSet<>(numPackages);
+
+ for (int i = 0; i < numPackages; i++) {
+ String pkgName = in.readUTF();
+ long lastBackup = in.readLong();
+ foundApps.add(pkgName); // all apps that we've addressed already
+ try {
+ PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
+ if (AppBackupUtils.appGetsFullBackup(pkg)
+ && AppBackupUtils.appIsEligibleForBackup(
+ pkg.applicationInfo, mPackageManager)) {
+ schedule.add(new FullBackupEntry(pkgName, lastBackup));
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Package " + pkgName
+ + " no longer eligible for full backup");
+ }
+ }
+ } catch (NameNotFoundException e) {
+ if (DEBUG) {
+ Slog.i(TAG, "Package " + pkgName
+ + " not installed; dropping from full backup");
+ }
+ }
+ }
+
+ // New apps can arrive "out of band" via OTA and similar, so we also need to
+ // scan to make sure that we're tracking all full-backup candidates properly
+ for (PackageInfo app : apps) {
+ if (AppBackupUtils.appGetsFullBackup(app)
+ && AppBackupUtils.appIsEligibleForBackup(
+ app.applicationInfo, mPackageManager)) {
+ if (!foundApps.contains(app.packageName)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "New full backup app " + app.packageName + " found");
+ }
+ schedule.add(new FullBackupEntry(app.packageName, 0));
+ changed = true;
+ }
+ }
+ }
+
+ Collections.sort(schedule);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to read backup schedule", e);
+ mFullBackupScheduleFile.delete();
+ schedule = null;
+ }
+ }
+
+ if (schedule == null) {
+ // no prior queue record, or unable to read it. Set up the queue
+ // from scratch.
+ changed = true;
+ schedule = new ArrayList<>(apps.size());
+ for (PackageInfo info : apps) {
+ if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
+ info.applicationInfo, mPackageManager)) {
+ schedule.add(new FullBackupEntry(info.packageName, 0));
+ }
+ }
+ }
+
+ if (changed) {
+ writeFullBackupScheduleAsync();
+ }
+ return schedule;
+ }
+
+ private Runnable mFullBackupScheduleWriter = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mQueueLock) {
+ try {
+ ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
+ DataOutputStream bufOut = new DataOutputStream(bufStream);
+ bufOut.writeInt(SCHEDULE_FILE_VERSION);
+
+ // version 1:
+ //
+ // [int] # of packages in the queue = N
+ // N * {
+ // [utf8] package name
+ // [long] last backup time for this package
+ // }
+ int numPackages = mFullBackupQueue.size();
+ bufOut.writeInt(numPackages);
+
+ for (int i = 0; i < numPackages; i++) {
+ FullBackupEntry entry = mFullBackupQueue.get(i);
+ bufOut.writeUTF(entry.packageName);
+ bufOut.writeLong(entry.lastBackup);
+ }
+ bufOut.flush();
+
+ AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
+ FileOutputStream out = af.startWrite();
+ out.write(bufStream.toByteArray());
+ af.finishWrite(out);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to write backup schedule!", e);
+ }
+ }
+ }
+ };
+
+ private void writeFullBackupScheduleAsync() {
+ mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
+ mBackupHandler.post(mFullBackupScheduleWriter);
+ }
+
+ private void parseLeftoverJournals() {
+ ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
+ for (DataChangedJournal journal : journals) {
+ if (!journal.equals(mJournal)) {
+ try {
+ journal.forEach(packageName -> {
+ Slog.i(TAG, "Found stale backup journal, scheduling");
+ if (MORE_DEBUG) Slog.i(TAG, " " + packageName);
+ dataChangedImpl(packageName);
+ });
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't read " + journal, e);
+ }
+ }
+ }
+ }
+
+ /** Used for generating random salts or passwords. */
+ public byte[] randomBytes(int bits) {
+ byte[] array = new byte[bits / 8];
+ mRng.nextBytes(array);
+ return array;
+ }
+
+ /** For adb backup/restore. */
+ public boolean setBackupPassword(String currentPw, String newPw) {
+ return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
+ }
+
+ /** For adb backup/restore. */
+ public boolean hasBackupPassword() {
+ return mBackupPasswordManager.hasBackupPassword();
+ }
+
+ /** For adb backup/restore. */
+ public boolean backupPasswordMatches(String currentPw) {
+ return mBackupPasswordManager.backupPasswordMatches(currentPw);
+ }
+
+ /**
+ * Maintain persistent state around whether need to do an initialize operation. This will lock
+ * on {@link #getQueueLock()}.
+ */
+ public void recordInitPending(
+ boolean isPending, String transportName, String transportDirName) {
+ synchronized (mQueueLock) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
+ }
+
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+
+ if (isPending) {
+ // We need an init before we can proceed with sending backup data.
+ // Record that with an entry in our set of pending inits, as well as
+ // journaling it via creation of a sentinel file.
+ mPendingInits.add(transportName);
+ try {
+ (new FileOutputStream(initPendingFile)).close();
+ } catch (IOException ioe) {
+ // Something is badly wrong with our permissions; just try to move on
+ }
+ } else {
+ // No more initialization needed; wipe the journal and reset our state.
+ initPendingFile.delete();
+ mPendingInits.remove(transportName);
+ }
+ }
+ }
+
+ /**
+ * Reset all of our bookkeeping because the backend data has been wiped (for example due to idle
+ * expiry), so we must re-upload all saved settings.
+ */
+ public void resetBackupState(File stateFileDir) {
+ synchronized (mQueueLock) {
+ mProcessedPackagesJournal.reset();
+
+ mCurrentToken = 0;
+ writeRestoreTokens();
+
+ // Remove all the state files
+ for (File sf : stateFileDir.listFiles()) {
+ // ... but don't touch the needs-init sentinel
+ if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
+ sf.delete();
+ }
+ }
+ }
+
+ // Enqueue a new backup of every participant
+ synchronized (mBackupParticipants) {
+ final int numParticipants = mBackupParticipants.size();
+ for (int i = 0; i < numParticipants; i++) {
+ HashSet<String> participants = mBackupParticipants.valueAt(i);
+ if (participants != null) {
+ for (String packageName : participants) {
+ dataChangedImpl(packageName);
+ }
+ }
+ }
+ }
+ }
+
+ private void onTransportRegistered(String transportName, String transportDirName) {
+ if (DEBUG) {
+ long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
+ Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
+ + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
+ }
+
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ stateDir.mkdirs();
+
+ File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+ if (initSentinel.exists()) {
+ synchronized (mQueueLock) {
+ mPendingInits.add(transportName);
+
+ // TODO: pick a better starting time than now + 1 minute
+ long delay = 1000 * 60; // one minute, in milliseconds
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + delay, mRunInitIntent);
+ }
+ }
+ }
+
+ // ----- Track installation/removal of packages -----
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
+
+ String action = intent.getAction();
+ boolean replacing = false;
+ boolean added = false;
+ boolean changed = false;
+ Bundle extras = intent.getExtras();
+ String[] pkgList = null;
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ final String pkgName = uri.getSchemeSpecificPart();
+ if (pkgName != null) {
+ pkgList = new String[]{pkgName};
+ }
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+
+ // At package-changed we only care about looking at new transport states
+ if (changed) {
+ final String[] components =
+ intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+ for (int i = 0; i < components.length; i++) {
+ Slog.i(TAG, " * " + components[i]);
+ }
+ }
+
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageChanged(pkgName, components));
+ return; // nothing more to do in the PACKAGE_CHANGED case
+ }
+
+ added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ added = true;
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ added = false;
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ }
+
+ if (pkgList == null || pkgList.length == 0) {
+ return;
+ }
+
+ final int uid = extras.getInt(Intent.EXTRA_UID);
+ if (added) {
+ synchronized (mBackupParticipants) {
+ if (replacing) {
+ // This is the package-replaced case; we just remove the entry
+ // under the old uid and fall through to re-add. If an app
+ // just added key/value backup participation, this picks it up
+ // as a known participant.
+ removePackageParticipantsLocked(pkgList, uid);
+ }
+ addPackageParticipantsLocked(pkgList);
+ }
+ // If they're full-backup candidates, add them there instead
+ final long now = System.currentTimeMillis();
+ for (final String packageName : pkgList) {
+ try {
+ PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
+ if (AppBackupUtils.appGetsFullBackup(app)
+ && AppBackupUtils.appIsEligibleForBackup(
+ app.applicationInfo, mPackageManager)) {
+ enqueueFullBackup(packageName, now);
+ scheduleNextFullBackupJob(0);
+ } else {
+ // The app might have just transitioned out of full-data into
+ // doing key/value backups, or might have just disabled backups
+ // entirely. Make sure it is no longer in the full-data queue.
+ synchronized (mQueueLock) {
+ dequeueFullBackupLocked(packageName);
+ }
+ writeFullBackupScheduleAsync();
+ }
+
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageAdded(packageName));
+
+ } catch (NameNotFoundException e) {
+ // doesn't really exist; ignore it
+ if (DEBUG) {
+ Slog.w(TAG, "Can't resolve new app " + packageName);
+ }
+ }
+ }
+
+ // Whenever a package is added or updated we need to update
+ // the package metadata bookkeeping.
+ dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
+ } else {
+ if (replacing) {
+ // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
+ } else {
+ // Outright removal. In the full-data case, the app will be dropped
+ // from the queue when its (now obsolete) name comes up again for
+ // backup.
+ synchronized (mBackupParticipants) {
+ removePackageParticipantsLocked(pkgList, uid);
+ }
+ }
+ for (final String pkgName : pkgList) {
+ mBackupHandler.post(
+ () -> mTransportManager.onPackageRemoved(pkgName));
+ }
+ }
+ }
+ };
+
+ // Add the backup agents in the given packages to our set of known backup participants.
+ // If 'packageNames' is null, adds all backup agents in the whole system.
+ private void addPackageParticipantsLocked(String[] packageNames) {
+ // Look for apps that define the android:backupAgent attribute
+ List<PackageInfo> targetApps = allAgentPackages();
+ if (packageNames != null) {
+ if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
+ for (String packageName : packageNames) {
+ addPackageParticipantsLockedInner(packageName, targetApps);
+ }
+ } else {
+ if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
+ addPackageParticipantsLockedInner(null, targetApps);
+ }
+ }
+
+ private void addPackageParticipantsLockedInner(String packageName,
+ List<PackageInfo> targetPkgs) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Examining " + packageName + " for backup agent");
+ }
+
+ for (PackageInfo pkg : targetPkgs) {
+ if (packageName == null || pkg.packageName.equals(packageName)) {
+ int uid = pkg.applicationInfo.uid;
+ HashSet<String> set = mBackupParticipants.get(uid);
+ if (set == null) {
+ set = new HashSet<>();
+ mBackupParticipants.put(uid, set);
+ }
+ set.add(pkg.packageName);
+ if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
+
+ // Schedule a backup for it on general principles
+ if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
+ Message msg = mBackupHandler
+ .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
+ mBackupHandler.sendMessage(msg);
+ }
+ }
+ }
+
+ // Remove the given packages' entries from our known active set.
+ private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
+ if (packageNames == null) {
+ Slog.w(TAG, "removePackageParticipants with null list");
+ return;
+ }
+
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
+ + " #" + packageNames.length);
+ }
+ for (String pkg : packageNames) {
+ // Known previous UID, so we know which package set to check
+ HashSet<String> set = mBackupParticipants.get(oldUid);
+ if (set != null && set.contains(pkg)) {
+ removePackageFromSetLocked(set, pkg);
+ if (set.isEmpty()) {
+ if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set");
+ mBackupParticipants.remove(oldUid);
+ }
+ }
+ }
+ }
+
+ private void removePackageFromSetLocked(final HashSet<String> set,
+ final String packageName) {
+ if (set.contains(packageName)) {
+ // Found it. Remove this one package from the bookkeeping, and
+ // if it's the last participating app under this uid we drop the
+ // (now-empty) set as well.
+ // Note that we deliberately leave it 'known' in the "ever backed up"
+ // bookkeeping so that its current-dataset data will be retrieved
+ // if the app is subsequently reinstalled
+ if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
+ set.remove(packageName);
+ mPendingBackups.remove(packageName);
+ }
+ }
+
+ // Returns the set of all applications that define an android:backupAgent attribute
+ private List<PackageInfo> allAgentPackages() {
+ // !!! TODO: cache this and regenerate only when necessary
+ int flags = PackageManager.GET_SIGNING_CERTIFICATES;
+ List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+ int numPackages = packages.size();
+ for (int a = numPackages - 1; a >= 0; a--) {
+ PackageInfo pkg = packages.get(a);
+ try {
+ ApplicationInfo app = pkg.applicationInfo;
+ if (((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+ || app.backupAgentName == null
+ || (app.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
+ packages.remove(a);
+ } else {
+ // we will need the shared library path, so look that up and store it here.
+ // This is used implicitly when we pass the PackageInfo object off to
+ // the Activity Manager to launch the app for backup/restore purposes.
+ app = mPackageManager.getApplicationInfo(pkg.packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES);
+ pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
+ pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
+ }
+ } catch (NameNotFoundException e) {
+ packages.remove(a);
+ }
+ }
+ return packages;
+ }
+
+ /**
+ * Called from the backup tasks: record that the given app has been successfully backed up at
+ * least once. This includes both key/value and full-data backups through the transport.
+ */
+ public void logBackupComplete(String packageName) {
+ if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
+
+ for (String receiver : mConstants.getBackupFinishedNotificationReceivers()) {
+ final Intent notification = new Intent();
+ notification.setAction(BACKUP_FINISHED_ACTION);
+ notification.setPackage(receiver);
+ notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
+ mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
+ }
+
+ mProcessedPackagesJournal.addPackage(packageName);
+ }
+
+ /**
+ * Persistently record the current and ancestral backup tokens, as well as the set of packages
+ * with data available in the ancestral dataset.
+ */
+ public void writeRestoreTokens() {
+ try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
+ // First, the version number of this record, for futureproofing
+ af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
+
+ // Write the ancestral and current tokens
+ af.writeLong(mAncestralToken);
+ af.writeLong(mCurrentToken);
+
+ // Now write the set of ancestral packages
+ if (mAncestralPackages == null) {
+ af.writeInt(-1);
+ } else {
+ af.writeInt(mAncestralPackages.size());
+ if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
+ for (String pkgName : mAncestralPackages) {
+ af.writeUTF(pkgName);
+ if (MORE_DEBUG) Slog.v(TAG, " " + pkgName);
+ }
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to write token file:", e);
+ }
+ }
+
+ /** Fires off a backup agent, blocking until it attaches or times out. */
+ @Nullable
+ public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
+ IBackupAgent agent = null;
+ synchronized (mAgentConnectLock) {
+ mConnecting = true;
+ mConnectedAgent = null;
+ try {
+ if (mActivityManager.bindBackupAgent(app.packageName, mode,
+ UserHandle.USER_OWNER)) {
+ Slog.d(TAG, "awaiting agent for " + app);
+
+ // success; wait for the agent to arrive
+ // only wait 10 seconds for the bind to happen
+ long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+ while (mConnecting && mConnectedAgent == null
+ && (System.currentTimeMillis() < timeoutMark)) {
+ try {
+ mAgentConnectLock.wait(5000);
+ } catch (InterruptedException e) {
+ // just bail
+ Slog.w(TAG, "Interrupted: " + e);
+ mConnecting = false;
+ mConnectedAgent = null;
+ }
+ }
+
+ // if we timed out with no connect, abort and move on
+ if (mConnecting) {
+ Slog.w(TAG, "Timeout waiting for agent " + app);
+ mConnectedAgent = null;
+ }
+ if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
+ agent = mConnectedAgent;
+ }
+ } catch (RemoteException e) {
+ // can't happen - ActivityManager is local
+ }
+ }
+ if (agent == null) {
+ try {
+ mActivityManager.clearPendingBackup();
+ } catch (RemoteException e) {
+ // can't happen - ActivityManager is local
+ }
+ }
+ return agent;
+ }
+
+ /** Unbind from a backup agent. */
+ public void unbindAgent(ApplicationInfo app) {
+ try {
+ mActivityManager.unbindBackupAgent(app);
+ } catch (RemoteException e) {
+ // Can't happen - activity manager is local
+ }
+ }
+
+ /**
+ * Clear an application's data, blocking until the operation completes or times out. If {@code
+ * keepSystemState} is {@code true}, we intentionally do not clear system state that would
+ * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
+ * bringing it into the actual expected state related to the already-restored notification state
+ * etc.
+ */
+ public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
+ // Don't wipe packages marked allowClearUserData=false
+ try {
+ PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "allowClearUserData=false so not wiping "
+ + packageName);
+ }
+ return;
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
+ return;
+ }
+
+ ClearDataObserver observer = new ClearDataObserver(this);
+
+ synchronized (mClearDataLock) {
+ mClearingData = true;
+ try {
+ mActivityManager.clearApplicationUserData(
+ packageName, keepSystemState, observer, 0);
+ } catch (RemoteException e) {
+ // can't happen because the activity manager is in this process
+ }
+
+ // only wait 10 seconds for the clear data to happen
+ long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+ while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
+ try {
+ mClearDataLock.wait(5000);
+ } catch (InterruptedException e) {
+ // won't happen, but still.
+ mClearingData = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the restore-set token for the best-available restore set for this {@code packageName}:
+ * the active set if possible, else the ancestral one. Returns zero if none available.
+ */
+ public long getAvailableRestoreToken(String packageName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getAvailableRestoreToken");
+
+ long token = mAncestralToken;
+ synchronized (mQueueLock) {
+ if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "App in ever-stored, so using current token");
+ }
+ token = mCurrentToken;
+ }
+ }
+ if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
+ return token;
+ }
+
+ /**
+ * Requests a backup for the inputted {@code packages}.
+ *
+ * @see #requestBackup(String[], IBackupObserver, IBackupManagerMonitor, int).
+ */
+ public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
+ return requestBackup(packages, observer, null, flags);
+ }
+
+ /**
+ * Requests a backup for the inputted {@code packages} with a specified {@link
+ * IBackupManagerMonitor}.
+ */
+ public int requestBackup(String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
+
+ if (packages == null || packages.length < 1) {
+ Slog.e(TAG, "No packages named for backup request");
+ BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+ monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+ BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
+ null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+ throw new IllegalArgumentException("No packages are provided for backup");
+ }
+
+ if (!mEnabled || !mProvisioned) {
+ Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
+ BackupObserverUtils.sendBackupFinished(observer,
+ BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ final int logTag = mProvisioned
+ ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
+ : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
+ monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+ return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
+ }
+
+ final TransportClient transportClient;
+ final String transportDirName;
+ try {
+ transportDirName =
+ mTransportManager.getTransportDirName(
+ mTransportManager.getCurrentTransportName());
+ transportClient =
+ mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
+ } catch (TransportNotRegisteredException e) {
+ BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+ monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
+ null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+ return BackupManager.ERROR_TRANSPORT_ABORTED;
+ }
+
+ OnTaskFinishedListener listener =
+ caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
+
+ ArrayList<String> fullBackupList = new ArrayList<>();
+ ArrayList<String> kvBackupList = new ArrayList<>();
+ for (String packageName : packages) {
+ if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+ kvBackupList.add(packageName);
+ continue;
+ }
+ try {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
+ if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
+ mPackageManager)) {
+ BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
+ BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ continue;
+ }
+ if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+ fullBackupList.add(packageInfo.packageName);
+ } else {
+ kvBackupList.add(packageInfo.packageName);
+ }
+ } catch (NameNotFoundException e) {
+ BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
+ BackupManager.ERROR_PACKAGE_NOT_FOUND);
+ }
+ }
+ EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
+ fullBackupList.size());
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
+ + fullBackupList.size() + " full backups, " + kvBackupList.size()
+ + " k/v backups");
+ }
+
+ boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
+
+ Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
+ msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
+ observer, monitor, listener, true, nonIncrementalBackup);
+ mBackupHandler.sendMessage(msg);
+ return BackupManager.SUCCESS;
+ }
+
+ /** Cancel all running backups. */
+ public void cancelBackups() {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "cancelBackups() called.");
+ }
+ final long oldToken = Binder.clearCallingIdentity();
+ try {
+ List<Integer> operationsToCancel = new ArrayList<>();
+ synchronized (mCurrentOpLock) {
+ for (int i = 0; i < mCurrentOperations.size(); i++) {
+ Operation op = mCurrentOperations.valueAt(i);
+ int token = mCurrentOperations.keyAt(i);
+ if (op.type == OP_TYPE_BACKUP) {
+ operationsToCancel.add(token);
+ }
+ }
+ }
+ for (Integer token : operationsToCancel) {
+ handleCancel(token, true /* cancelAll */);
+ }
+ // We don't want the backup jobs to kick in any time soon.
+ // Reschedules them to run in the distant future.
+ KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+ FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
+ }
+ }
+
+ /** Schedule a timeout message for the operation identified by {@code token}. */
+ public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
+ int operationType) {
+ if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
+ Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
+ + Integer.toHexString(token) + " of type " + operationType);
+ return;
+ }
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+ + " interval=" + interval + " callback=" + callback);
+ }
+
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
+ Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
+ token, 0, callback);
+ mBackupHandler.sendMessageDelayed(msg, interval);
+ }
+ }
+
+ private int getMessageIdForOperationType(int operationType) {
+ switch (operationType) {
+ case OP_TYPE_BACKUP_WAIT:
+ return MSG_BACKUP_OPERATION_TIMEOUT;
+ case OP_TYPE_RESTORE_WAIT:
+ return MSG_RESTORE_OPERATION_TIMEOUT;
+ default:
+ Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
+ + operationType);
+ return -1;
+ }
+ }
+
+ /**
+ * Add an operation to the list of currently running operations. Used for cancellation,
+ * completion and timeout callbacks that act on the operation via the {@code token}.
+ */
+ public void putOperation(int token, Operation operation) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
+ + operation.type);
+ }
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.put(token, operation);
+ }
+ }
+
+ /**
+ * Remove an operation from the list of currently running operations. An operation is removed
+ * when it is completed, cancelled, or timed out, and thus no longer running.
+ */
+ public void removeOperation(int token) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
+ }
+ synchronized (mCurrentOpLock) {
+ if (mCurrentOperations.get(token) == null) {
+ Slog.w(TAG, "Duplicate remove for operation. token="
+ + Integer.toHexString(token));
+ }
+ mCurrentOperations.remove(token);
+ }
+ }
+
+ /** Block until we received an operation complete message (from the agent or cancellation). */
+ public boolean waitUntilOperationComplete(int token) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Blocking until operation complete for "
+ + Integer.toHexString(token));
+ }
+ int finalState = OP_PENDING;
+ Operation op = null;
+ synchronized (mCurrentOpLock) {
+ while (true) {
+ op = mCurrentOperations.get(token);
+ if (op == null) {
+ // mysterious disappearance: treat as success with no callback
+ break;
+ } else {
+ if (op.state == OP_PENDING) {
+ try {
+ mCurrentOpLock.wait();
+ } catch (InterruptedException e) {
+ }
+ // When the wait is notified we loop around and recheck the current state
+ } else {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Unblocked waiting for operation token="
+ + Integer.toHexString(token));
+ }
+ // No longer pending; we're done
+ finalState = op.state;
+ break;
+ }
+ }
+ }
+ }
+
+ removeOperation(token);
+ if (op != null) {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+ }
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "operation " + Integer.toHexString(token)
+ + " complete: finalState=" + finalState);
+ }
+ return finalState == OP_ACKNOWLEDGED;
+ }
+
+ /** Cancel the operation associated with {@code token}. */
+ public void handleCancel(int token, boolean cancelAll) {
+ // Notify any synchronous waiters
+ Operation op = null;
+ synchronized (mCurrentOpLock) {
+ op = mCurrentOperations.get(token);
+ if (MORE_DEBUG) {
+ if (op == null) {
+ Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
+ + " but no op found");
+ }
+ }
+ int state = (op != null) ? op.state : OP_TIMEOUT;
+ if (state == OP_ACKNOWLEDGED) {
+ // The operation finished cleanly, so we have nothing more to do.
+ if (DEBUG) {
+ Slog.w(TAG, "Operation already got an ack."
+ + "Should have been removed from mCurrentOperations.");
+ }
+ op = null;
+ mCurrentOperations.delete(token);
+ } else if (state == OP_PENDING) {
+ if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
+ op.state = OP_TIMEOUT;
+ // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
+ // called after we receive cancel here. We need this op's state there.
+
+ // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
+ // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
+ // doesn't require cancellation.
+ if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+ }
+ }
+ mCurrentOpLock.notifyAll();
+ }
+
+ // If there's a TimeoutHandler for this event, call it
+ if (op != null && op.callback != null) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, " Invoking cancel on " + op.callback);
+ }
+ op.callback.handleCancel(cancelAll);
+ }
+ }
+
+ /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
+ public boolean isBackupOperationInProgress() {
+ synchronized (mCurrentOpLock) {
+ for (int i = 0; i < mCurrentOperations.size(); i++) {
+ Operation op = mCurrentOperations.valueAt(i);
+ if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** Unbind the backup agent and kill the app if it's a non-system app. */
+ public void tearDownAgentAndKill(ApplicationInfo app) {
+ if (app == null) {
+ // Null means the system package, so just quietly move on. :)
+ return;
+ }
+
+ try {
+ // unbind and tidy up even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(app);
+
+ // The agent was running with a stub Application object, so shut it down.
+ // !!! We hardcode the confirmation UI's package name here rather than use a
+ // manifest flag! TODO something less direct.
+ if (app.uid >= Process.FIRST_APPLICATION_UID
+ && !app.packageName.equals("com.android.backupconfirm")) {
+ if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ } else {
+ if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
+ }
+
+ /** For adb backup/restore. */
+ public boolean deviceIsEncrypted() {
+ try {
+ return mStorageManager.getEncryptionState()
+ != StorageManager.ENCRYPTION_STATE_NONE
+ && mStorageManager.getPasswordType()
+ != StorageManager.CRYPT_TYPE_DEFAULT;
+ } catch (Exception e) {
+ // If we can't talk to the storagemanager service we have a serious problem; fail
+ // "secure" i.e. assuming that the device is encrypted.
+ Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
+ return true;
+ }
+ }
+
+ // ----- Full-data backup scheduling -----
+
+ /**
+ * Schedule a job to tell us when it's a good time to run a full backup
+ */
+ public void scheduleNextFullBackupJob(long transportMinLatency) {
+ synchronized (mQueueLock) {
+ if (mFullBackupQueue.size() > 0) {
+ // schedule the next job at the point in the future when the least-recently
+ // backed up app comes due for backup again; or immediately if it's already
+ // due.
+ final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
+ final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
+ final long interval = mConstants.getFullBackupIntervalMilliseconds();
+ final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
+ final long latency = Math.max(transportMinLatency, appLatency);
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ FullBackupJob.schedule(mContext, latency, mConstants);
+ }
+ };
+ mBackupHandler.postDelayed(r, 2500);
+ } else {
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Full backup queue empty; not scheduling");
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove a package from the full-data queue.
+ */
+ @GuardedBy("mQueueLock")
+ private void dequeueFullBackupLocked(String packageName) {
+ final int numPackages = mFullBackupQueue.size();
+ for (int i = numPackages - 1; i >= 0; i--) {
+ final FullBackupEntry e = mFullBackupQueue.get(i);
+ if (packageName.equals(e.packageName)) {
+ mFullBackupQueue.remove(i);
+ }
+ }
+ }
+
+ /**
+ * Enqueue full backup for the given app, with a note about when it last ran.
+ */
+ public void enqueueFullBackup(String packageName, long lastBackedUp) {
+ FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
+ synchronized (mQueueLock) {
+ // First, sanity check that we aren't adding a duplicate. Slow but
+ // straightforward; we'll have at most on the order of a few hundred
+ // items in this list.
+ dequeueFullBackupLocked(packageName);
+
+ // This is also slow but easy for modest numbers of apps: work backwards
+ // from the end of the queue until we find an item whose last backup
+ // time was before this one, then insert this new entry after it. If we're
+ // adding something new we don't bother scanning, and just prepend.
+ int which = -1;
+ if (lastBackedUp > 0) {
+ for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
+ final FullBackupEntry entry = mFullBackupQueue.get(which);
+ if (entry.lastBackup <= lastBackedUp) {
+ mFullBackupQueue.add(which + 1, newEntry);
+ break;
+ }
+ }
+ }
+ if (which < 0) {
+ // this one is earlier than any existing one, so prepend
+ mFullBackupQueue.add(0, newEntry);
+ }
+ }
+ writeFullBackupScheduleAsync();
+ }
+
+ private boolean fullBackupAllowable(String transportName) {
+ if (!mTransportManager.isTransportRegistered(transportName)) {
+ Slog.w(TAG, "Transport not registered; full data backup not performed");
+ return false;
+ }
+
+ // Don't proceed unless we have already established package metadata
+ // for the current dataset via a key/value backup pass.
+ try {
+ String transportDirName = mTransportManager.getTransportDirName(transportName);
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
+ if (pmState.length() <= 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Full backup requested but dataset not yet initialized");
+ }
+ return false;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Conditions are right for a full backup operation, so run one. The model we use is
+ * to perform one app backup per scheduled job execution, and to reschedule the job
+ * with zero latency as long as conditions remain right and we still have work to do.
+ *
+ * <p>This is the "start a full backup operation" entry point called by the scheduled job.
+ *
+ * @return Whether ongoing work will continue. The return value here will be passed
+ * along as the return value to the scheduled job's onStartJob() callback.
+ */
+ public boolean beginFullBackup(FullBackupJob scheduledJob) {
+ final long now = System.currentTimeMillis();
+ final long fullBackupInterval;
+ final long keyValueBackupInterval;
+ synchronized (mConstants) {
+ fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
+ keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
+ }
+ FullBackupEntry entry = null;
+ long latency = fullBackupInterval;
+
+ if (!mEnabled || !mProvisioned) {
+ // Backups are globally disabled, so don't proceed. We also don't reschedule
+ // the job driving automatic backups; that job will be scheduled again when
+ // the user enables backup.
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+ + " p=" + mProvisioned + "; ignoring");
+ }
+ return false;
+ }
+
+ // Don't run the backup if we're in battery saver mode, but reschedule
+ // to try again in the not-so-distant future.
+ final PowerSaveState result =
+ mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
+ if (result.batterySaverEnabled) {
+ if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
+ FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+ return false;
+ }
+
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Beginning scheduled full backup operation");
+ }
+
+ // Great; we're able to run full backup jobs now. See if we have any work to do.
+ synchronized (mQueueLock) {
+ if (mRunningFullBackupTask != null) {
+ Slog.e(TAG, "Backup triggered but one already/still running!");
+ return false;
+ }
+
+ // At this point we think that we have work to do, but possibly not right now.
+ // Any exit without actually running backups will also require that we
+ // reschedule the job.
+ boolean runBackup = true;
+ boolean headBusy;
+
+ do {
+ // Recheck each time, because culling due to ineligibility may
+ // have emptied the queue.
+ if (mFullBackupQueue.size() == 0) {
+ // no work to do so just bow out
+ if (DEBUG) {
+ Slog.i(TAG, "Backup queue empty; doing nothing");
+ }
+ runBackup = false;
+ break;
+ }
+
+ headBusy = false;
+
+ String transportName = mTransportManager.getCurrentTransportName();
+ if (!fullBackupAllowable(transportName)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Preconditions not met; not running full backup");
+ }
+ runBackup = false;
+ // Typically this means we haven't run a key/value backup yet. Back off
+ // full-backup operations by the key/value job's run interval so that
+ // next time we run, we are likely to be able to make progress.
+ latency = keyValueBackupInterval;
+ }
+
+ if (runBackup) {
+ entry = mFullBackupQueue.get(0);
+ long timeSinceRun = now - entry.lastBackup;
+ runBackup = (timeSinceRun >= fullBackupInterval);
+ if (!runBackup) {
+ // It's too early to back up the next thing in the queue, so bow out
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Device ready but too early to back up next app");
+ }
+ // Wait until the next app in the queue falls due for a full data backup
+ latency = fullBackupInterval - timeSinceRun;
+ break; // we know we aren't doing work yet, so bail.
+ }
+
+ try {
+ PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+ if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
+ // The head app isn't supposed to get full-data backups [any more];
+ // so we cull it and force a loop around to consider the new head
+ // app.
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Culling package " + entry.packageName
+ + " in full-backup queue but not eligible");
+ }
+ mFullBackupQueue.remove(0);
+ headBusy = true; // force the while() condition
+ continue;
+ }
+
+ final int privFlags = appInfo.applicationInfo.privateFlags;
+ headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
+ && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+
+ if (headBusy) {
+ final long nextEligible = System.currentTimeMillis()
+ + BUSY_BACKOFF_MIN_MILLIS
+ + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
+ if (DEBUG_SCHEDULING) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Slog.i(TAG, "Full backup time but " + entry.packageName
+ + " is busy; deferring to "
+ + sdf.format(new Date(nextEligible)));
+ }
+ // This relocates the app's entry from the head of the queue to
+ // its order-appropriate position further down, so upon looping
+ // a new candidate will be considered at the head.
+ enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
+ }
+ } catch (NameNotFoundException nnf) {
+ // So, we think we want to back this up, but it turns out the package
+ // in question is no longer installed. We want to drop it from the
+ // queue entirely and move on, but if there's nothing else in the queue
+ // we should bail entirely. headBusy cannot have been set to true yet.
+ runBackup = (mFullBackupQueue.size() > 1);
+ } catch (RemoteException e) {
+ // Cannot happen; the Activity Manager is in the same process
+ }
+ }
+ } while (headBusy);
+
+ if (!runBackup) {
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
+ }
+ final long deferTime = latency; // pin for the closure
+ mBackupHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ FullBackupJob.schedule(mContext, deferTime, mConstants);
+ }
+ });
+ return false;
+ }
+
+ // Okay, the top thing is ready for backup now. Do it.
+ mFullBackupQueue.remove(0);
+ CountDownLatch latch = new CountDownLatch(1);
+ String[] pkg = new String[]{entry.packageName};
+ mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
+ this,
+ /* observer */ null,
+ pkg,
+ /* updateSchedule */ true,
+ scheduledJob,
+ latch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ "BMS.beginFullBackup()");
+ // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+ mWakelock.acquire();
+ (new Thread(mRunningFullBackupTask)).start();
+ }
+
+ return true;
+ }
+
+ /**
+ * The job scheduler says our constraints don't hold anymore, so tear down any ongoing backup
+ * task right away.
+ */
+ public void endFullBackup() {
+ // offload the mRunningFullBackupTask.handleCancel() call to another thread,
+ // as we might have to wait for mCancelLock
+ Runnable endFullBackupRunnable = new Runnable() {
+ @Override
+ public void run() {
+ PerformFullTransportBackupTask pftbt = null;
+ synchronized (mQueueLock) {
+ if (mRunningFullBackupTask != null) {
+ pftbt = mRunningFullBackupTask;
+ }
+ }
+ if (pftbt != null) {
+ if (DEBUG_SCHEDULING) {
+ Slog.i(TAG, "Telling running backup to stop");
+ }
+ pftbt.handleCancel(true);
+ }
+ }
+ };
+ new Thread(endFullBackupRunnable, "end-full-backup").start();
+ }
+
+ /** Used by both incremental and full restore to restore widget data. */
+ public void restoreWidgetData(String packageName, byte[] widgetData) {
+ // Apply the restored widget state and generate the ID update for the app
+ // TODO: http://b/22388012
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Incorporating restored widget data");
+ }
+ AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
+ }
+
+ // *****************************
+ // NEW UNIFIED RESTORE IMPLEMENTATION
+ // *****************************
+
+ /** Schedule a backup pass for {@code packageName}. */
+ public void dataChangedImpl(String packageName) {
+ HashSet<String> targets = dataChangedTargets(packageName);
+ dataChangedImpl(packageName, targets);
+ }
+
+ private void dataChangedImpl(String packageName, HashSet<String> targets) {
+ // Record that we need a backup pass for the caller. Since multiple callers
+ // may share a uid, we need to note all candidates within that uid and schedule
+ // a backup pass for each of them.
+ if (targets == null) {
+ Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mQueueLock) {
+ // Note that this client has made data changes that need to be backed up
+ if (targets.contains(packageName)) {
+ // Add the caller to the set of pending backups. If there is
+ // one already there, then overwrite it, but no harm done.
+ BackupRequest req = new BackupRequest(packageName);
+ if (mPendingBackups.put(packageName, req) == null) {
+ if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
+
+ // Journal this request in case of crash. The put()
+ // operation returned null when this package was not already
+ // in the set; we want to avoid touching the disk redundantly.
+ writeToJournalLocked(packageName);
+ }
+ }
+ }
+
+ // ...and schedule a backup pass if necessary
+ KeyValueBackupJob.schedule(mContext, mConstants);
+ }
+
+ // Note: packageName is currently unused, but may be in the future
+ private HashSet<String> dataChangedTargets(String packageName) {
+ // If the caller does not hold the BACKUP permission, it can only request a
+ // backup of its own data.
+ if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+ Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+ synchronized (mBackupParticipants) {
+ return mBackupParticipants.get(Binder.getCallingUid());
+ }
+ }
+
+ // a caller with full permission can ask to back up any participating app
+ if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+ return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
+ } else {
+ synchronized (mBackupParticipants) {
+ return SparseArrayUtils.union(mBackupParticipants);
+ }
+ }
+ }
+
+ private void writeToJournalLocked(String str) {
+ try {
+ if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
+ mJournal.addPackage(str);
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't write " + str + " to backup journal", e);
+ mJournal = null;
+ }
+ }
+
+ // ----- IBackupManager binder interface -----
+
+ /** Sent from an app's backup agent to let the service know that there's new data to backup. */
+ public void dataChanged(final String packageName) {
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ // TODO: http://b/22388012
+ // App is running under a non-owner user profile. For now, we do not back
+ // up data from secondary user profiles.
+ // TODO: backups for all user profiles although don't add backup for profiles
+ // without adding admin control in DevicePolicyManager.
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
+ + callingUserHandle);
+ }
+ return;
+ }
+
+ final HashSet<String> targets = dataChangedTargets(packageName);
+ if (targets == null) {
+ Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ mBackupHandler.post(new Runnable() {
+ public void run() {
+ dataChangedImpl(packageName, targets);
+ }
+ });
+ }
+
+ /** Run an initialize operation for the given transport. */
+ public void initializeTransports(String[] transportNames, IBackupObserver observer) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "initializeTransport");
+ Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ mWakelock.acquire();
+ OnTaskFinishedListener listener = caller -> mWakelock.release();
+ mBackupHandler.post(
+ new PerformInitializeTask(this, transportNames, observer, listener));
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Clear the given package's backup data from the current transport. */
+ public void clearBackupData(String transportName, String packageName) {
+ if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
+ PackageInfo info;
+ try {
+ info = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
+ } catch (NameNotFoundException e) {
+ Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
+ return;
+ }
+
+ // If the caller does not hold the BACKUP permission, it can only request a
+ // wipe of its own backed-up data.
+ Set<String> apps;
+ if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+ Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+ apps = mBackupParticipants.get(Binder.getCallingUid());
+ } else {
+ // a caller with full permission can ask to back up any participating app
+ // !!! TODO: allow data-clear of ANY app?
+ if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
+ apps = mProcessedPackagesJournal.getPackagesCopy();
+ }
+
+ if (apps.contains(packageName)) {
+ // found it; fire off the clear request
+ if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
+ mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
+ synchronized (mQueueLock) {
+ TransportClient transportClient =
+ mTransportManager
+ .getTransportClient(transportName, "BMS.clearBackupData()");
+ if (transportClient == null) {
+ // transport is currently unregistered -- make sure to retry
+ Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
+ new ClearRetryParams(transportName, packageName));
+ mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
+ return;
+ }
+ long oldId = Binder.clearCallingIdentity();
+ OnTaskFinishedListener listener =
+ caller ->
+ mTransportManager.disposeOfTransportClient(transportClient, caller);
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(
+ MSG_RUN_CLEAR,
+ new ClearParams(transportClient, info, listener));
+ mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+ }
+
+ /**
+ * Run a backup pass immediately for any applications that have declared that they have pending
+ * updates.
+ */
+ public void backupNow() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ final PowerSaveState result =
+ mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
+ if (result.batterySaverEnabled) {
+ if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
+ KeyValueBackupJob.schedule(mContext, mConstants); // try again in several hours
+ } else {
+ if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
+ synchronized (mQueueLock) {
+ // Fire the intent that kicks off the whole shebang...
+ try {
+ mRunBackupIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ // should never happen
+ Slog.e(TAG, "run-backup intent cancelled!");
+ }
+
+ // ...and cancel any pending scheduled job, because we've just superseded it
+ KeyValueBackupJob.cancel(mContext);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Returns {@code true} if the system user has gone through SUW. */
+ public boolean deviceIsProvisioned() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
+ }
+
+ /**
+ * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
+ * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
+ * return to the caller until the backup has been completed. It requires on-screen confirmation
+ * by the user.
+ */
+ public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
+ boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
+ boolean compress, boolean doKeyValue, String[] pkgList) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
+
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ throw new IllegalStateException("Backup supported only for the device owner");
+ }
+
+ // Validate
+ if (!doAllApps) {
+ if (!includeShared) {
+ // If we're backing up shared data (sdcard or equivalent), then we can run
+ // without any supplied app names. Otherwise, we'd be doing no work, so
+ // report the error.
+ if (pkgList == null || pkgList.length == 0) {
+ throw new IllegalArgumentException(
+ "Backup requested but neither shared nor any apps named");
+ }
+ }
+ }
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ // Doesn't make sense to do a full backup prior to setup
+ if (!deviceIsProvisioned()) {
+ Slog.i(TAG, "Backup not supported before setup");
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
+ + " shared=" + includeShared + " all=" + doAllApps + " system="
+ + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
+ }
+ Slog.i(TAG, "Beginning adb backup...");
+
+ AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
+ includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
+ pkgList);
+ final int token = generateRandomIntegerToken();
+ synchronized (mAdbBackupRestoreConfirmations) {
+ mAdbBackupRestoreConfirmations.put(token, params);
+ }
+
+ // start up the confirmation UI
+ if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
+ if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
+ Slog.e(TAG, "Unable to launch backup confirmation UI");
+ mAdbBackupRestoreConfirmations.delete(token);
+ return;
+ }
+
+ // make sure the screen is lit for the user interaction
+ mPowerManager.userActivity(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0);
+
+ // start the confirmation countdown
+ startConfirmationTimeout(token, params);
+
+ // wait for the backup to be performed
+ if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
+ waitForCompletion(params);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
+ }
+ Binder.restoreCallingIdentity(oldId);
+ Slog.d(TAG, "Adb backup processing complete.");
+ }
+ }
+
+ /** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
+ public void fullTransportBackup(String[] pkgNames) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "fullTransportBackup");
+
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ throw new IllegalStateException("Restore supported only for the device owner");
+ }
+
+ String transportName = mTransportManager.getCurrentTransportName();
+ if (!fullBackupAllowable(transportName)) {
+ Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "fullTransportBackup()");
+ }
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ CountDownLatch latch = new CountDownLatch(1);
+ Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
+ this,
+ /* observer */ null,
+ pkgNames,
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ latch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ "BMS.fullTransportBackup()");
+ // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+ mWakelock.acquire();
+ (new Thread(task, "full-transport-master")).start();
+ do {
+ try {
+ latch.await();
+ break;
+ } catch (InterruptedException e) {
+ // Just go back to waiting for the latch to indicate completion
+ }
+ } while (true);
+
+ // We just ran a backup on these packages, so kick them to the end of the queue
+ final long now = System.currentTimeMillis();
+ for (String pkg : pkgNames) {
+ enqueueFullBackup(pkg, now);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Done with full transport backup.");
+ }
+ }
+
+ /**
+ * Used by 'adb restore' to run a restore pass, blocking until completion. Requires user
+ * confirmation.
+ */
+ public void adbRestore(ParcelFileDescriptor fd) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
+
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ // TODO: http://b/22388012
+ if (callingUserHandle != UserHandle.USER_SYSTEM) {
+ throw new IllegalStateException("Restore supported only for the device owner");
+ }
+
+ long oldId = Binder.clearCallingIdentity();
+
+ try {
+ // Check whether the device has been provisioned -- we don't handle
+ // full restores prior to completing the setup process.
+ if (!deviceIsProvisioned()) {
+ Slog.i(TAG, "Full restore not permitted before setup");
+ return;
+ }
+
+ Slog.i(TAG, "Beginning restore...");
+
+ AdbRestoreParams params = new AdbRestoreParams(fd);
+ final int token = generateRandomIntegerToken();
+ synchronized (mAdbBackupRestoreConfirmations) {
+ mAdbBackupRestoreConfirmations.put(token, params);
+ }
+
+ // start up the confirmation UI
+ if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
+ if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
+ Slog.e(TAG, "Unable to launch restore confirmation");
+ mAdbBackupRestoreConfirmations.delete(token);
+ return;
+ }
+
+ // make sure the screen is lit for the user interaction
+ mPowerManager.userActivity(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0);
+
+ // start the confirmation countdown
+ startConfirmationTimeout(token, params);
+
+ // wait for the restore to be performed
+ if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
+ waitForCompletion(params);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
+ }
+ Binder.restoreCallingIdentity(oldId);
+ Slog.i(TAG, "adb restore processing complete.");
+ }
+ }
+
+ private boolean startConfirmationUi(int token, String action) {
+ try {
+ Intent confIntent = new Intent(action);
+ confIntent.setClassName("com.android.backupconfirm",
+ "com.android.backupconfirm.BackupRestoreConfirmation");
+ confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
+ confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
+ } catch (ActivityNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private void startConfirmationTimeout(int token, AdbParams params) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Posting conf timeout msg after "
+ + TIMEOUT_FULL_CONFIRMATION + " millis");
+ }
+ Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
+ token, 0, params);
+ mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
+ }
+
+ private void waitForCompletion(AdbParams params) {
+ synchronized (params.latch) {
+ while (!params.latch.get()) {
+ try {
+ params.latch.wait();
+ } catch (InterruptedException e) { /* never interrupted */ }
+ }
+ }
+ }
+
+ /** Called when adb backup/restore has completed. */
+ public void signalAdbBackupRestoreCompletion(AdbParams params) {
+ synchronized (params.latch) {
+ params.latch.set(true);
+ params.latch.notifyAll();
+ }
+ }
+
+ /**
+ * Confirm that the previously-requested full backup/restore operation can proceed. This is used
+ * to require a user-facing disclosure about the operation.
+ */
+ public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
+ String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
+ if (DEBUG) {
+ Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
+ + " allow=" + allow);
+ }
+
+ // TODO: possibly require not just this signature-only permission, but even
+ // require that the specific designated confirmation-UI app uid is the caller?
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "acknowledgeAdbBackupOrRestore");
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+
+ AdbParams params;
+ synchronized (mAdbBackupRestoreConfirmations) {
+ params = mAdbBackupRestoreConfirmations.get(token);
+ if (params != null) {
+ mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
+ mAdbBackupRestoreConfirmations.delete(token);
+
+ if (allow) {
+ final int verb = params instanceof AdbBackupParams
+ ? MSG_RUN_ADB_BACKUP
+ : MSG_RUN_ADB_RESTORE;
+
+ params.observer = observer;
+ params.curPassword = curPassword;
+
+ params.encryptPassword = encPpassword;
+
+ if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(verb, params);
+ mBackupHandler.sendMessage(msg);
+ } else {
+ Slog.w(TAG, "User rejected full backup/restore operation");
+ // indicate completion without having actually transferred any data
+ signalAdbBackupRestoreCompletion(params);
+ }
+ } else {
+ Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** User-configurable enabling/disabling of backups. */
+ public void setBackupEnabled(boolean enable) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "setBackupEnabled");
+
+ Slog.i(TAG, "Backup enabled => " + enable);
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ boolean wasEnabled = mEnabled;
+ synchronized (this) {
+ // TODO(b/118520567): Clean up writing backup enabled logic.
+ BackupManagerService.writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
+ mEnabled = enable;
+ }
+
+ synchronized (mQueueLock) {
+ if (enable && !wasEnabled && mProvisioned) {
+ // if we've just been enabled, start scheduling backup passes
+ KeyValueBackupJob.schedule(mContext, mConstants);
+ scheduleNextFullBackupJob(0);
+ } else if (!enable) {
+ // No longer enabled, so stop running backups
+ if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
+
+ KeyValueBackupJob.cancel(mContext);
+
+ // This also constitutes an opt-out, so we wipe any data for
+ // this device from the backend. We start that process with
+ // an alarm in order to guarantee wakelock states.
+ if (wasEnabled && mProvisioned) {
+ // NOTE: we currently flush every registered transport, not just
+ // the currently-active one.
+ List<String> transportNames = new ArrayList<>();
+ List<String> transportDirNames = new ArrayList<>();
+ mTransportManager.forEachRegisteredTransport(
+ name -> {
+ final String dirName;
+ try {
+ dirName =
+ mTransportManager
+ .getTransportDirName(name);
+ } catch (TransportNotRegisteredException e) {
+ // Should never happen
+ Slog.e(TAG, "Unexpected unregistered transport", e);
+ return;
+ }
+ transportNames.add(name);
+ transportDirNames.add(dirName);
+ });
+
+ // build the set of transports for which we are posting an init
+ for (int i = 0; i < transportNames.size(); i++) {
+ recordInitPending(
+ true,
+ transportNames.get(i),
+ transportDirNames.get(i));
+ }
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
+ mRunInitIntent);
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Enable/disable automatic restore of app data at install time. */
+ public void setAutoRestore(boolean doAutoRestore) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "setAutoRestore");
+
+ Slog.i(TAG, "Auto restore => " + doAutoRestore);
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+ mAutoRestore = doAutoRestore;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Mark the backup service as having been provisioned. */
+ public void setBackupProvisioned(boolean available) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "setBackupProvisioned");
+ /*
+ * This is now a no-op; provisioning is simply the device's own setup state.
+ */
+ }
+
+ /** Report whether the backup mechanism is currently enabled. */
+ public boolean isBackupEnabled() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "isBackupEnabled");
+ return mEnabled; // no need to synchronize just to read it
+ }
+
+ /** Report the name of the currently active transport. */
+ public String getCurrentTransport() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getCurrentTransport");
+ String currentTransport = mTransportManager.getCurrentTransportName();
+ if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
+ return currentTransport;
+ }
+
+ /**
+ * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+ * null} if no transport selected or if the transport selected is not registered.
+ */
+ @Nullable
+ public ComponentName getCurrentTransportComponent() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ return mTransportManager.getCurrentTransportComponent();
+ } catch (TransportNotRegisteredException e) {
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /** Report all known, available backup transports by name. */
+ public String[] listAllTransports() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "listAllTransports");
+
+ return mTransportManager.getRegisteredTransportNames();
+ }
+
+ /** Report all known, available backup transports by component. */
+ public ComponentName[] listAllTransportComponents() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "listAllTransportComponents");
+ return mTransportManager.getRegisteredTransportComponents();
+ }
+
+ /** Report all system whitelisted transports. */
+ public String[] getTransportWhitelist() {
+ // No permission check, intentionally.
+ Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
+ String[] whitelistedTransports = new String[whitelistedComponents.size()];
+ int i = 0;
+ for (ComponentName component : whitelistedComponents) {
+ whitelistedTransports[i] = component.flattenToShortString();
+ i++;
+ }
+ return whitelistedTransports;
+ }
+
+ /**
+ * Update the attributes of the transport identified by {@code transportComponent}. If the
+ * specified transport has not been bound at least once (for registration), this call will be
+ * ignored. Only the host process of the transport can change its description, otherwise a
+ * {@link SecurityException} will be thrown.
+ *
+ * @param transportComponent The identity of the transport being described.
+ * @param name A {@link String} with the new name for the transport. This is NOT for
+ * identification. MUST NOT be {@code null}.
+ * @param configurationIntent An {@link Intent} that can be passed to
+ * {@link Context#startActivity} in order to launch the transport's configuration UI. It may
+ * be {@code null} if the transport does not offer any user-facing configuration UI.
+ * @param currentDestinationString A {@link String} describing the destination to which the
+ * transport is currently sending data. MUST NOT be {@code null}.
+ * @param dataManagementIntent An {@link Intent} that can be passed to
+ * {@link Context#startActivity} in order to launch the transport's data-management UI. It
+ * may be {@code null} if the transport does not offer any user-facing data
+ * management UI.
+ * @param dataManagementLabel A {@link String} to be used as the label for the transport's data
+ * management affordance. This MUST be {@code null} when dataManagementIntent is
+ * {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+ * @throws SecurityException If the UID of the calling process differs from the package UID of
+ * {@code transportComponent} or if the caller does NOT have BACKUP permission.
+ */
+ public void updateTransportAttributes(
+ ComponentName transportComponent,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ @Nullable String dataManagementLabel) {
+ updateTransportAttributes(
+ Binder.getCallingUid(),
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+
+ @VisibleForTesting
+ void updateTransportAttributes(
+ int callingUid,
+ ComponentName transportComponent,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ @Nullable String dataManagementLabel) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "updateTransportAttributes");
+
+ Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
+ Preconditions.checkNotNull(name, "name can't be null");
+ Preconditions.checkNotNull(
+ currentDestinationString, "currentDestinationString can't be null");
+ Preconditions.checkArgument(
+ (dataManagementIntent == null) == (dataManagementLabel == null),
+ "dataManagementLabel should be null iff dataManagementIntent is null");
+
+ try {
+ int transportUid =
+ mContext.getPackageManager()
+ .getPackageUid(transportComponent.getPackageName(), 0);
+ if (callingUid != transportUid) {
+ throw new SecurityException("Only the transport can change its description");
+ }
+ } catch (NameNotFoundException e) {
+ throw new SecurityException("Transport package not found", e);
+ }
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ mTransportManager.updateTransportAttributes(
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /**
+ * Selects transport {@code transportName} and returns previously selected transport.
+ *
+ * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} instead.
+ */
+ @Deprecated
+ @Nullable
+ public String selectBackupTransport(String transportName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "selectBackupTransport");
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ String previousTransportName = mTransportManager.selectTransport(transportName);
+ updateStateForTransport(transportName);
+ Slog.v(TAG, "selectBackupTransport(transport = " + transportName
+ + "): previous transport = " + previousTransportName);
+ return previousTransportName;
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ /**
+ * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
+ * with the result upon completion.
+ */
+ public void selectBackupTransportAsync(
+ ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
+
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ String transportString = transportComponent.flattenToShortString();
+ Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
+ mBackupHandler.post(
+ () -> {
+ String transportName = null;
+ int result =
+ mTransportManager.registerAndSelectTransport(transportComponent);
+ if (result == BackupManager.SUCCESS) {
+ try {
+ transportName =
+ mTransportManager.getTransportName(transportComponent);
+ updateStateForTransport(transportName);
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Transport got unregistered");
+ result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
+ }
+ }
+
+ try {
+ if (transportName != null) {
+ listener.onSuccess(transportName);
+ } else {
+ listener.onFailure(result);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ private void updateStateForTransport(String newTransportName) {
+ // Publish the name change
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+
+ // And update our current-dataset bookkeeping
+ String callerLogString = "BMS.updateStateForTransport()";
+ TransportClient transportClient =
+ mTransportManager.getTransportClient(newTransportName, callerLogString);
+ if (transportClient != null) {
+ try {
+ IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
+ mCurrentToken = transport.getCurrentRestoreSet();
+ } catch (Exception e) {
+ // Oops. We can't know the current dataset token, so reset and figure it out
+ // when we do the next k/v backup operation on this transport.
+ mCurrentToken = 0;
+ Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
+ }
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ } else {
+ Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
+ // The named transport isn't registered, so we can't know what its current dataset token
+ // is. Reset as above.
+ mCurrentToken = 0;
+ }
+ }
+
+ /**
+ * Supply the configuration intent for the given transport. If the name is not one of the
+ * available transports, or if the transport does not supply any configuration UI, the method
+ * returns {@code null}.
+ */
+ public Intent getConfigurationIntent(String transportName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getConfigurationIntent");
+ try {
+ Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
+ }
+ return intent;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Supply the current destination string for the given transport. If the name is not one of the
+ * registered transports the method will return null.
+ *
+ * <p>This string is used VERBATIM as the summary text of the relevant Settings item.
+ *
+ * @param transportName The name of the registered transport.
+ * @return The current destination string or null if the transport is not registered.
+ */
+ public String getDestinationString(String transportName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "getDestinationString");
+
+ try {
+ String string = mTransportManager.getTransportCurrentDestinationString(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getDestinationString() returning " + string);
+ }
+ return string;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /** Supply the manage-data intent for the given transport. */
+ public Intent getDataManagementIntent(String transportName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getDataManagementIntent");
+
+ try {
+ Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
+ }
+ return intent;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Supply the menu label for affordances that fire the manage-data intent for the given
+ * transport.
+ */
+ public String getDataManagementLabel(String transportName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getDataManagementLabel");
+
+ try {
+ String label = mTransportManager.getTransportDataManagementLabel(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getDataManagementLabel() returning " + label);
+ }
+ return label;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Callback: a requested backup agent has been instantiated. This should only be called from the
+ * {@link ActivityManager}.
+ */
+ public void agentConnected(String packageName, IBinder agentBinder) {
+ synchronized (mAgentConnectLock) {
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+ IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
+ mConnectedAgent = agent;
+ mConnecting = false;
+ } else {
+ Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ + " claiming agent connected");
+ }
+ mAgentConnectLock.notifyAll();
+ }
+ }
+
+ /**
+ * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
+ * to come up in the first place, the agentBinder argument will be {@code null}. This should
+ * only be called from the {@link ActivityManager}.
+ */
+ public void agentDisconnected(String packageName) {
+ // TODO: handle backup being interrupted
+ synchronized (mAgentConnectLock) {
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ mConnectedAgent = null;
+ mConnecting = false;
+ } else {
+ Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ + " claiming agent disconnected");
+ }
+ mAgentConnectLock.notifyAll();
+ }
+ }
+
+ /**
+ * An application being installed will need a restore pass, then the {@link PackageManager} will
+ * need to be told when the restore is finished.
+ */
+ public void restoreAtInstall(String packageName, int token) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ + " attemping install-time restore");
+ return;
+ }
+
+ boolean skip = false;
+
+ long restoreSet = getAvailableRestoreToken(packageName);
+ if (DEBUG) {
+ Slog.v(TAG, "restoreAtInstall pkg=" + packageName
+ + " token=" + Integer.toHexString(token)
+ + " restoreSet=" + Long.toHexString(restoreSet));
+ }
+ if (restoreSet == 0) {
+ if (MORE_DEBUG) Slog.i(TAG, "No restore set");
+ skip = true;
+ }
+
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
+ if (transportClient == null) {
+ if (DEBUG) Slog.w(TAG, "No transport client");
+ skip = true;
+ }
+
+ if (!mAutoRestore) {
+ if (DEBUG) {
+ Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
+ }
+ skip = true;
+ }
+
+ if (!skip) {
+ try {
+ // okay, we're going to attempt a restore of this package from this restore set.
+ // The eventual message back into the Package Manager to run the post-install
+ // steps for 'token' will be issued from the restore handling code.
+
+ mWakelock.acquire();
+
+ OnTaskFinishedListener listener = caller -> {
+ mTransportManager.disposeOfTransportClient(transportClient, caller);
+ mWakelock.release();
+ };
+
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Restore at install of " + packageName);
+ }
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj =
+ RestoreParams.createForRestoreAtInstall(
+ transportClient,
+ /* observer */ null,
+ /* monitor */ null,
+ restoreSet,
+ packageName,
+ token,
+ listener);
+ mBackupHandler.sendMessage(msg);
+ } catch (Exception e) {
+ // Calling into the transport broke; back off and proceed with the installation.
+ Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
+ skip = true;
+ }
+ }
+
+ if (skip) {
+ // Auto-restore disabled or no way to attempt a restore
+
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(
+ transportClient, "BMS.restoreAtInstall()");
+ }
+
+ // Tell the PackageManager to proceed with the post-install handling for this package.
+ if (DEBUG) Slog.v(TAG, "Finishing install immediately");
+ try {
+ mPackageManagerBinder.finishPackageInstall(token, false);
+ } catch (RemoteException e) { /* can't happen */ }
+ }
+ }
+
+ /** Hand off a restore session. */
+ public IRestoreSession beginRestoreSession(String packageName, String transport) {
+ if (DEBUG) {
+ Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
+ + " transport=" + transport);
+ }
+
+ boolean needPermission = true;
+ if (transport == null) {
+ transport = mTransportManager.getCurrentTransportName();
+
+ if (packageName != null) {
+ PackageInfo app = null;
+ try {
+ app = mPackageManager.getPackageInfo(packageName, 0);
+ } catch (NameNotFoundException nnf) {
+ Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+ throw new IllegalArgumentException("Package " + packageName + " not found");
+ }
+
+ if (app.applicationInfo.uid == Binder.getCallingUid()) {
+ // So: using the current active transport, and the caller has asked
+ // that its own package will be restored. In this narrow use case
+ // we do not require the caller to hold the permission.
+ needPermission = false;
+ }
+ }
+ }
+
+ if (needPermission) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "beginRestoreSession");
+ } else {
+ if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
+ }
+
+ synchronized (this) {
+ if (mActiveRestoreSession != null) {
+ Slog.i(TAG, "Restore session requested but one already active");
+ return null;
+ }
+ if (mBackupRunning) {
+ Slog.i(TAG, "Restore session requested but currently running backups");
+ return null;
+ }
+ mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+ mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
+ }
+ return mActiveRestoreSession;
+ }
+
+ /** Clear the specified restore session. */
+ public void clearRestoreSession(ActiveRestoreSession currentSession) {
+ synchronized (this) {
+ if (currentSession != mActiveRestoreSession) {
+ Slog.e(TAG, "ending non-current restore session");
+ } else {
+ if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
+ mActiveRestoreSession = null;
+ mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+ }
+ }
+ }
+
+ /**
+ * Note that a currently-active backup agent has notified us that it has completed the given
+ * outstanding asynchronous backup/restore operation.
+ */
+ public void opComplete(int token, long result) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
+ }
+ Operation op = null;
+ synchronized (mCurrentOpLock) {
+ op = mCurrentOperations.get(token);
+ if (op != null) {
+ if (op.state == OP_TIMEOUT) {
+ // The operation already timed out, and this is a late response. Tidy up
+ // and ignore it; we've already dealt with the timeout.
+ op = null;
+ mCurrentOperations.delete(token);
+ } else if (op.state == OP_ACKNOWLEDGED) {
+ if (DEBUG) {
+ Slog.w(TAG, "Received duplicate ack for token="
+ + Integer.toHexString(token));
+ }
+ op = null;
+ mCurrentOperations.remove(token);
+ } else if (op.state == OP_PENDING) {
+ // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
+ // called after we we receive this call.
+ op.state = OP_ACKNOWLEDGED;
+ }
+ }
+ mCurrentOpLock.notifyAll();
+ }
+
+ // The completion callback, if any, is invoked on the handler
+ if (op != null && op.callback != null) {
+ Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+ Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
+ mBackupHandler.sendMessage(msg);
+ }
+ }
+
+ /** Checks if the package is eligible for backup. */
+ public boolean isAppEligibleForBackup(String packageName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
+
+ long oldToken = Binder.clearCallingIdentity();
+ try {
+ String callerLogString = "BMS.isAppEligibleForBackup";
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient(callerLogString);
+ boolean eligible =
+ AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName, mPackageManager);
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
+ return eligible;
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
+ }
+ }
+
+ /** Returns the inputted packages that are eligible for backup. */
+ public String[] filterAppsEligibleForBackup(String[] packages) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
+
+ long oldToken = Binder.clearCallingIdentity();
+ try {
+ String callerLogString = "BMS.filterAppsEligibleForBackup";
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient(callerLogString);
+ List<String> eligibleApps = new LinkedList<>();
+ for (String packageName : packages) {
+ if (AppBackupUtils
+ .appIsRunningAndEligibleForBackupWithTransport(
+ transportClient, packageName, mPackageManager)) {
+ eligibleApps.add(packageName);
+ }
+ }
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+ }
+ return eligibleApps.toArray(new String[eligibleApps.size()]);
+ } finally {
+ Binder.restoreCallingIdentity(oldToken);
+ }
+ }
+
+ /** Prints service state for 'dumpsys backup'. */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ if (args != null) {
+ for (String arg : args) {
+ if ("-h".equals(arg)) {
+ pw.println("'dumpsys backup' optional arguments:");
+ pw.println(" -h : this help text");
+ pw.println(" a[gents] : dump information about defined backup agents");
+ return;
+ } else if ("agents".startsWith(arg)) {
+ dumpAgents(pw);
+ return;
+ } else if ("transportclients".equals(arg.toLowerCase())) {
+ mTransportManager.dumpTransportClients(pw);
+ return;
+ } else if ("transportstats".equals(arg.toLowerCase())) {
+ mTransportManager.dumpTransportStats(pw);
+ return;
+ }
+ }
+ }
+ dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void dumpAgents(PrintWriter pw) {
+ List<PackageInfo> agentPackages = allAgentPackages();
+ pw.println("Defined backup agents:");
+ for (PackageInfo pkg : agentPackages) {
+ pw.print(" ");
+ pw.print(pkg.packageName);
+ pw.println(':');
+ pw.print(" ");
+ pw.println(pkg.applicationInfo.backupAgentName);
+ }
+ }
+
+ private void dumpInternal(PrintWriter pw) {
+ synchronized (mQueueLock) {
+ pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+ + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
+ pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
+ if (mBackupRunning) pw.println("Backup currently running");
+ pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
+ pw.println("Last backup pass started: " + mLastBackupPass
+ + " (now = " + System.currentTimeMillis() + ')');
+ pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled());
+
+ pw.println("Transport whitelist:");
+ for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
+ pw.print(" ");
+ pw.println(transport.flattenToShortString());
+ }
+
+ pw.println("Available transports:");
+ final String[] transports = listAllTransports();
+ if (transports != null) {
+ for (String t : transports) {
+ pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * "
+ : " ") + t);
+ try {
+ File dir = new File(mBaseStateDir,
+ mTransportManager.getTransportDirName(t));
+ pw.println(" destination: "
+ + mTransportManager.getTransportCurrentDestinationString(t));
+ pw.println(" intent: "
+ + mTransportManager.getTransportConfigurationIntent(t));
+ for (File f : dir.listFiles()) {
+ pw.println(
+ " " + f.getName() + " - " + f.length() + " state bytes");
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in transport", e);
+ pw.println(" Error: " + e);
+ }
+ }
+ }
+
+ mTransportManager.dumpTransportClients(pw);
+
+ pw.println("Pending init: " + mPendingInits.size());
+ for (String s : mPendingInits) {
+ pw.println(" " + s);
+ }
+
+ pw.print("Ancestral: ");
+ pw.println(Long.toHexString(mAncestralToken));
+ pw.print("Current: ");
+ pw.println(Long.toHexString(mCurrentToken));
+
+ int numPackages = mBackupParticipants.size();
+ pw.println("Participants:");
+ for (int i = 0; i < numPackages; i++) {
+ int uid = mBackupParticipants.keyAt(i);
+ pw.print(" uid: ");
+ pw.println(uid);
+ HashSet<String> participants = mBackupParticipants.valueAt(i);
+ for (String app : participants) {
+ pw.println(" " + app);
+ }
+ }
+
+ pw.println("Ancestral packages: "
+ + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
+ if (mAncestralPackages != null) {
+ for (String pkg : mAncestralPackages) {
+ pw.println(" " + pkg);
+ }
+ }
+
+ Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
+ pw.println("Ever backed up: " + processedPackages.size());
+ for (String pkg : processedPackages) {
+ pw.println(" " + pkg);
+ }
+
+ pw.println("Pending key/value backup: " + mPendingBackups.size());
+ for (BackupRequest req : mPendingBackups.values()) {
+ pw.println(" " + req);
+ }
+
+ pw.println("Full backup queue:" + mFullBackupQueue.size());
+ for (FullBackupEntry entry : mFullBackupQueue) {
+ pw.print(" ");
+ pw.print(entry.lastBackup);
+ pw.print(" : ");
+ pw.println(entry.packageName);
+ }
+ }
+ }
+
+
+ public IBackupManager getBackupManagerBinder() {
+ return mBackupManagerBinder;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
index 94365d7..bace1aa 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -1,10 +1,10 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import android.annotation.Nullable;
import android.app.backup.FullBackup;
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 6af098b..5e92339 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -16,13 +16,13 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -39,8 +39,8 @@
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.utils.FullBackupUtils;
@@ -53,7 +53,7 @@
* and emitting it to the designated OutputStream.
*/
public class FullBackupEngine {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
OutputStream mOutput;
FullBackupPreflight mPreflightHook;
BackupRestoreTask mTimeoutMonitor;
@@ -178,7 +178,7 @@
}
public FullBackupEngine(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
OutputStream output,
FullBackupPreflight preflightHook,
PackageInfo pkg,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index bc7d9fc..e142537 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -17,8 +17,8 @@
package com.android.server.backup.fullbackup;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.app.backup.IBackupManager;
import android.content.ComponentName;
@@ -34,7 +34,7 @@
import com.android.internal.backup.IObbBackupService;
import com.android.internal.util.Preconditions;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.FullBackupUtils;
import java.io.IOException;
@@ -45,11 +45,11 @@
*/
public class FullBackupObbConnection implements ServiceConnection {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
volatile IObbBackupService mService;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- public FullBackupObbConnection(BackupManagerService backupManagerService) {
+ public FullBackupObbConnection(UserBackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
mService = null;
mAgentTimeoutParameters = Preconditions.checkNotNull(
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 44edabc..43a80c4 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -16,13 +16,13 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.app.backup.IFullBackupRestoreObserver;
import android.content.pm.ApplicationInfo;
@@ -37,7 +37,7 @@
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.KeyValueAdbBackupEngine;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.AppBackupUtils;
import com.android.server.backup.utils.PasswordUtils;
@@ -66,7 +66,7 @@
*/
public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
FullBackupEngine mBackupEngine;
final AtomicBoolean mLatch;
@@ -86,7 +86,7 @@
String mEncryptPassword;
private final int mCurrentOpToken;
- public PerformAdbBackupTask(BackupManagerService backupManagerService,
+ public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 755095e..5b449c5 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -19,9 +19,9 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.annotation.Nullable;
import android.app.IBackupAgent;
@@ -45,10 +45,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FullBackupJob;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
import com.android.server.backup.remote.RemoteCall;
@@ -97,7 +97,7 @@
*/
public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
public static PerformFullTransportBackupTask newWithCurrentTransport(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
IFullBackupRestoreObserver observer,
String[] whichPackages,
boolean updateSchedule,
@@ -128,7 +128,7 @@
private static final String TAG = "PFTBT";
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
private final Object mCancelLock = new Object();
ArrayList<PackageInfo> mPackages;
@@ -150,7 +150,7 @@
private final int mCurrentOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
+ public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
TransportClient transportClient,
IFullBackupRestoreObserver observer,
String[] whichPackages, boolean updateSchedule,
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index f66d8cc..ba153bf 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -35,10 +35,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformAdbBackupTask;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.keyvalue.BackupRequest;
@@ -84,10 +84,10 @@
public static final int MSG_BACKUP_RESTORE_STEP = 20;
public static final int MSG_OP_COMPLETE = 21;
- private final BackupManagerService backupManagerService;
+ private final UserBackupManagerService backupManagerService;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
+ public BackupHandler(UserBackupManagerService backupManagerService, Looper looper) {
super(looper);
this.backupManagerService = backupManagerService;
mAgentTimeoutParameters = Preconditions.checkNotNull(
diff --git a/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java b/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
index b0b8037..396f369 100644
--- a/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
@@ -18,13 +18,13 @@
import android.content.pm.IPackageDataObserver;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
public class ClearDataObserver extends IPackageDataObserver.Stub {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
- public ClearDataObserver(BackupManagerService backupManagerService) {
+ public ClearDataObserver(UserBackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
}
diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
index d028104..5ffa795 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
@@ -22,20 +22,20 @@
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.transport.TransportClient;
import java.io.File;
public class PerformClearTask implements Runnable {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final TransportManager mTransportManager;
private final TransportClient mTransportClient;
private final PackageInfo mPackage;
private final OnTaskFinishedListener mListener;
- PerformClearTask(BackupManagerService backupManagerService,
+ PerformClearTask(UserBackupManagerService backupManagerService,
TransportClient transportClient, PackageInfo packageInfo,
OnTaskFinishedListener listener) {
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 1ef740d..6b78fbf 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -30,8 +30,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.transport.TransportClient;
import java.io.File;
@@ -49,7 +49,7 @@
* operation was successful then it's {@link BackupTransport#TRANSPORT_OK}.
*/
public class PerformInitializeTask implements Runnable {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final TransportManager mTransportManager;
private final String[] mQueue;
private final File mBaseStateDir;
@@ -57,7 +57,7 @@
@Nullable private IBackupObserver mObserver;
public PerformInitializeTask(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
String[] transportNames,
@Nullable IBackupObserver observer,
OnTaskFinishedListener listener) {
@@ -72,7 +72,7 @@
@VisibleForTesting
PerformInitializeTask(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportManager transportManager,
String[] transportNames,
@Nullable IBackupObserver observer,
diff --git a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
index 69720d4..7e2ac796 100644
--- a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
@@ -23,15 +23,15 @@
import android.os.Handler;
import android.util.Slog;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.UserBackupManagerService;
public class ProvisionedObserver extends ContentObserver {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
public ProvisionedObserver(
- BackupManagerService backupManagerService, Handler handler) {
+ UserBackupManagerService backupManagerService, Handler handler) {
super(handler);
this.backupManagerService = backupManagerService;
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
index 6f574ca..2a5d913 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -18,8 +18,8 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.RUN_BACKUP_ACTION;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.RUN_BACKUP_ACTION;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
import android.app.PendingIntent;
@@ -29,13 +29,13 @@
import android.os.Message;
import android.util.Slog;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
public class RunBackupReceiver extends BroadcastReceiver {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
- public RunBackupReceiver(BackupManagerService backupManagerService) {
+ public RunBackupReceiver(UserBackupManagerService backupManagerService) {
this.backupManagerService = backupManagerService;
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 548c580..38870cb 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -17,8 +17,8 @@
package com.android.server.backup.internal;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.RUN_INITIALIZE_ACTION;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.RUN_INITIALIZE_ACTION;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -27,12 +27,12 @@
import android.util.ArraySet;
import android.util.Slog;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
public class RunInitializeReceiver extends BroadcastReceiver {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
- public RunInitializeReceiver(BackupManagerService backupManagerService) {
+ public RunInitializeReceiver(UserBackupManagerService backupManagerService) {
mBackupManagerService = backupManagerService;
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
index bb8a1d1..535c7cb 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -30,6 +30,7 @@
import com.android.server.EventLogTags;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteResult;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
@@ -54,7 +55,7 @@
public class KeyValueBackupReporter {
@VisibleForTesting static final String TAG = "KeyValueBackupTask";
private static final boolean DEBUG = BackupManagerService.DEBUG;
- @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false;
+ @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG;
static void onNewThread(String threadName) {
if (DEBUG) {
@@ -62,12 +63,12 @@
}
}
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final IBackupObserver mObserver;
@Nullable private IBackupManagerMonitor mMonitor;
KeyValueBackupReporter(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
IBackupObserver observer,
@Nullable IBackupManagerMonitor monitor) {
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index d6f2a87..f39d795 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -22,9 +22,9 @@
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -35,7 +35,6 @@
import android.app.backup.BackupManager;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupCallback;
-import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.content.pm.ApplicationInfo;
@@ -55,11 +54,11 @@
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
@@ -95,12 +94,12 @@
* <p>A few definitions:
*
* <ul>
- * <li>State directory: {@link BackupManagerService#getBaseStateDir()}/<transport>
+ * <li>State directory: {@link UserBackupManagerService#getBaseStateDir()}/<transport>
* <li>State file: {@link
- * BackupManagerService#getBaseStateDir()}/<transport>/<package><br>
+ * UserBackupManagerService#getBaseStateDir()}/<transport>/<package><br>
* Represents the state of the backup data for a specific package in the current dataset.
- * <li>Stage directory: {@link BackupManagerService#getDataDir()}
- * <li>Stage file: {@link BackupManagerService#getDataDir()}/<package>.data<br>
+ * <li>Stage directory: {@link UserBackupManagerService#getDataDir()}
+ * <li>Stage file: {@link UserBackupManagerService#getDataDir()}/<package>.data<br>
* Contains staged data that the agents wrote via {@link BackupDataOutput}, to be transmitted
* to the transport.
* </ul>
@@ -112,7 +111,7 @@
* of incremental choice. If non-incremental, PM will only be backed-up if specified in the queue,
* and if it's the case it will be re-positioned at the head of the queue.
*
- * <p>Before starting, this task will register itself in {@link BackupManagerService} current
+ * <p>Before starting, this task will register itself in {@link UserBackupManagerService} current
* operations.
*
* <p>In summary, this task will for each package:
@@ -121,7 +120,7 @@
* <li>Bind to its {@link IBackupAgent}.
* <li>Request transport quota and flags.
* <li>Call {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
- * ParcelFileDescriptor, long, int, IBackupManager, int)} via {@link RemoteCall} passing the
+ * ParcelFileDescriptor, long, IBackupCallback, int)} via {@link RemoteCall} passing the
* old state file descriptor (read), the backup data file descriptor (write), the new state
* file descriptor (write), the quota and the transport flags. This will call {@link
* BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} with
@@ -131,7 +130,7 @@
* <ul>
* <li>Agent response.
* <li>Agent time-out (specified via {@link
- * BackupManagerService#getAgentTimeoutParameters()}.
+ * UserBackupManagerService#getAgentTimeoutParameters()}.
* <li>External cancellation or thread interrupt.
* </ul>
* <li>Unbind the agent.
@@ -149,11 +148,11 @@
* <li>Mark data-changed for the remaining packages in the queue (skipped packages).
* <li>Delete the {@link DataChangedJournal} provided. Note that this should not be the current
* journal.
- * <li>Set {@link BackupManagerService} current token as {@link
+ * <li>Set {@link UserBackupManagerService} current token as {@link
* IBackupTransport#getCurrentRestoreSet()}, if applicable.
* <li>Add the transport to the list of transports pending initialization ({@link
- * BackupManagerService#getPendingInits()}) and kick-off initialization if the transport ever
- * returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
+ * UserBackupManagerService#getPendingInits()}) and kick-off initialization if the transport
+ * ever returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
* <li>Unregister the task in current operations.
* <li>Release the wakelock.
* <li>Kick-off {@link PerformFullTransportBackupTask} if a list of full-backup packages was
@@ -174,7 +173,7 @@
private static final int THREAD_PRIORITY = Process.THREAD_PRIORITY_BACKGROUND;
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
private static final String BLANK_STATE_FILE_NAME = "blank_state";
- private static final String PM_PACKAGE = BackupManagerService.PACKAGE_MANAGER_SENTINEL;
+ private static final String PM_PACKAGE = UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
@VisibleForTesting public static final String STAGING_FILE_SUFFIX = ".data";
@VisibleForTesting public static final String NEW_STATE_FILE_SUFFIX = ".new";
@@ -182,7 +181,7 @@
* Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new
* dedicated thread and kicks off the operation in it.
*
- * @param backupManagerService The {@link BackupManagerService} system service.
+ * @param backupManagerService The {@link UserBackupManagerService} instance.
* @param transportClient The {@link TransportClient} that contains the transport used for the
* operation.
* @param transportDirName The value of {@link IBackupTransport#transportDirName()} for the
@@ -201,7 +200,7 @@
* @return The {@link KeyValueBackupTask} that was started.
*/
public static KeyValueBackupTask start(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
String transportDirName,
List<String> queue,
@@ -232,7 +231,7 @@
return task;
}
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final PackageManager mPackageManager;
private final TransportManager mTransportManager;
private final TransportClient mTransportClient;
@@ -289,7 +288,7 @@
@VisibleForTesting
public KeyValueBackupTask(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
String transportDirName,
List<String> queue,
diff --git a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
index 28d85a6..bfc97ae 100644
--- a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
+++ b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
@@ -20,12 +20,12 @@
import android.app.backup.IBackupManager;
import android.os.RemoteException;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
/**
* An implementation of {@link IBackupCallback} that routes the result to {@link
- * BackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token provided
- * in the constructor.
+ * UserBackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token
+ * provided in the constructor.
*/
public class ServiceBackupCallback extends IBackupCallback.Stub {
private final IBackupManager mBackupManager;
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 140dded..e273b32 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -36,8 +36,8 @@
import android.os.PowerManager;
import android.util.Slog;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.params.RestoreGetSetsParams;
import com.android.server.backup.params.RestoreParams;
@@ -53,14 +53,14 @@
private final TransportManager mTransportManager;
private final String mTransportName;
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
@Nullable private final String mPackageName;
public RestoreSet[] mRestoreSets = null;
boolean mEnded = false;
boolean mTimedOut = false;
public ActiveRestoreSession(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
@Nullable String packageName,
String transportName) {
mBackupManagerService = backupManagerService;
@@ -405,10 +405,10 @@
// Posted to the handler to tear down a restore session in a cleanly synchronized way
public class EndRestoreRunnable implements Runnable {
- BackupManagerService mBackupManager;
+ UserBackupManagerService mBackupManager;
ActiveRestoreSession mSession;
- public EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
+ public EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session) {
mBackupManager = manager;
mSession = session;
}
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index a8c7ce6..e4890e0 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -23,8 +23,8 @@
import com.android.internal.util.Preconditions;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -35,12 +35,12 @@
public class AdbRestoreFinishedLatch implements BackupRestoreTask {
private static final String TAG = "AdbRestoreFinishedLatch";
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
final CountDownLatch mLatch;
private final int mCurrentOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
+ public AdbRestoreFinishedLatch(UserBackupManagerService backupManagerService,
int currentOpToken) {
this.backupManagerService = backupManagerService;
mLatch = new CountDownLatch(1);
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
index dc7044e..184a6d0 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
@@ -3,7 +3,7 @@
import android.app.IBackupAgent;
import android.os.RemoteException;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
/**
* Runner that can be placed on a separate thread to do in-process invocation of the "restore
@@ -13,10 +13,10 @@
private final IBackupAgent mAgent;
private final int mToken;
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
AdbRestoreFinishedRunnable(IBackupAgent agent, int token,
- BackupManagerService backupManagerService) {
+ UserBackupManagerService backupManagerService) {
mAgent = agent;
mToken = token;
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 1084f52..0d26ea5 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -16,13 +16,13 @@
package com.android.server.backup.restore;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import android.app.ApplicationThreadConstants;
@@ -44,10 +44,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.BytesReadListener;
import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -57,7 +57,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -68,7 +67,7 @@
*/
public class FullRestoreEngine extends RestoreEngine {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
// Task in charge of monitoring timeouts
private final BackupRestoreTask mMonitorTask;
@@ -129,7 +128,7 @@
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
final boolean mIsAdbRestore;
- public FullRestoreEngine(BackupManagerService backupManagerService,
+ public FullRestoreEngine(UserBackupManagerService backupManagerService,
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
boolean allowObbs, int ephemeralOpToken, boolean isAdbRestore) {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 32dbad9..c904256 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,18 +16,15 @@
package com.android.server.backup.restore;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
@@ -36,15 +33,12 @@
import android.content.pm.Signature;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
-import com.android.server.backup.PackageManagerBackupAgent;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
import com.android.server.backup.utils.PasswordUtils;
@@ -72,7 +66,7 @@
public class PerformAdbRestoreTask implements Runnable {
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
private final ParcelFileDescriptor mInputFile;
private final String mCurrentPassword;
private final String mDecryptPassword;
@@ -106,7 +100,7 @@
// Packages we've already wiped data on when restoring their first file
private final HashSet<String> mClearedPackages = new HashSet<>();
- public PerformAdbRestoreTask(BackupManagerService backupManagerService,
+ public PerformAdbRestoreTask(UserBackupManagerService backupManagerService,
ParcelFileDescriptor fd, String curPassword, String decryptPassword,
IFullBackupRestoreObserver observer, AtomicBoolean latch) {
this.mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 580f70a..f7efad6 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -17,12 +17,12 @@
package com.android.server.backup.restore;
import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
+import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -40,8 +40,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -62,8 +62,8 @@
import com.android.server.backup.BackupUtils;
import com.android.server.backup.PackageManagerBackupAgent;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.utils.AppBackupUtils;
@@ -80,7 +80,7 @@
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
- private BackupManagerService backupManagerService;
+ private UserBackupManagerService backupManagerService;
private final TransportManager mTransportManager;
// Transport client we're working with to do the restore
private final TransportClient mTransportClient;
@@ -164,7 +164,7 @@
// This task can assume that the wakelock is properly held for it and doesn't have to worry
// about releasing it.
public PerformUnifiedRestoreTask(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
index 635b6d6..c4aa2d7 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
@@ -21,7 +21,7 @@
import android.os.RemoteException;
import com.android.server.backup.FileMetadata;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import java.io.IOException;
@@ -35,9 +35,9 @@
private final FileMetadata mInfo;
private final ParcelFileDescriptor mSocket;
private final int mToken;
- private final BackupManagerService mBackupManagerService;
+ private final UserBackupManagerService mBackupManagerService;
- RestoreFileRunnable(BackupManagerService backupManagerService, IBackupAgent agent,
+ RestoreFileRunnable(UserBackupManagerService backupManagerService, IBackupAgent agent,
FileMetadata info, ParcelFileDescriptor socket, int token) throws IOException {
mAgent = agent;
mInfo = info;
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index c933833..e465c7e 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -17,8 +17,8 @@
package com.android.server.backup.utils;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.annotation.Nullable;
import android.app.backup.BackupTransport;
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 6dd5284..0f4b681 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -34,14 +34,14 @@
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.app.backup.BackupAgent;
import android.app.backup.BackupManagerMonitor;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6174300..784d398 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -32,6 +32,10 @@
"android.hardware.tv.cec-V1.0-java",
],
+ required: [
+ "gps_debug.conf",
+ ],
+
static_libs: [
"time_zone_distro",
"time_zone_distro_installer",
@@ -69,3 +73,9 @@
name: "services.core",
static_libs: ["services.core.priorityboosted"],
}
+
+
+prebuilt_etc {
+ name: "gps_debug.conf",
+ src: "java/com/android/server/location/gps_debug.conf",
+}
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/AbstractMasterSystemService.java
index 9c1e3cd..76010b3 100644
--- a/services/core/java/com/android/server/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/AbstractMasterSystemService.java
@@ -39,6 +39,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.util.List;
@@ -93,6 +94,12 @@
public boolean debug = false;
/**
+ * Whether the service is allowed to bind to an instant-app.
+ */
+ @GuardedBy("mLock")
+ protected boolean mAllowInstantService;
+
+ /**
* Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
* be disabled through {@link UserManager}.
*/
@@ -176,6 +183,107 @@
}
/**
+ * Gets whether the service is allowed to bind to an instant-app.
+ *
+ * <p>Typically called by {@code ShellCommand} during CTS tests.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final boolean getAllowInstantService() {
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ return mAllowInstantService;
+ }
+ }
+
+ /**
+ * Sets whether the service is allowed to bind to an instant-app.
+ *
+ * <p>Typically called by {@code ShellCommand} during CTS tests.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final void setAllowInstantService(boolean mode) {
+ Slog.i(mTag, "setAllowInstantService(): " + mode);
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ mAllowInstantService = mode;
+ }
+ }
+
+ /**
+ * Temporary sets the service implementation.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @param componentName name of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically reset
+ * to the default component after this timeout expires).
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ * @throws IllegalArgumentException if value of {@code durationMs} is higher than
+ * {@link #getMaximumTemporaryServiceDurationMs()}.
+ */
+ public final void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+ int durationMs) {
+ Slog.i(mTag, "setTemporaryService(" + userId + ") to " + componentName + " for "
+ + durationMs + "ms");
+ enforceCallingPermissionForManagement();
+
+ Preconditions.checkNotNull(componentName);
+ final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
+ if (durationMs > maxDurationMs) {
+ throw new IllegalArgumentException(
+ "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+ }
+
+ synchronized (mLock) {
+ final S service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.setTemporaryServiceLocked(componentName, durationMs);
+ }
+ }
+ }
+
+ /**
+ * Gets the maximum time the service implementation can be changed.
+ *
+ * @throws UnsupportedOperationException if subclass doesn't override it.
+ */
+ protected int getMaximumTemporaryServiceDurationMs() {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
+
+ /**
+ * Resets the temporary service implementation to the default component.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final void resetTemporaryService(@UserIdInt int userId) {
+ Slog.i(mTag, "resetTemporaryService(): " + userId);
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ final S service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.resetTemporaryServiceLocked();
+ }
+ }
+ }
+
+ /**
+ * Asserts that the caller has permissions to manage this service.
+ *
+ * <p>Typically called by {@code ShellCommand} implementations.
+ *
+ * @throws UnsupportedOperationException if subclass doesn't override it.
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ protected void enforceCallingPermissionForManagement() {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
+
+ /**
* Creates a new service that will be added to the cache.
*
* @param resolvedUserId the resolved user id for the service.
@@ -362,6 +470,7 @@
pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
pw.print(" Verbose: "); pw.println(realVerbose);
pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
+ pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
pw.print(prefix); pw.print("Settings property: "); pw.println(
getServiceSettingsProperty());
pw.print(prefix); pw.print("Cached services: ");
diff --git a/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java b/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java
new file mode 100644
index 0000000..f532b22
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Base class representing a remote service that can queue multiple pending requests while not
+ * bound.
+ *
+ * @param <S> the concrete remote service class
+ *
+ * @hide
+ */
+public abstract class AbstractMultiplePendingRequestsRemoteService<
+ S extends AbstractMultiplePendingRequestsRemoteService<S>>
+ extends AbstractRemoteService<S> {
+
+ private final int mInitialCapacity;
+
+ protected ArrayList<PendingRequest<S>> mPendingRequests;
+
+ public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
+ @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
+ @NonNull VultureCallback callback, boolean bindInstantServiceAllowed, boolean verbose,
+ int initialCapacity) {
+ super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
+ verbose);
+ mInitialCapacity = initialCapacity;
+ }
+
+ @Override // from AbstractRemoteService
+ void handlePendingRequests() {
+ if (mPendingRequests != null) {
+ final int size = mPendingRequests.size();
+ if (mVerbose) Slog.v(mTag, "Sending " + size + " pending requests");
+ for (int i = 0; i < size; i++) {
+ mPendingRequests.get(i).run();
+ }
+ mPendingRequests = null;
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ protected void handleOnDestroy() {
+ if (mPendingRequests != null) {
+ final int size = mPendingRequests.size();
+ if (mVerbose) Slog.v(mTag, "Canceling " + size + " pending requests");
+ for (int i = 0; i < size; i++) {
+ mPendingRequests.get(i).cancel();
+ }
+ mPendingRequests = null;
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dump(prefix, pw);
+
+ pw.append(prefix).append("initialCapacity=").append(String.valueOf(mInitialCapacity))
+ .println();
+ final int size = mPendingRequests == null ? 0 : mPendingRequests.size();
+ pw.append(prefix).append("pendingRequests=").append(String.valueOf(size)).println();
+ }
+
+ @Override // from AbstractRemoteService
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ if (mPendingRequests == null) {
+ mPendingRequests = new ArrayList<>(mInitialCapacity);
+ }
+ mPendingRequests.add(pendingRequest);
+ if (mVerbose) {
+ Slog.v(mTag, "queued " + mPendingRequests.size() + " requests; last=" + pendingRequest);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index 71d261c..a26102d 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -26,12 +26,17 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -49,6 +54,9 @@
public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
M extends AbstractMasterSystemService<M, S>> {
+ /** Handler message to {@link #resetTemporaryServiceLocked()} */
+ private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
+
protected final @UserIdInt int mUserId;
protected final Object mLock;
protected final String mTag = getClass().getSimpleName();
@@ -70,6 +78,26 @@
@GuardedBy("mLock")
private ServiceInfo mServiceInfo;
+ /**
+ * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ */
+ @GuardedBy("mLock")
+ private String mTemporaryServiceName;
+
+ /**
+ * When the temporary service will expire (and reset back to the default).
+ */
+ @GuardedBy("mLock")
+ private long mTemporaryServiceExpiration;
+
+ /**
+ * Handler used to reset the temporary service name.
+ */
+ @GuardedBy("mLock")
+ private Handler mTemporaryHandler;
+
protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock,
@UserIdInt int userId) {
mMaster = master;
@@ -108,7 +136,8 @@
* <p>Typically called when the service {@link Settings} property or {@link UserManager}
* restriction changed, which includes the initial creation of the service.
*
- * <p>Subclasses can extend this method to provide extra initialization.
+ * <p>Subclasses can extend this method to provide extra initialization, like clearing up
+ * previous state.
*
* @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
*
@@ -129,7 +158,7 @@
mDisabled = disabled;
ComponentName serviceComponent = null;
ServiceInfo serviceInfo = null;
- final String componentName = getComponentNameFromSettings();
+ final String componentName = getComponentNameLocked();
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -190,6 +219,29 @@
}
/**
+ * Gets the current name of the service, which is either the
+ * {@link #getDefaultComponentName() default service} or the
+ * {@link #setTemporaryServiceLocked(String, int) temporary one}.
+ */
+ protected final String getComponentNameLocked() {
+ if (mTemporaryServiceName != null) {
+ // Always log it, as it should only be used on CTS or during development
+ Slog.w(mTag, "getComponentName(): using temporary name " + mTemporaryServiceName);
+ return mTemporaryServiceName;
+ }
+ return getDefaultComponentName();
+ }
+
+ /**
+ * Gets the name of the default component for the service.
+ *
+ * <p>Typically implemented by returning {@link #getComponentNameFromSettings()} or by using
+ * a string from the system resources.
+ */
+ @Nullable
+ protected abstract String getDefaultComponentName();
+
+ /**
* Gets this name of the remote service this service binds to as defined by {@link Settings}.
*/
@Nullable
@@ -200,6 +252,66 @@
}
/**
+ * Checks whether the current service for the user was temporarily set.
+ */
+ public final boolean isTemporaryServiceSetLocked() {
+ return mTemporaryServiceName != null;
+ }
+
+ /**
+ * Temporary sets the service implementation.
+ *
+ * @param componentName name of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically reset
+ * to the default component after this timeout expires).
+ */
+ protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
+ mTemporaryServiceName = componentName;
+
+ if (mTemporaryHandler == null) {
+ mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+ synchronized (mLock) {
+ resetTemporaryServiceLocked();
+ }
+ } else {
+ Slog.wtf(mTag, "invalid handler msg: " + msg);
+ }
+ }
+ };
+ } else {
+ removeResetTemporaryServiceMessageLocked();
+ }
+ mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
+ mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+
+ updateLocked(mDisabled);
+ }
+
+ private void removeResetTemporaryServiceMessageLocked() {
+ if (mMaster.verbose) {
+ Slog.v(mTag, "setTemporaryServiceLocked(): removing old message");
+ }
+ // NOTE: caller should already have checked it
+ mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+ }
+
+ /**
+ * Resets the temporary service implementation to the default component.
+ */
+ protected final void resetTemporaryServiceLocked() {
+ Slog.i(mTag, "resetting temporary service from " + mTemporaryServiceName);
+ mTemporaryServiceName = null;
+ if (mTemporaryHandler != null) {
+ removeResetTemporaryServiceMessageLocked();
+ mTemporaryHandler = null;
+ }
+ updateLocked(mDisabled);
+ }
+
+ /**
* Gets the {@link ComponentName} of the remote service this service binds to, or {@code null}
* if the service is disabled.
*/
@@ -289,12 +401,14 @@
pw.print(prefix); pw.print("Service UID: ");
pw.println(mServiceInfo.applicationInfo.uid);
}
- final String componentName = getComponentNameFromSettings();
- if (componentName != null) {
- pw.print(prefix); pw.print("Service name: ");
- pw.println(componentName);
+ if (mTemporaryServiceName != null) {
+ pw.print(prefix); pw.print("Temporary service name: "); pw.print(mTemporaryServiceName);
+ final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
+ pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.println(")");
+ pw.print(prefix); pw.print(prefix);
+ pw.print("Default service name: "); pw.println(getDefaultComponentName());
} else {
- pw.println("No service package set");
+ pw.print(prefix); pw.print("Service name: "); pw.println(getDefaultComponentName());
}
}
}
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
index 73a34d6..f636487 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -45,13 +45,20 @@
*
* <p>All state of this class is modified on a handler thread.
*
+ * <p><b>NOTE: </b>this class should not be extended directly, you should extend either
+ * {@link AbstractSinglePendingRequestRemoteService} or
+ * {@link AbstractMultiplePendingRequestsRemoteService}.
+ *
* <p>See {@code com.android.server.autofill.RemoteFillService} for a concrete
* (no pun intended) example of how to use it.
*
+ * @param <S> the concrete remote service class
+ *
* @hide
*/
//TODO(b/117779333): improve javadoc above instead of using Autofill as an example
-public abstract class AbstractRemoteService implements DeathRecipient {
+public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
+ implements DeathRecipient {
private static final int MSG_UNBIND = 1;
@@ -64,8 +71,6 @@
protected final Handler mHandler;
protected final ComponentName mComponentName;
- protected PendingRequest<? extends AbstractRemoteService> mPendingRequest;
-
private final Context mContext;
private final Intent mIntent;
private final VultureCallback mVultureCallback;
@@ -88,10 +93,11 @@
*
* @param service service that died!
*/
- void onServiceDied(AbstractRemoteService service);
+ void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service);
}
- public AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
+ // NOTE: must be package-protected so this class is not extend outside
+ AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
@NonNull ComponentName componentName, int userId, @NonNull VultureCallback callback,
boolean bindInstantServiceAllowed, boolean verbose) {
mContext = context;
@@ -118,12 +124,25 @@
return mDestroyed;
}
+ private void handleOnConnectedStateChangedInternal(boolean connected) {
+ if (connected) {
+ handlePendingRequests();
+ }
+ handleOnConnectedStateChanged(connected);
+ }
+
/**
- * Callback called when the system connected / disconnected to the service.
+ * Handles the pending requests when the connection it bounds to the remote service.
+ */
+ abstract void handlePendingRequests();
+
+ /**
+ * Callback called when the system connected / disconnected to the service and the pending
+ * requests have been handled.
*
* @param state {@code true} when connected, {@code false} when disconnected.
*/
- protected void onConnectedStateChanged(boolean state) {
+ protected void handleOnConnectedStateChanged(boolean state) {
}
/**
@@ -144,14 +163,18 @@
private void handleDestroy() {
if (checkIfDestroyed()) return;
- if (mPendingRequest != null) {
- mPendingRequest.cancel();
- mPendingRequest = null;
- }
- ensureUnbound();
+ handleOnDestroy();
+ handleEnsureUnbound();
mDestroyed = true;
}
+ /**
+ * Clears the state when this object is destroyed.
+ *
+ * <p>Typically used to cancel the pending requests.
+ */
+ protected abstract void handleOnDestroy();
+
@Override // from DeathRecipient
public void binderDied() {
mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleBinderDied, this));
@@ -183,9 +206,7 @@
pw.append(prefix).append(tab).append("destroyed=")
.append(String.valueOf(mDestroyed)).println();
pw.append(prefix).append(tab).append("bound=")
- .append(String.valueOf(isBound())).println();
- pw.append(prefix).append(tab).append("hasPendingRequest=")
- .append(String.valueOf(mPendingRequest != null)).println();
+ .append(String.valueOf(handleIsBound())).println();
pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
pw.append(prefix).append("idleTimeout=")
.append(Long.toString(getTimeoutIdleBindMillis() / 1000)).append("s").println();
@@ -194,7 +215,7 @@
pw.println();
}
- protected void scheduleRequest(PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+ protected void scheduleRequest(@NonNull PendingRequest<S> pendingRequest) {
mHandler.sendMessage(obtainMessage(
AbstractRemoteService::handlePendingRequest, this, pendingRequest));
}
@@ -215,19 +236,20 @@
private void handleUnbind() {
if (checkIfDestroyed()) return;
- ensureUnbound();
+ handleEnsureUnbound();
}
- private void handlePendingRequest(
- PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+ /**
+ * Handles a request, either processing it right now when bound, or saving it to be handled when
+ * bound.
+ */
+ protected final void handlePendingRequest(@NonNull PendingRequest<S> pendingRequest) {
if (checkIfDestroyed() || mCompleted) return;
- if (!isBound()) {
- if (mPendingRequest != null) {
- mPendingRequest.cancel();
- }
- mPendingRequest = pendingRequest;
- ensureBound();
+ if (!handleIsBound()) {
+ if (mVerbose) Slog.v(mTag, "handlePendingRequest(): queuing" + pendingRequest);
+ handlePendingRequestWhileUnBound(pendingRequest);
+ handleEnsureBound();
} else {
if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest);
pendingRequest.run();
@@ -237,12 +259,17 @@
}
}
- private boolean isBound() {
+ /**
+ * Defines what to do with a request that arrives while not bound to the service.
+ */
+ abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest);
+
+ private boolean handleIsBound() {
return mServiceInterface != null;
}
- private void ensureBound() {
- if (isBound() || mBinding) return;
+ private void handleEnsureBound() {
+ if (handleIsBound() || mBinding) return;
if (mVerbose) Slog.v(mTag, "ensureBound()");
mBinding = true;
@@ -253,7 +280,7 @@
}
final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
- new UserHandle(mUserId));
+ mHandler, new UserHandle(mUserId));
if (!willBind) {
Slog.w(mTag, "could not bind to " + mIntent + " using flags " + flags);
@@ -265,13 +292,13 @@
}
}
- private void ensureUnbound() {
- if (!isBound() && !mBinding) return;
+ private void handleEnsureUnbound() {
+ if (!handleIsBound() && !mBinding) return;
if (mVerbose) Slog.v(mTag, "ensureUnbound()");
mBinding = false;
- if (isBound()) {
- onConnectedStateChanged(false);
+ if (handleIsBound()) {
+ handleOnConnectedStateChangedInternal(false);
if (mServiceInterface != null) {
mServiceInterface.asBinder().unlinkToDeath(this, 0);
mServiceInterface = null;
@@ -283,6 +310,7 @@
private class RemoteServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mVerbose) Slog.v(mTag, "onServiceConnected()");
if (mDestroyed || !mBinding) {
// This is abnormal. Unbinding the connection has been requested already.
Slog.wtf(mTag, "onServiceConnected() was dispatched after unbindService.");
@@ -296,15 +324,7 @@
handleBinderDied();
return;
}
- onConnectedStateChanged(true);
-
- if (mPendingRequest != null) {
- final PendingRequest<? extends AbstractRemoteService> pendingRequest =
- mPendingRequest;
- mPendingRequest = null;
- handlePendingRequest(pendingRequest);
- }
-
+ handleOnConnectedStateChangedInternal(true);
mServiceDied = false;
}
@@ -325,25 +345,12 @@
return mDestroyed;
}
- protected boolean handleResponseCallbackCommon(
- PendingRequest<? extends AbstractRemoteService> pendingRequest) {
- if (isDestroyed()) return false;
-
- if (mPendingRequest == pendingRequest) {
- mPendingRequest = null;
- }
- if (mPendingRequest == null) {
- scheduleUnbind();
- }
- return true;
- }
-
/**
* Base class for the requests serviced by the remote service.
*
* @param <S> the remote service class
*/
- public abstract static class PendingRequest<S extends AbstractRemoteService>
+ public abstract static class PendingRequest<S extends AbstractRemoteService<S>>
implements Runnable {
protected final String mTag = getClass().getSimpleName();
protected final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java b/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java
new file mode 100644
index 0000000..8e1f540
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+
+/**
+ * Base class representing a remote service that can have only one pending requests while not bound.
+ *
+ * <p>If another request is received while not bound, the previous one will be canceled.
+ *
+ * @param <S> the concrete remote service class
+ *
+ * @hide
+ */
+public abstract class AbstractSinglePendingRequestRemoteService<
+ S extends AbstractSinglePendingRequestRemoteService<S>> extends AbstractRemoteService<S> {
+
+ protected PendingRequest<S> mPendingRequest;
+
+ public AbstractSinglePendingRequestRemoteService(@NonNull Context context,
+ @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
+ @NonNull VultureCallback callback, boolean bindInstantServiceAllowed,
+ boolean verbose) {
+ super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
+ verbose);
+ }
+
+ @Override // from AbstractRemoteService
+ void handlePendingRequests() {
+ if (mPendingRequest != null) {
+ final PendingRequest<S> pendingRequest = mPendingRequest;
+ mPendingRequest = null;
+ handlePendingRequest(pendingRequest);
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ protected void handleOnDestroy() {
+ if (mPendingRequest != null) {
+ mPendingRequest.cancel();
+ mPendingRequest = null;
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dump(prefix, pw);
+ pw.append(prefix).append("hasPendingRequest=")
+ .append(String.valueOf(mPendingRequest != null)).println();
+ }
+
+ @Override // from AbstractRemoteService
+ void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+ if (mPendingRequest != null) {
+ if (mVerbose) {
+ Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest);
+ }
+ mPendingRequest.cancel();
+ }
+ mPendingRequest = pendingRequest;
+ }
+}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 356a4da..8d912fa 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -692,7 +692,7 @@
}
});
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
storageManagerInternal.addExternalStoragePolicy(
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index dd96075..81f0259 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -56,6 +56,7 @@
private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
private boolean mEnabled;
private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
@@ -97,6 +98,9 @@
mBinderCallsStats.setSamplingInterval(mParser.getInt(
SETTINGS_SAMPLING_INTERVAL_KEY,
BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+ mBinderCallsStats.setMaxBinderCallStats(mParser.getInt(
+ SETTINGS_MAX_CALL_STATS_KEY,
+ BinderCallsStats.MAX_BINDER_CALL_STATS_COUNT_DEFAULT));
final boolean enabled =
@@ -112,6 +116,7 @@
}
mEnabled = enabled;
mBinderCallsStats.reset();
+ mBinderCallsStats.setAddDebugEntries(enabled);
}
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 564d35a..bb3b9f7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1667,6 +1667,24 @@
loge("Error parsing ip address in validation event");
}
}
+
+ @Override
+ public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+ String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+ // Netd event only allow registrants from system. Each NetworkMonitor thread is under
+ // the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
+ // event callback for certain nai. e.g. cellular. Register here to pass to
+ // NetworkMonitor instead.
+ // TODO: Move the Dns Event to NetworkMonitor. Use Binder.clearCallingIdentity() in
+ // registerNetworkAgent to have NetworkMonitor created with system process as design
+ // expectation. Also, NetdEventListenerService only allow one callback from each
+ // caller type. Need to re-factor NetdEventListenerService to allow multiple
+ // NetworkMonitor registrants.
+ if (nai != null && nai.satisfies(mDefaultRequest)) {
+ nai.networkMonitor.sendMessage(NetworkMonitor.EVENT_DNS_NOTIFICATION, returnCode);
+ }
+ }
};
@VisibleForTesting
@@ -2081,12 +2099,7 @@
return new MockableSystemProperties();
}
- private void updateTcpBufferSizes(NetworkAgentInfo nai) {
- if (isDefaultNetwork(nai) == false) {
- return;
- }
-
- String tcpBufferSizes = nai.linkProperties.getTcpBufferSizes();
+ private void updateTcpBufferSizes(String tcpBufferSizes) {
String[] values = null;
if (tcpBufferSizes != null) {
values = tcpBufferSizes.split(",");
@@ -4762,8 +4775,8 @@
updateUids(nai, null, nai.networkCapabilities);
}
- private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
- LinkProperties newLp = new LinkProperties(networkAgent.linkProperties);
+ private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+ LinkProperties oldLp) {
int netId = networkAgent.network.netId;
// The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
@@ -4778,7 +4791,9 @@
// for (LinkProperties lp : newLp.getStackedLinks()) {
// updateMtu(lp, null);
// }
- updateTcpBufferSizes(networkAgent);
+ if (isDefaultNetwork(networkAgent)) {
+ updateTcpBufferSizes(newLp.getTcpBufferSizes());
+ }
updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId);
@@ -4788,8 +4803,6 @@
// updateDnses will fetch the private DNS configuration from DnsManager.
mDnsManager.updatePrivateDnsStatus(netId, newLp);
- // Start or stop clat accordingly to network state.
- networkAgent.updateClat(mNMS);
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
@@ -4800,8 +4813,12 @@
synchronized (networkAgent) {
networkAgent.linkProperties = newLp;
}
+ // Start or stop clat accordingly to network state.
+ networkAgent.updateClat(mNMS);
notifyIfacesChangedForNetworkStats();
- notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+ if (networkAgent.everConnected) {
+ notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+ }
}
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
@@ -5106,13 +5123,7 @@
"; created=" + nai.created +
"; everConnected=" + nai.everConnected);
}
- LinkProperties oldLp = nai.linkProperties;
- synchronized (nai) {
- nai.linkProperties = newLp;
- }
- if (nai.everConnected) {
- updateLinkProperties(nai, oldLp);
- }
+ updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
}
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
@@ -5273,7 +5284,7 @@
notifyLockdownVpn(newNetwork);
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
- updateTcpBufferSizes(newNetwork);
+ updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
notifyIfacesChangedForNetworkStats();
}
@@ -5688,7 +5699,8 @@
}
handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
- updateLinkProperties(networkAgent, null);
+ updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
+ null);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
scheduleUnvalidatedPrompt(networkAgent);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 7ee3d3b..126bf65 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1236,7 +1236,8 @@
OsConstants.UDP_ENCAP,
OsConstants.UDP_ENCAP_ESPINUDP);
- mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
+ mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
+ new ParcelFileDescriptor(sockFd), callingUid);
if (port != 0) {
Log.v(TAG, "Binding to port " + port);
Os.bind(sockFd, INADDR_ANY, port);
@@ -1696,7 +1697,7 @@
mSrvConfig
.getNetdInstance()
.ipSecApplyTransportModeTransform(
- socket.getFileDescriptor(),
+ socket,
callingUid,
direction,
c.getSourceAddress(),
@@ -1715,7 +1716,7 @@
throws RemoteException {
mSrvConfig
.getNetdInstance()
- .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
+ .ipSecRemoveTransportModeTransform(socket);
}
/**
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index e5275e5..cc7bf33 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -19,7 +19,6 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -85,7 +84,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
@@ -114,7 +112,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
-import java.util.Objects;
import java.util.Set;
/**
@@ -3413,48 +3410,6 @@
}
}
- @Override
- public PendingIntent createManageLocationPermissionIntent(String packageName,
- String permission) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkArgument(permission.equals(Manifest.permission.ACCESS_FINE_LOCATION)
- || permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION)
- || permission.equals(Manifest.permission.ACCESS_BACKGROUND_LOCATION));
-
- int callingUid = Binder.getCallingUid();
- long token = Binder.clearCallingIdentity();
- try {
- String locProvider = getNetworkProviderPackage();
- if (locProvider == null) {
- return null;
- }
-
- PackageInfo locProviderInfo;
- try {
- locProviderInfo = mContext.getPackageManager().getPackageInfo(
- locProvider, PackageManager.MATCH_DIRECT_BOOT_AUTO);
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Could not resolve " + locProvider, e);
- return null;
- }
-
- if (locProviderInfo.applicationInfo.uid != callingUid) {
- throw new SecurityException("Only " + locProvider + " can call this API");
- }
-
- Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.putExtra(Intent.EXTRA_PERMISSION_NAME, permission);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- return PendingIntent.getActivity(mContext,
- Objects.hash(packageName, permission), intent,
- PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
private void log(String log) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, log);
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index c563ad2..fa3baba 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -94,6 +94,8 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
pw.print("Start time: ");
pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStats.getStartTimeMillis()));
+ pw.print("On battery time (ms): ");
+ pw.println(mStats.getBatteryTimeMillis());
final List<LooperStats.ExportedEntry> entries = mStats.getEntries();
entries.sort(Comparator
.comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid)
diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java
new file mode 100644
index 0000000..ccfac80
--- /dev/null
+++ b/services/core/java/com/android/server/RuntimeService.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.Binder;
+import android.service.runtime.DebugEntryProto;
+import android.service.runtime.RuntimeServiceInfoProto;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import libcore.timezone.TimeZoneDataFiles;
+import libcore.util.CoreLibraryDebug;
+import libcore.util.DebugInfo;
+
+import com.android.internal.util.DumpUtils;
+import com.android.timezone.distro.DistroException;
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.FileUtils;
+import com.android.timezone.distro.TimeZoneDistro;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * This service exists only as a "dumpsys" target which reports information about the status of the
+ * runtime and related libraries.
+ */
+public class RuntimeService extends Binder {
+
+ private static final String TAG = "RuntimeService";
+
+ private final Context mContext;
+
+ public RuntimeService(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
+ return;
+ }
+
+ boolean protoFormat = hasOption(args, "--proto");
+ ProtoOutputStream proto = null;
+
+ DebugInfo coreLibraryDebugInfo = CoreLibraryDebug.getDebugInfo();
+ addTimeZoneApkDebugInfo(coreLibraryDebugInfo);
+
+ if (protoFormat) {
+ proto = new ProtoOutputStream(fd);
+ reportTimeZoneInfoProto(coreLibraryDebugInfo, proto);
+ } else {
+ reportTimeZoneInfo(coreLibraryDebugInfo, pw);
+ }
+
+ if (protoFormat) {
+ proto.flush();
+ }
+ }
+
+ /** Returns {@code true} if {@code args} contains {@code arg}. */
+ private static boolean hasOption(String[] args, String arg) {
+ for (String opt : args) {
+ if (arg.equals(opt)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add information to {@link DebugInfo} about the time zone data supplied by the
+ * "Time zone updates via APK" feature.
+ */
+ private static void addTimeZoneApkDebugInfo(DebugInfo coreLibraryDebugInfo) {
+ // Add /data tz data set using the DistroVersion class (which libcore cannot use).
+ // This update mechanism will be removed after the time zone APEX is launched so this
+ // untidiness will disappear with it.
+ String debugKeyPrefix = "core_library.timezone.data_";
+ String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile(
+ TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
+ addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo);
+ }
+
+ /**
+ * Prints {@code coreLibraryDebugInfo} to {@code pw}.
+ *
+ * <p>If you change this method, make sure to modify
+ * {@link #reportTimeZoneInfoProto(DebugInfo, ProtoOutputStream)} as well.
+ */
+ private static void reportTimeZoneInfo(DebugInfo coreLibraryDebugInfo,
+ PrintWriter pw) {
+ pw.println("Core Library Debug Info: ");
+ for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
+ pw.print(debugEntry.getKey());
+ pw.print(": \"");
+ pw.print(debugEntry.getStringValue());
+ pw.println("\"");
+ }
+ }
+
+ /**
+ * Adds {@code coreLibraryDebugInfo} to {@code protoStream}.
+ *
+ * <p>If you change this method, make sure to modify
+ * {@link #reportTimeZoneInfo(DebugInfo, PrintWriter)}.
+ */
+ private static void reportTimeZoneInfoProto(
+ DebugInfo coreLibraryDebugInfo, ProtoOutputStream protoStream) {
+ for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
+ long entryToken = protoStream.start(RuntimeServiceInfoProto.DEBUG_ENTRY);
+ protoStream.write(DebugEntryProto.KEY, debugEntry.getKey());
+ protoStream.write(DebugEntryProto.STRING_VALUE, debugEntry.getStringValue());
+ protoStream.end(entryToken);
+ }
+ }
+
+ /**
+ * Adds version information to {@code debugInfo} from the distro_version file that may exist
+ * at {@code distroVersionFileName}. If the file does not exist or cannot be read this is
+ * reported as debug information too.
+ */
+ private static void addDistroVersionDebugInfo(String distroVersionFileName,
+ String debugKeyPrefix, DebugInfo debugInfo) {
+ File file = new File(distroVersionFileName);
+ String statusKey = debugKeyPrefix + "status";
+ if (file.exists()) {
+ try {
+ byte[] versionBytes =
+ FileUtils.readBytes(file, DistroVersion.DISTRO_VERSION_FILE_LENGTH);
+ DistroVersion distroVersion = DistroVersion.fromBytes(versionBytes);
+ String formatVersionString = distroVersion.formatMajorVersion + "."
+ + distroVersion.formatMinorVersion;
+ debugInfo.addStringEntry(statusKey, "OK")
+ .addStringEntry(debugKeyPrefix + "formatVersion", formatVersionString)
+ .addStringEntry(debugKeyPrefix + "rulesVersion",
+ distroVersion.rulesVersion)
+ .addStringEntry(debugKeyPrefix + "revision",
+ distroVersion.revision);
+ } catch (IOException | DistroException e) {
+ debugInfo.addStringEntry(statusKey, "ERROR");
+ debugInfo.addStringEntry(debugKeyPrefix + "exception_class",
+ e.getClass().getName());
+ debugInfo.addStringEntry(debugKeyPrefix + "exception_msg", e.getMessage());
+ logMessage("Error reading " + file, e);
+ }
+ } else {
+ debugInfo.addStringEntry(statusKey, "NOT_FOUND");
+ }
+ }
+
+ private static void logMessage(String msg, Throwable t) {
+ Slog.v(TAG, msg, t);
+ }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 923ac00..390126c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -99,6 +99,7 @@
import android.os.storage.VolumeRecord;
import android.provider.MediaStore;
import android.provider.Settings;
+import android.sysprop.VoldProperties;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -183,8 +184,7 @@
private static final String ZRAM_ENABLED_PROPERTY =
"persist.sys.zram_enabled";
- private static final boolean ENABLE_ISOLATED_STORAGE = SystemProperties
- .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+ private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -1016,7 +1016,7 @@
// On an encrypted device we can't see system properties yet, so pull
// the system locale out of the mount service.
- if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
+ if ("".equals(VoldProperties.encrypt_progress().orElse(""))) {
copyLocaleFromMountService();
}
}
@@ -2686,24 +2686,35 @@
class AppFuseMountScope extends AppFuseBridge.MountScope {
boolean opened = false;
- public AppFuseMountScope(int uid, int pid, int mountId) {
- super(uid, pid, mountId);
+ public AppFuseMountScope(int uid, int mountId) {
+ super(uid, mountId);
}
@Override
public ParcelFileDescriptor open() throws NativeDaemonConnectorException {
try {
return new ParcelFileDescriptor(
- mVold.mountAppFuse(uid, Process.myPid(), mountId));
+ mVold.mountAppFuse(uid, mountId));
} catch (Exception e) {
throw new NativeDaemonConnectorException("Failed to mount", e);
}
}
@Override
+ public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
+ throws NativeDaemonConnectorException {
+ try {
+ return new ParcelFileDescriptor(
+ mVold.openAppFuseFile(uid, mountId, fileId, flags));
+ } catch (Exception e) {
+ throw new NativeDaemonConnectorException("Failed to open", e);
+ }
+ }
+
+ @Override
public void close() throws Exception {
if (opened) {
- mVold.unmountAppFuse(uid, Process.myPid(), mountId);
+ mVold.unmountAppFuse(uid, mountId);
opened = false;
}
}
@@ -2713,7 +2724,6 @@
public @Nullable AppFuseMount mountProxyFileDescriptorBridge() {
Slog.v(TAG, "mountProxyFileDescriptorBridge");
final int uid = Binder.getCallingUid();
- final int pid = Binder.getCallingPid();
while (true) {
synchronized (mAppFuseLock) {
@@ -2727,7 +2737,7 @@
final int name = mNextAppFuseName++;
try {
return new AppFuseMount(
- name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name)));
+ name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, name)));
} catch (FuseUnavailableMountException e) {
if (newlyCreated) {
// If newly created bridge fails, it's a real error.
@@ -2748,14 +2758,13 @@
public @Nullable ParcelFileDescriptor openProxyFileDescriptor(
int mountId, int fileId, int mode) {
Slog.v(TAG, "mountProxyFileDescriptor");
- final int pid = Binder.getCallingPid();
try {
synchronized (mAppFuseLock) {
if (mAppFuseBridge == null) {
Slog.e(TAG, "FuseBridge has not been created");
return null;
}
- return mAppFuseBridge.openFile(pid, mountId, fileId, mode);
+ return mAppFuseBridge.openFile(mountId, fileId, mode);
}
} catch (FuseUnavailableMountException | InterruptedException error) {
Slog.v(TAG, "The mount point has already been invalid", error);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f0b472b..a2cbfaa 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -47,6 +47,7 @@
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import android.util.LocalLog;
import android.util.StatsLog;
@@ -1664,6 +1665,14 @@
}
@Override
+ public void notifyEmergencyNumberList(List<EmergencyNumber> emergencyNumberList) {
+ // TODO checkPermission, modify Listener constent documentation
+ // TODO implement multisim emergency number list update in listener
+ // TODO implement PhoneStateListenerTest
+ }
+
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
new file mode 100644
index 0000000..629e882
--- /dev/null
+++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package com.android.server;
+
+import android.app.ActivityThread;
+import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.util.Slog;
+
+/**
+ * Receiver responsible for updating the wallpaper when the device
+ * configuration has changed.
+ *
+ * @hide
+ */
+public class WallpaperUpdateReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "WallpaperUpdateReceiver";
+ private static final boolean DEBUG = false;
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ if (DEBUG) Slog.d(TAG, "onReceive: " + intent);
+
+ if (intent != null && Intent.ACTION_DEVICE_CUSTOMIZATION_READY.equals(intent.getAction())) {
+ AsyncTask.execute(this::updateWallpaper);
+ }
+ }
+
+ private void updateWallpaper() {
+ try {
+ ActivityThread currentActivityThread = ActivityThread.currentActivityThread();
+ Context uiContext = currentActivityThread.getSystemUiContext();
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(uiContext);
+ if (DEBUG) Slog.d(TAG, "Set customized default_wallpaper.");
+ Bitmap blank = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+ // set a blank wallpaper to force a redraw of default_wallpaper
+ wallpaperManager.setBitmap(blank);
+ wallpaperManager.setResource(com.android.internal.R.drawable.default_wallpaper);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to customize system wallpaper." + e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index d1b56e9..e80e9e1 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -105,6 +105,7 @@
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.graphics.allocator@2.0::IAllocator",
"android.hardware.graphics.composer@2.1::IComposer",
+ "android.hardware.health@2.0::IHealth",
"android.hardware.media.omx@1.0::IOmx",
"android.hardware.media.omx@1.0::IOmxStore",
"android.hardware.sensors@1.0::ISensors",
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 5c77f0a..8571ae6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
@@ -26,8 +28,6 @@
import java.io.PrintWriter;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
-
/**
* Settings constants that can modify the activity manager's behavior.
*/
@@ -222,6 +222,10 @@
// Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
volatile boolean mFlagActivityStartsLoggingEnabled;
+ // Indicates whether the background activity starts is enabled.
+ // Controlled by Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED
+ volatile boolean mFlagBackgroundActivityStartsEnabled;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -256,6 +260,10 @@
private static final Uri ACTIVITY_STARTS_LOGGING_ENABLED_URI = Settings.Global.getUriFor(
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED);
+ private static final Uri BACKGROUND_ACTIVITY_STARTS_ENABLED_URI =
+ Settings.Global.getUriFor(
+ Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED);
+
public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
super(handler);
mService = service;
@@ -266,8 +274,10 @@
mResolver = resolver;
mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this);
mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this);
+ mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this);
updateConstants();
updateActivityStartsLoggingEnabled();
+ updateBackgroundActivityStartsEnabled();
}
public void setOverrideMaxCachedProcesses(int value) {
@@ -290,6 +300,8 @@
updateConstants();
} else if (ACTIVITY_STARTS_LOGGING_ENABLED_URI.equals(uri)) {
updateActivityStartsLoggingEnabled();
+ } else if (BACKGROUND_ACTIVITY_STARTS_ENABLED_URI.equals(uri)) {
+ updateBackgroundActivityStartsEnabled();
}
}
@@ -373,6 +385,11 @@
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1;
}
+ private void updateBackgroundActivityStartsEnabled() {
+ mFlagBackgroundActivityStartsEnabled = Settings.Global.getInt(mResolver,
+ Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED, 1) == 1;
+ }
+
private void updateMaxCachedProcesses() {
CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5f9e349..8842f41 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
import android.os.WorkSource;
import android.os.storage.StorageManager;
import android.provider.Settings;
+import android.sysprop.VoldProperties;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.SuggestionSpan;
@@ -328,6 +329,7 @@
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleController;
+import com.android.server.DisplayThread;
import com.android.server.IntentResolver;
import com.android.server.IoThread;
import com.android.server.LocalServices;
@@ -665,16 +667,50 @@
final class PidMap {
private final SparseArray<ProcessRecord> mPidMap = new SparseArray<>();
+ /**
+ * Puts the process record in the map.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
void put(int key, ProcessRecord value) {
- mPidMap.put(key, value);
+ synchronized (this) {
+ mPidMap.put(key, value);
+ }
mAtmInternal.onProcessMapped(key, value.getWindowProcessController());
}
+ /**
+ * Removes the process record from the map.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
void remove(int pid) {
- mPidMap.remove(pid);
+ synchronized (this) {
+ mPidMap.remove(pid);
+ }
mAtmInternal.onProcessUnMapped(pid);
}
+ /**
+ * Removes the process record from the map if it has a thread.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
+ boolean removeIfNoThread(int pid) {
+ boolean removed = false;
+ synchronized (this) {
+ final ProcessRecord app = get(pid);
+ if (app != null && app.thread == null) {
+ mPidMap.remove(pid);
+ removed = true;
+ }
+ }
+ if (removed) {
+ mAtmInternal.onProcessUnMapped(pid);
+ }
+ return removed;
+ }
+
ProcessRecord get(int pid) {
return mPidMap.get(pid);
}
@@ -1888,9 +1924,7 @@
app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.put(app.pid, app);
- }
+ mPidsSelfLocked.put(app.pid, app);
mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
@@ -2262,7 +2296,8 @@
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mActivityTaskManager = atm;
- mActivityTaskManager.setActivityManagerService(mIntentFirewall, mPendingIntentController);
+ mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController,
+ DisplayThread.get().getLooper());
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mProcessCpuThread = new Thread("CpuTracker") {
@@ -4252,14 +4287,7 @@
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
- boolean gone = false;
- synchronized (mPidsSelfLocked) {
- ProcessRecord knownApp = mPidsSelfLocked.get(pid);
- if (knownApp != null && knownApp.thread == null) {
- mPidsSelfLocked.remove(pid);
- gone = true;
- }
- }
+ boolean gone = mPidsSelfLocked.removeIfNoThread(pid);
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
@@ -4372,7 +4400,6 @@
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
- app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
app.forcingToImportant = null;
@@ -4579,6 +4606,10 @@
profilerInfo.closeFd();
profilerInfo = null;
}
+
+ // Make app active after binding application or client may be running requests (e.g
+ // starting activities) before it is ready.
+ app.makeActive(thread, mProcessStats);
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
mProcessList.updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
@@ -4777,8 +4808,8 @@
SystemProperties.set("sys.boot_completed", "1");
// And trigger dev.bootcomplete if we are not showing encryption progress
- if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
- || "".equals(SystemProperties.get("vold.encrypt_progress"))) {
+ if (!"trigger_restart_min_framework".equals(VoldProperties.decrypt().orElse(""))
+ || "".equals(VoldProperties.encrypt_progress().orElse(""))) {
SystemProperties.set("dev.bootcomplete", "1");
}
mUserController.sendBootCompleted(
@@ -13108,11 +13139,8 @@
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
- boolean removed;
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.remove(app.pid);
- mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
+ mPidsSelfLocked.remove(app.pid);
+ mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
@@ -18909,7 +18937,6 @@
if (memoryStat == null) {
continue;
}
- // TODO(rslawik): Delete RSS high-water mark field.
ProcessMemoryState processMemoryState =
new ProcessMemoryState(uid,
r.processName,
@@ -18919,7 +18946,6 @@
memoryStat.rssInBytes,
memoryStat.cacheInBytes,
memoryStat.swapInBytes,
- memoryStat.rssHighWatermarkInBytes,
memoryStat.startTimeNanos);
processMemoryStates.add(processMemoryState);
}
@@ -19201,6 +19227,10 @@
return mConstants.mFlagActivityStartsLoggingEnabled;
}
+ public boolean isBackgroundActivityStartsEnabled() {
+ return mConstants.mFlagBackgroundActivityStartsEnabled;
+ }
+
public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
synchronized(ActivityManagerService.this) {
ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing
@@ -19341,7 +19371,7 @@
@Override
public boolean isAppStorageSandboxed(int pid, int uid) {
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
return false;
}
if (uid == SHELL_UID || uid == ROOT_UID) {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 2541352..24543b7 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -25,10 +25,12 @@
import android.net.wifi.WifiActivityEnergyInfo;
import android.os.BatteryStats;
import android.os.Parcelable;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
+import android.os.ThreadLocalWorkSource;
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.IntArray;
@@ -43,11 +45,9 @@
import libcore.util.EmptyArray;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -74,7 +74,12 @@
private final ScheduledExecutorService mExecutorService =
Executors.newSingleThreadScheduledExecutor(
(ThreadFactory) r -> {
- Thread t = new Thread(r, "batterystats-worker");
+ Thread t = new Thread(
+ () -> {
+ ThreadLocalWorkSource.setUid(Process.myUid());
+ r.run();
+ },
+ "batterystats-worker");
t.setPriority(Thread.NORM_PRIORITY);
return t;
});
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a0977be..8c39d75 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -58,8 +58,9 @@
/**
* BROADCASTS
*
- * We keep two broadcast queues and associated bookkeeping, one for those at
- * foreground priority, and one for normal (background-priority) broadcasts.
+ * We keep three broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts, and one to
+ * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
*/
public final class BroadcastQueue {
private static final String TAG = "BroadcastQueue";
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index cc3da1c..90fe30c 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -49,13 +49,8 @@
private static final boolean DEVICE_HAS_PER_APP_MEMCG =
SystemProperties.getBoolean("ro.config.per_app_memcg", false);
- /** Path to check if device has memcg */
- private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
/** Path to memory stat file for logging app start memory state */
private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
- /** Path to memory max usage file for logging app memory state */
- private static final String MEMORY_MAX_USAGE_FILE_FMT =
- "/dev/memcg/apps/uid_%d/pid_%d/memory.max_usage_in_bytes";
/** Path to procfs stat file for logging app start memory state */
private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
/** Path to procfs status file for logging app memory state */
@@ -98,14 +93,7 @@
@Nullable
static MemoryStat readMemoryStatFromMemcg(int uid, int pid) {
final String statPath = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
- MemoryStat stat = parseMemoryStatFromMemcg(readFileContents(statPath));
- if (stat == null) {
- return null;
- }
- String maxUsagePath = String.format(Locale.US, MEMORY_MAX_USAGE_FILE_FMT, uid, pid);
- stat.rssHighWatermarkInBytes = parseMemoryMaxUsageFromMemCg(
- readFileContents(maxUsagePath));
- return stat;
+ return parseMemoryStatFromMemcg(readFileContents(statPath));
}
/**
@@ -116,12 +104,7 @@
@Nullable
public static MemoryStat readMemoryStatFromProcfs(int pid) {
final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
- MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath));
- if (stat == null) {
- return null;
- }
- stat.rssHighWatermarkInBytes = readRssHighWaterMarkFromProcfs(pid);
- return stat;
+ return parseMemoryStatFromProcfs(readFileContents(statPath));
}
/**
@@ -185,19 +168,6 @@
return memoryStat;
}
- @VisibleForTesting
- static long parseMemoryMaxUsageFromMemCg(String memoryMaxUsageContents) {
- if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) {
- return 0;
- }
- try {
- return Long.parseLong(memoryMaxUsageContents);
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Failed to parse value", e);
- return 0;
- }
- }
-
/**
* Parses relevant statistics out from the contents of the /proc/pid/stat file in procfs.
*/
@@ -258,8 +228,6 @@
public long cacheInBytes;
/** Number of bytes of swap usage */
public long swapInBytes;
- /** Number of bytes of peak anonymous and swap cache memory */
- public long rssHighWatermarkInBytes;
/** Device time when the processes started. */
public long startTimeNanos;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4b19398..62f1009 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -27,7 +27,6 @@
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
-import static android.os.storage.StorageManager.PROP_ISOLATED_STORAGE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -73,6 +72,7 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.text.TextUtils;
import android.util.EventLog;
@@ -1246,10 +1246,8 @@
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
checkSlow(startTime, "startProcess: removing from pids map");
- synchronized (mService.mPidsSelfLocked) {
- mService.mPidsSelfLocked.remove(app.pid);
- mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
+ mService.mPidsSelfLocked.remove(app.pid);
+ mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
checkSlow(startTime, "startProcess: done removing from pids map");
app.setPid(0);
}
@@ -1281,8 +1279,7 @@
final IPackageManager pm = AppGlobals.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName,
MATCH_DIRECT_BOOT_AUTO, app.userId);
- if (SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)
- && mountExtStorageFull) {
+ if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) {
mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
} else {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
@@ -1768,8 +1765,8 @@
mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
true /*replacingPid*/);
}
+ mService.mPidsSelfLocked.put(pid, app);
synchronized (mService.mPidsSelfLocked) {
- mService.mPidsSelfLocked.put(pid, app);
if (!procAttached) {
Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
@@ -1929,10 +1926,8 @@
.pendingStart)) {
int pid = app.pid;
if (pid > 0) {
- synchronized (mService.mPidsSelfLocked) {
- mService.mPidsSelfLocked.remove(pid);
- mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
+ mService.mPidsSelfLocked.remove(pid);
+ mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index a5848ca..4c4a090 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -65,6 +65,7 @@
// permission in the corresponding .te file your feature belongs to.
@VisibleForTesting
static final String[] sGlobalSettings = new String[] {
+ Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
};
@VisibleForTesting
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index b817669..48b4145 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -1,17 +1,6 @@
{
"presubmit": [
{
- "name": "CtsActivityManagerDeviceTestCases",
- "options": [
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "android.support.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "CtsActivityManagerDeviceSdk25TestCases",
"options": [
{
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 67d27c9..6cde4ad 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -649,6 +649,9 @@
private String mEnabledSurroundFormats;
private boolean mSurroundModeChanged;
+ @GuardedBy("mSettingsLock")
+ private int mAssistantUid;
+
// Intent "extra" data keys.
public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
public static final String CONNECT_INTENT_KEY_STATE = "state";
@@ -1079,6 +1082,10 @@
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, forDock);
sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
sendEnabledSurroundFormats(mContentResolver, true);
+ updateAssistantUId(true);
+ }
+ synchronized (mAccessibilityServiceUidsLock) {
+ AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
}
synchronized (mHdmiClientLock) {
if (mHdmiManager != null && mHdmiTvClient != null) {
@@ -1404,6 +1411,39 @@
}
}
+ @GuardedBy("mSettingsLock")
+ private void updateAssistantUId(boolean forceUpdate) {
+ int assistantUid = 0;
+
+ // Consider assistants in the following order of priority:
+ // 1) voice interaction service
+ // 2) assistant
+ String assistantName = Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.VOICE_INTERACTION_SERVICE, UserHandle.USER_CURRENT);
+ if (TextUtils.isEmpty(assistantName)) {
+ assistantName = Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT);
+ }
+ if (!TextUtils.isEmpty(assistantName)) {
+ String packageName = ComponentName.unflattenFromString(assistantName).getPackageName();
+ if (!TextUtils.isEmpty(packageName)) {
+ try {
+ assistantUid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG,
+ "updateAssistantUId() could not find UID for package: " + packageName);
+ }
+ }
+ }
+
+ if (assistantUid != mAssistantUid || forceUpdate) {
+ AudioSystem.setAssistantUid(assistantUid);
+ mAssistantUid = assistantUid;
+ }
+ }
+
private void readPersistedSettings() {
final ContentResolver cr = mContentResolver;
@@ -1447,6 +1487,7 @@
readDockAudioSettings(cr);
sendEncodedSurroundMode(cr, "readPersistedSettings");
sendEnabledSurroundFormats(cr, true);
+ updateAssistantUId(true);
}
mMuteAffectedStreams = System.getIntForUser(cr,
@@ -5811,6 +5852,9 @@
mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this);
+
+ mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
}
@Override
@@ -5832,6 +5876,7 @@
updateMasterMono(mContentResolver);
updateEncodedSurroundOutput();
sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged);
+ updateAssistantUId(false);
}
}
@@ -7658,6 +7703,7 @@
mAccessibilityServiceUids = uids.toArray();
}
}
+ AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 2c2d404..eaa7a83 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -19,19 +19,11 @@
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.security.KeyStore;
-import android.text.TextUtils;
import android.util.Slog;
-import com.android.internal.statusbar.IStatusBarService;
-
import java.util.ArrayList;
/**
@@ -39,88 +31,15 @@
*/
public abstract class AuthenticationClient extends ClientMonitor {
private long mOpId;
- private Handler mHandler;
public abstract int handleFailedAttempt();
public abstract void resetFailedAttempts();
- public abstract String getErrorString(int error, int vendorCode);
- public abstract String getAcquiredString(int acquireInfo, int vendorCode);
- /**
- * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
- */
- public abstract int getBiometricType();
public static final int LOCKOUT_NONE = 0;
public static final int LOCKOUT_TIMED = 1;
public static final int LOCKOUT_PERMANENT = 2;
private final boolean mRequireConfirmation;
- // Callback mechanism received from the client
- // (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient)
- private IBiometricPromptReceiver mDialogReceiverFromClient;
- private Bundle mBundle;
- private IStatusBarService mStatusBarService;
- private boolean mInLockout;
- private TokenEscrow mEscrow;
- protected boolean mDialogDismissed;
-
- /**
- * Container that holds the identifier and authToken. For biometrics that require user
- * confirmation, these should not be sent to their final destinations until the user confirms.
- */
- class TokenEscrow {
- final BiometricAuthenticator.Identifier mIdentifier;
- final ArrayList<Byte> mToken;
-
- TokenEscrow(BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) {
- mIdentifier = identifier;
- mToken = token;
- }
-
- BiometricAuthenticator.Identifier getIdentifier() {
- return mIdentifier;
- }
-
- ArrayList<Byte> getToken() {
- return mToken;
- }
- }
-
- // Receives events from SystemUI and handles them before forwarding them to BiometricDialog
- protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
- @Override // binder call
- public void onDialogDismissed(int reason) {
- if (mBundle != null && mDialogReceiverFromClient != null) {
- try {
- if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Positive button is used by passive modalities as a "confirm" button,
- // do not send to client
- mDialogReceiverFromClient.onDialogDismissed(reason);
- }
- if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
- onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
- 0 /* vendorCode */);
- } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Have the service send the token to KeyStore, and send onAuthenticated
- // to the application.
- if (mEscrow != null) {
- if (DEBUG) Slog.d(getLogTag(), "Confirmed");
- addTokenToKeyStore(mEscrow.getToken());
- notifyClientAuthenticationSucceeded(mEscrow.getIdentifier());
- mEscrow = null;
- onAuthenticationConfirmed();
- } else {
- Slog.e(getLogTag(), "Escrow is null!!!");
- }
- }
- mDialogDismissed = true;
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception", e);
- }
- stop(true /* initiatedByClient */);
- }
- }
- };
/**
* This method is called when authentication starts.
@@ -133,25 +52,13 @@
*/
public abstract void onStop();
- /**
- * This method is called when biometric authentication was confirmed by the user. The client
- * should be removed.
- */
- public abstract void onAuthenticationConfirmed();
-
public AuthenticationClient(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
- boolean requireConfirmation) {
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
- restricted, owner);
+ restricted, owner, cookie);
mOpId = opId;
- mBundle = bundle;
- mDialogReceiverFromClient = dialogReceiver;
- mStatusBarService = statusBarService;
- mHandler = new Handler(Looper.getMainLooper());
mRequireConfirmation = requireConfirmation;
}
@@ -164,175 +71,99 @@
stop(false /* initiatedByClient */);
}
- @Override
- public boolean onAcquired(int acquiredInfo, int vendorCode) {
- // If the dialog is showing, the client doesn't need to receive onAcquired messages.
- if (mBundle != null) {
- try {
- if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- mStatusBarService.onBiometricHelp(getAcquiredString(acquiredInfo, vendorCode));
- }
- return false; // acquisition continues
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception when sending acquired message", e);
- return true; // client failed
- } finally {
- // Good scans will keep the device awake
- if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- notifyUserActivity();
- }
- }
- } else {
- return super.onAcquired(acquiredInfo, vendorCode);
- }
- }
-
- @Override
- public boolean onError(long deviceId, int error, int vendorCode) {
- if (mDialogDismissed) {
- // If user cancels authentication, the application has already received the
- // ERROR_USER_CANCELED message from onDialogDismissed()
- // and stopped the biometric hardware, so there is no need to send a
- // ERROR_CANCELED message.
- return true;
- }
- if (mBundle != null && error != BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) {
- try {
- mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Remote exception when sending error", e);
- }
- }
- return super.onError(deviceId, error, vendorCode);
- }
-
- public void setTitleIfEmpty(CharSequence title) {
- if (TextUtils.isEmpty(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
- mBundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
- }
- }
-
public boolean isBiometricPrompt() {
- return mBundle != null;
+ return getCookie() != 0;
}
- private void notifyClientAuthenticationSucceeded(BiometricAuthenticator.Identifier identifier)
- throws RemoteException {
- final BiometricServiceBase.ServiceListener listener = getListener();
- // Explicitly have if/else here to make it super obvious in case the code is
- // touched in the future.
- if (!getIsRestricted()) {
- listener.onAuthenticationSucceeded(
- getHalDeviceId(), identifier, getTargetUserId());
- } else {
- listener.onAuthenticationSucceeded(
- getHalDeviceId(), null, getTargetUserId());
- }
- }
-
- private void addTokenToKeyStore(ArrayList<Byte> token) {
- // Send the token to KeyStore
- final byte[] byteToken = new byte[token.size()];
- for (int i = 0; i < token.size(); i++) {
- byteToken[i] = token.get(i);
- }
- KeyStore.getInstance().addAuthToken(byteToken);
+ public boolean getRequireConfirmation() {
+ return mRequireConfirmation;
}
@Override
public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> token) {
- if (authenticated) {
- mAlreadyDone = true;
- if (mRequireConfirmation) {
- // Store the token so it can be sent to keystore after the user presses confirm
- mEscrow = new TokenEscrow(identifier, token);
- } else {
- addTokenToKeyStore(token);
- }
- }
+ final BiometricServiceBase.ServiceListener listener = getListener();
+ mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
boolean result = false;
- // If the biometric dialog is showing, notify authentication succeeded
- if (mBundle != null) {
- try {
- if (authenticated) {
- mStatusBarService.onBiometricAuthenticated();
- } else {
- mStatusBarService.onBiometricHelp(getContext().getResources().getString(
- com.android.internal.R.string.biometric_not_recognized));
+ try {
+ if (authenticated) {
+ mAlreadyDone = true;
+ if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + getOwnerString()
+ + ", ID:" + identifier.getBiometricId()
+ + ", isBP: " + isBiometricPrompt()
+ + ", listener: " + listener
+ + ", requireConfirmation: " + mRequireConfirmation);
+ if (listener != null) {
+ vibrateSuccess();
}
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Failed to notify Authenticated:", e);
- }
- }
+ result = true;
+ resetFailedAttempts();
+ onStop();
- final BiometricServiceBase.ServiceListener listener = getListener();
- if (listener != null) {
- try {
- mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
- if (!authenticated) {
- listener.onAuthenticationFailed(getHalDeviceId());
- } else {
- if (DEBUG) {
- Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
- + ", id=" + identifier.getBiometricId());
- }
- if (!mRequireConfirmation) {
- notifyClientAuthenticationSucceeded(identifier);
- }
+ final byte[] byteToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ byteToken[i] = token.get(i);
}
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "Failed to notify Authenticated:", e);
- result = true; // client failed
- }
- } else {
- result = true; // client not listening
- }
- if (!authenticated) {
- if (listener != null) {
- vibrateError();
- }
- // allow system-defined limit of number of attempts before giving up
- int lockoutMode = handleFailedAttempt();
- if (lockoutMode != LOCKOUT_NONE) {
- try {
- mInLockout = true;
- Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" +
- lockoutMode + ")");
+ if (isBiometricPrompt() && listener != null) {
+ // BiometricService will add the token to keystore
+ listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
+ } else if (!isBiometricPrompt() && listener != null) {
+ KeyStore.getInstance().addAuthToken(byteToken);
+ try {
+ // Explicitly have if/else here to make it super obvious in case the code is
+ // touched in the future.
+ if (!getIsRestricted()) {
+ listener.onAuthenticationSucceeded(
+ getHalDeviceId(), identifier, getTargetUserId());
+ } else {
+ listener.onAuthenticationSucceeded(
+ getHalDeviceId(), null, getTargetUserId());
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "Remote exception", e);
+ }
+ } else {
+ // Client not listening
+ Slog.w(getLogTag(), "Client not listening");
+ result = true;
+ }
+ } else {
+ if (listener != null) {
+ vibrateError();
+ }
+ // Allow system-defined limit of number of attempts before giving up
+ final int lockoutMode = handleFailedAttempt();
+ if (lockoutMode != LOCKOUT_NONE) {
+ Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
+ + lockoutMode + ")");
stop(false);
- int errorCode = lockoutMode == LOCKOUT_TIMED ?
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
- BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-
- // Send the lockout message to the system dialog
- if (mBundle != null) {
- mStatusBarService.onBiometricError(
- getErrorString(errorCode, 0 /* vendorCode */));
- mHandler.postDelayed(() -> {
- try {
- listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "RemoteException while sending error");
- }
- }, BiometricPrompt.HIDE_DIALOG_DELAY);
- } else {
- listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+ final int errorCode = lockoutMode == LOCKOUT_TIMED
+ ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (listener != null) {
+ listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */,
+ getCookie());
}
- } catch (RemoteException e) {
- Slog.w(getLogTag(), "Failed to notify lockout:", e);
+ } else {
+ // Don't send onAuthenticationFailed if we're in lockout, it causes a
+ // janky UI on Keyguard/BiometricPrompt since "authentication failed"
+ // will show briefly and be replaced by "device locked out" message.
+ if (listener != null) {
+ if (isBiometricPrompt()) {
+ listener.onAuthenticationFailedInternal(getCookie(),
+ getRequireConfirmation());
+ } else {
+ listener.onAuthenticationFailed(getHalDeviceId());
+ }
+ }
}
+ result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
}
- result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
- } else {
- if (listener != null) {
- vibrateSuccess();
- }
- // we have a valid biometric that doesn't require confirmation, done
- result |= !mRequireConfirmation;
- resetFailedAttempts();
- onStop();
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "Remote exception", e);
+ result = true;
}
return result;
}
@@ -353,16 +184,6 @@
return result;
}
if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
-
- // If authenticating with system dialog, show the dialog
- if (mBundle != null) {
- try {
- mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
- getBiometricType(), mRequireConfirmation, getTargetUserId());
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Unable to show biometric dialog", e);
- }
- }
} catch (RemoteException e) {
Slog.e(getLogTag(), "startAuthentication failed", e);
return ERROR_ESRCH;
@@ -390,18 +211,6 @@
} catch (RemoteException e) {
Slog.e(getLogTag(), "stopAuthentication failed", e);
return ERROR_ESRCH;
- } finally {
- // If the user already cancelled authentication (via some interaction with the
- // dialog, we do not need to hide it since it's already hidden.
- // If the device is in lockout, don't hide the dialog - it will automatically hide
- // after BiometricPrompt.HIDE_DIALOG_DELAY
- if (mBundle != null && !mDialogDismissed && !mInLockout) {
- try {
- mStatusBarService.hideBiometricDialog();
- } catch (RemoteException e) {
- Slog.e(getLogTag(), "Unable to hide biometric dialog", e);
- }
- }
}
mAlreadyCancelled = true;
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 5f09189..add55ea 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -19,9 +19,17 @@
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
import android.app.UserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
@@ -32,9 +40,9 @@
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
@@ -50,14 +58,21 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.security.KeyStore;
+import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Random;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
@@ -66,32 +81,10 @@
private static final String TAG = "BiometricService";
- /**
- * No biometric methods or nothing has been enrolled.
- * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
- * modalities when calling authenticate().
- */
- private static final int BIOMETRIC_NONE = 0;
-
- /**
- * Constant representing fingerprint.
- */
- private static final int BIOMETRIC_FINGERPRINT = 1 << 0;
-
- /**
- * Constant representing iris.
- */
- private static final int BIOMETRIC_IRIS = 1 << 1;
-
- /**
- * Constant representing face.
- */
- private static final int BIOMETRIC_FACE = 1 << 2;
-
private static final int[] FEATURE_ID = {
- BIOMETRIC_FINGERPRINT,
- BIOMETRIC_IRIS,
- BIOMETRIC_FACE
+ TYPE_FINGERPRINT,
+ TYPE_IRIS,
+ TYPE_FACE
};
private final AppOpsManager mAppOps;
@@ -242,10 +235,367 @@
*/
private final class BiometricServiceWrapper extends IBiometricService.Stub {
+ /**
+ * Authentication either just called and we have not transitioned to the CALLED state, or
+ * authentication terminated (success or error).
+ */
+ private static final int STATE_AUTH_IDLE = 0;
+ /**
+ * Authentication was called and we are waiting for the <Biometric>Services to return their
+ * cookies before starting the hardware and showing the BiometricPrompt.
+ */
+ private static final int STATE_AUTH_CALLED = 1;
+ /**
+ * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
+ */
+ private static final int STATE_AUTH_STARTED = 2;
+ /**
+ * Authentication is paused, waiting for the user to press "try again" button. Since the
+ * try again button requires us to cancel authentication, this represents the state where
+ * ERROR_CANCELED is not received yet.
+ */
+ private static final int STATE_AUTH_PAUSED = 3;
+ /**
+ * Same as above, except the ERROR_CANCELED has been received.
+ */
+ private static final int STATE_AUTH_PAUSED_CANCELED = 4;
+ /**
+ * Authentication is successful, but we're waiting for the user to press "confirm" button.
+ */
+ private static final int STATE_AUTH_PENDING_CONFIRM = 5;
+
+ final class AuthSession {
+ // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
+ // <Biometric>Services before we can start authenticating. Pairs that have been returned
+ // are moved to mModalitiesMatched.
+ final HashMap<Integer, Integer> mModalitiesWaiting;
+ // Pairs that have been matched.
+ final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>();
+
+ // The following variables are passed to authenticateInternal, which initiates the
+ // appropriate <Biometric>Services.
+ final IBinder mToken;
+ final long mSessionId;
+ final int mUserId;
+ // Original receiver from BiometricPrompt.
+ final IBiometricServiceReceiver mClientReceiver;
+ final String mOpPackageName;
+ // Info to be shown on BiometricDialog when all cookies are returned.
+ final Bundle mBundle;
+ final int mCallingUid;
+ final int mCallingPid;
+ final int mCallingUserId;
+ // Continue authentication with the same modality/modalities after "try again" is
+ // pressed
+ final int mModality;
+
+ // The current state, which can be either idle, called, or started
+ private int mState = STATE_AUTH_IDLE;
+ // For explicit confirmation, do not send to keystore until the user has confirmed
+ // the authentication.
+ byte[] mTokenEscrow;
+
+ AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
+ int userId, IBiometricServiceReceiver receiver, String opPackageName,
+ Bundle bundle, int callingUid, int callingPid, int callingUserId,
+ int modality) {
+ mModalitiesWaiting = modalities;
+ mToken = token;
+ mSessionId = sessionId;
+ mUserId = userId;
+ mClientReceiver = receiver;
+ mOpPackageName = opPackageName;
+ mBundle = bundle;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ mCallingUserId = callingUserId;
+ mModality = modality;
+ }
+
+ boolean containsCookie(int cookie) {
+ if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
+ return true;
+ }
+ if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ final List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (mCurrentAuthSession != null
+ && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)
+ && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ // We only care about this state, since <Biometric>Service will
+ // cancel any client that's still in STATE_AUTH_STARTED
+ mStatusBarService.hideBiometricDialog();
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_canceled)
+ );
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to get running tasks", e);
+ }
+ }
+ }
+
+ private final IActivityTaskManager mActivityTaskManager = getContext().getSystemService(
+ ActivityTaskManager.class).getService();
+ private final IStatusBarService mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ private final BiometricTaskStackListener mTaskStackListener =
+ new BiometricTaskStackListener();
+ private final Random mRandom = new Random();
+
+ // The current authentication session, null if idle/done. We need to track both the current
+ // and pending sessions since errors may be sent to either.
+ private AuthSession mCurrentAuthSession;
+ private AuthSession mPendingAuthSession;
+
+ // Wrap the client's receiver so we can do things with the BiometricDialog first
+ private final IBiometricServiceReceiverInternal mInternalReceiver =
+ new IBiometricServiceReceiverInternal.Stub() {
+ @Override
+ public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ try {
+ if (!requireConfirmation) {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ KeyStore.getInstance().addAuthToken(token);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } else {
+ // Store the auth token and submit it to keystore after the confirmation
+ // button has been pressed.
+ mCurrentAuthSession.mTokenEscrow = token;
+ mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
+ }
+
+ // Notify SysUI that the biometric has been authenticated. SysUI already knows
+ // the implicit/explicit state and will react accordingly.
+ mStatusBarService.onBiometricAuthenticated();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ try {
+ mStatusBarService.onBiometricHelp(getContext().getResources().getString(
+ com.android.internal.R.string.biometric_not_recognized));
+ if (requireConfirmation) {
+ mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
+ mStatusBarService.showBiometricTryAgain();
+ // Cancel authentication. Skip the token/package check since we are
+ // cancelling from system server. The interface is permission protected so
+ // this is fine.
+ cancelInternal(null /* token */, null /* package */,
+ false /* fromClient */);
+ }
+ mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onError(int cookie, int error, String message) throws RemoteException {
+ Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
+ // Errors can either be from the current auth session or the pending auth session.
+ // The pending auth session may receive errors such as ERROR_LOCKOUT before
+ // it becomes the current auth session. Similarly, the current auth session may
+ // receive errors such as ERROR_CANCELED while the pending auth session is preparing
+ // to be started. Thus we must match error messages with their cookies to be sure
+ // of their intended receivers.
+ try {
+ if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
+ if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
+ mStatusBarService.onBiometricError(message);
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ mStatusBarService.hideBiometricDialog();
+ } else {
+ // Send errors after the dialog is dismissed.
+ mHandler.postDelayed(() -> {
+ try {
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }, BiometricPrompt.HIDE_DIALOG_DELAY);
+ }
+ } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+ || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) {
+ if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+ && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ // Skip the first ERROR_CANCELED message when this happens, since
+ // "try again" requires us to cancel authentication but keep
+ // the prompt showing.
+ mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED;
+ } else {
+ // In the "try again" state, we should forward canceled errors to
+ // the client and and clean up.
+ mCurrentAuthSession.mClientReceiver.onError(error, message);
+ mStatusBarService.onBiometricError(message);
+ mActivityTaskManager.unregisterTaskStackListener(
+ mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+ } else {
+ Slog.e(TAG, "Impossible session error state: "
+ + mCurrentAuthSession.mState);
+ }
+ } else if (mPendingAuthSession != null
+ && mPendingAuthSession.containsCookie(cookie)) {
+ if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
+ mPendingAuthSession.mClientReceiver.onError(error, message);
+ mPendingAuthSession.mState = STATE_AUTH_IDLE;
+ mPendingAuthSession = null;
+ } else {
+ Slog.e(TAG, "Impossible pending session error state: "
+ + mPendingAuthSession.mState);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onAcquired(int acquiredInfo, String message) throws RemoteException {
+ if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+ try {
+ mStatusBarService.onBiometricHelp(message);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
+ @Override
+ public void onDialogDismissed(int reason) throws RemoteException {
+ if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Positive button is used by passive modalities as a "confirm" button,
+ // do not send to client
+ mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
+ // Cancel authentication. Skip the token/package check since we are cancelling
+ // from system server. The interface is permission protected so this is fine.
+ cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+ }
+ if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_user_canceled));
+ } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ // Have the service send the token to KeyStore, and send onAuthenticated
+ // to the application
+ KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ }
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ }
+
+ @Override
+ public void onTryAgainPressed() {
+ Slog.d(TAG, "onTryAgainPressed");
+ // No need to check permission, since it can only be invoked by SystemUI
+ // (or system server itself).
+ mHandler.post(() -> {
+ authenticateInternal(mCurrentAuthSession.mToken,
+ mCurrentAuthSession.mSessionId,
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.mClientReceiver,
+ mCurrentAuthSession.mOpPackageName,
+ mCurrentAuthSession.mBundle,
+ mCurrentAuthSession.mCallingUid,
+ mCurrentAuthSession.mCallingPid,
+ mCurrentAuthSession.mCallingUserId,
+ mCurrentAuthSession.mModality);
+ });
+ }
+ };
+
+ @Override // Binder call
+ public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
+ checkInternalPermission();
+
+ Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ if (pair.getValue() == cookie) {
+ mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
+ mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
+ Slog.d(TAG, "Matched cookie: " + cookie + ", "
+ + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
+ break;
+ }
+ }
+
+ if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
+ final boolean mContinuing = mCurrentAuthSession != null
+ && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
+ mCurrentAuthSession = mPendingAuthSession;
+ mPendingAuthSession = null;
+
+ mCurrentAuthSession.mState = STATE_AUTH_STARTED;
+ try {
+ int modality = TYPE_NONE;
+ it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ if (pair.getKey() == TYPE_FINGERPRINT) {
+ mFingerprintService.startPreparedClient(pair.getValue());
+ } else if (pair.getKey() == TYPE_IRIS) {
+ Slog.e(TAG, "Iris unsupported");
+ } else if (pair.getKey() == TYPE_FACE) {
+ mFaceService.startPreparedClient(pair.getValue());
+ } else {
+ Slog.e(TAG, "Unknown modality: " + pair.getKey());
+ }
+ modality |= pair.getKey();
+ }
+
+ if (!mContinuing) {
+ mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
+ mInternalReceiver, modality, requireConfirmation, userId);
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+
@Override // Binder call
public void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle)
+ throws RemoteException {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -261,16 +611,45 @@
checkInternalPermission();
}
- if (token == null || receiver == null || opPackageName == null || bundle == null
- || dialogReceiver == null) {
+ if (token == null || receiver == null || opPackageName == null || bundle == null) {
Slog.e(TAG, "Unable to authenticate, one or more null arguments");
return;
}
// Check the usage of this in system server. Need to remove this check if it becomes
// a public API.
- if (bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) {
+ final boolean useDefaultTitle =
+ bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
+ if (useDefaultTitle) {
checkInternalPermission();
+ // Set the default title if necessary
+ try {
+ if (useDefaultTitle) {
+ final List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ for (int i = 0; i < procs.size(); i++) {
+ final ActivityManager.RunningAppProcessInfo info = procs.get(i);
+ if (info.uid == callingUid
+ && info.importance == IMPORTANCE_FOREGROUND) {
+ PackageManager pm = getContext().getPackageManager();
+ final CharSequence label = pm.getApplicationLabel(
+ pm.getApplicationInfo(info.processName,
+ PackageManager.GET_META_DATA));
+ final String title = getContext()
+ .getString(R.string.biometric_dialog_default_title, label);
+ if (TextUtils.isEmpty(
+ bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
+ bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
+ }
+ break;
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Name not found", e);
+ }
}
mHandler.post(() -> {
@@ -285,13 +664,13 @@
getContext().getString(R.string.biometric_error_hw_unavailable);
switch (error) {
case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
- receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ receiver.onError(error, hardwareUnavailable);
break;
case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
- receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+ receiver.onError(error, hardwareUnavailable);
break;
case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
- receiver.onError(0 /* deviceId */, error,
+ receiver.onError(error,
getErrorString(modality, error, 0 /* vendorCode */));
break;
default:
@@ -304,60 +683,91 @@
return;
}
- // Actually start authentication
mCurrentModality = modality;
- try {
- // No polymorphism :(
- if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
- mFingerprintService.authenticateFromService(token, sessionId, userId,
- receiver, flags, opPackageName, bundle, dialogReceiver,
- callingUid, callingPid, callingUserId);
- } else if (mCurrentModality == BIOMETRIC_IRIS) {
- Slog.w(TAG, "Unsupported modality");
- } else if (mCurrentModality == BIOMETRIC_FACE) {
- mFaceService.authenticateFromService(true /* requireConfirmation */,
- token, sessionId, userId, receiver, flags, opPackageName,
- bundle, dialogReceiver, callingUid, callingPid, callingUserId);
- } else {
- Slog.w(TAG, "Unsupported modality");
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to start authentication", e);
- }
+
+ // Actually start authentication
+ authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
+ callingUid, callingPid, callingUserId, modality);
});
}
+ /**
+ * authenticate() (above) which is called from BiometricPrompt determines which
+ * modality/modalities to start authenticating with. authenticateInternal() should only be
+ * used for:
+ * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
+ * invoked, shortly after which BiometricPrompt is shown and authentication starts
+ * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
+ * and the user has pressed "try again"
+ */
+ private void authenticateInternal(IBinder token, long sessionId, int userId,
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
+ int callingUid, int callingPid, int callingUserId, int modality) {
+ try {
+ // Generate random cookies to pass to the services that should prepare to start
+ // authenticating. Store the cookie here and wait for all services to "ack"
+ // with the cookie. Once all cookies are received, we can show the prompt
+ // and let the services start authenticating. The cookie should be non-zero.
+ final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
+ Slog.d(TAG, "Creating auth session. Modality: " + modality
+ + ", cookie: " + cookie);
+ final HashMap<Integer, Integer> authenticators = new HashMap<>();
+ authenticators.put(modality, cookie);
+ mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
+ receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
+ modality);
+ mPendingAuthSession.mState = STATE_AUTH_CALLED;
+ // No polymorphism :(
+ if ((modality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.prepareForAuthentication(token, sessionId, userId,
+ mInternalReceiver, opPackageName, cookie,
+ callingUid, callingPid, callingUserId);
+ }
+ if ((modality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((modality & TYPE_FACE) != 0) {
+ mFaceService.prepareForAuthentication(true /* requireConfirmation */,
+ token, sessionId, userId, mInternalReceiver, opPackageName,
+ cookie, callingUid, callingPid, callingUserId);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to start authentication", e);
+ }
+ }
+
@Override // Binder call
public void cancelAuthentication(IBinder token, String opPackageName)
throws RemoteException {
checkPermission();
-
if (token == null || opPackageName == null) {
Slog.e(TAG, "Unable to cancel, one or more null arguments");
return;
}
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
+ // We need to check the current authenticators state. If we're pending confirm
+ // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
+ // since we won't be getting an onError from the driver.
+ if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ mHandler.post(() -> {
+ try {
+ // Send error to client
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ getContext().getString(
+ com.android.internal.R.string.biometric_error_user_canceled)
+ );
- mHandler.post(() -> {
- try {
- if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
- mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId);
- } else if (mCurrentModality == BIOMETRIC_IRIS) {
- Slog.w(TAG, "Unsupported modality");
- } else if (mCurrentModality == BIOMETRIC_FACE) {
- mFaceService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId);
- } else {
- Slog.w(TAG, "Unsupported modality");
+ mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mCurrentAuthSession = null;
+ mStatusBarService.hideBiometricDialog();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel authentication");
- }
- });
+ });
+ } else {
+ cancelInternal(token, opPackageName, true /* fromClient */);
+ }
}
@Override // Binder call
@@ -402,6 +812,31 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ mHandler.post(() -> {
+ try {
+ // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+ // drivers have canceled authentication.
+ if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ if ((mCurrentModality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((mCurrentModality & TYPE_FACE) != 0) {
+ mFaceService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to cancel authentication");
+ }
+ });
+ }
}
private void checkAppOp(String opPackageName, int callingUid) {
@@ -413,7 +848,7 @@
}
private void checkInternalPermission() {
- getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
+ getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
"Must have USE_BIOMETRIC_INTERNAL permission");
}
@@ -490,16 +925,19 @@
* returns errors through the callback (no biometric feature, hardware not detected, no
* templates enrolled, etc). This service must not start authentication if errors are sent.
*
- * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE},
- * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE}
+ * @Returns A pair [Modality, Error] with Modality being one of
+ * {@link BiometricAuthenticator#TYPE_NONE},
+ * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
+ * {@link BiometricAuthenticator#TYPE_IRIS},
+ * {@link BiometricAuthenticator#TYPE_FACE}
* and the error containing one of the {@link BiometricConstants} errors.
*/
private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
- int modality = BIOMETRIC_NONE;
+ int modality = TYPE_NONE;
// No biometric features, send error
if (mAuthenticators.isEmpty()) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
}
// Assuming that authenticators are listed in priority-order, the rest of this function
@@ -512,13 +950,13 @@
boolean hasTemplatesEnrolled = false;
boolean enabledForApps = false;
- int firstHwAvailable = BIOMETRIC_NONE;
+ int firstHwAvailable = TYPE_NONE;
for (int i = 0; i < mAuthenticators.size(); i++) {
modality = mAuthenticators.get(i).getType();
BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
if (authenticator.isHardwareDetected()) {
isHardwareDetected = true;
- if (firstHwAvailable == BIOMETRIC_NONE) {
+ if (firstHwAvailable == TYPE_NONE) {
// Store the first one since we want to return the error in correct priority
// order.
firstHwAvailable = modality;
@@ -538,13 +976,13 @@
// Check error conditions
if (!isHardwareDetected) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
} else if (!hasTemplatesEnrolled) {
// Return the modality here so the correct error string can be sent. This error is
// preferred over !enabledForApps
return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
} else if (!enabledForApps) {
- return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
}
return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
@@ -552,11 +990,11 @@
private boolean isEnabledForApp(int modality) {
switch(modality) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return true;
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return true;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return mSettingObserver.getFaceEnabledForApps();
default:
Slog.w(TAG, "Unsupported modality: " + modality);
@@ -566,12 +1004,12 @@
private String getErrorString(int type, int error, int vendorCode) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return FingerprintManager.getErrorString(getContext(), error, vendorCode);
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
Slog.w(TAG, "Modality not supported");
return null; // not supported
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return FaceManager.getErrorString(getContext(), error, vendorCode);
default:
Slog.w(TAG, "Unable to get error string for modality: " + type);
@@ -581,12 +1019,12 @@
private BiometricAuthenticator getAuthenticator(int type) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return (FingerprintManager)
getContext().getSystemService(Context.FINGERPRINT_SERVICE);
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return null;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return (FaceManager)
getContext().getSystemService(Context.FACE_SERVICE);
default:
@@ -596,11 +1034,11 @@
private boolean hasFeature(int type) {
switch (type) {
- case BIOMETRIC_FINGERPRINT:
+ case TYPE_FINGERPRINT:
return mHasFeatureFingerprint;
- case BIOMETRIC_IRIS:
+ case TYPE_IRIS:
return mHasFeatureIris;
- case BIOMETRIC_FACE:
+ case TYPE_FACE:
return mHasFeatureFace;
default:
return false;
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 74d742a..9649ccd 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -16,7 +16,6 @@
package com.android.server.biometrics;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import android.app.ActivityManager;
@@ -36,8 +35,9 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.fingerprint.Fingerprint;
import android.os.Binder;
import android.os.Bundle;
@@ -56,7 +56,6 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
@@ -106,6 +105,7 @@
protected final AppOpsManager mAppOps;
protected final H mHandler = new H();
+ private IBiometricService mBiometricService;
private ClientMonitor mCurrentClient;
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
@@ -223,12 +223,9 @@
public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver,
- IStatusBarService statusBarService, boolean requireConfirmation) {
- super(context, getMetrics(), daemon, halDeviceId, token, listener,
- targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
- statusBarService, requireConfirmation);
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId,
+ groupId, opId, restricted, owner, cookie, requireConfirmation);
}
@Override
@@ -279,11 +276,6 @@
}
return AuthenticationClient.LOCKOUT_NONE;
}
-
- @Override
- public void onAuthenticationConfirmed() {
- removeClient(mCurrentClient);
- }
}
protected class EnrollClientImpl extends EnrollClient {
@@ -345,18 +337,28 @@
default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
- void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
- throws RemoteException;
+ void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
- void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId)
- throws RemoteException;
+ default void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
- void onAuthenticationFailed(long deviceId)
- throws RemoteException;
+ default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
- void onError(long deviceId, int error, int vendorCode)
- throws RemoteException;
+ default void onAuthenticationFailed(long deviceId) throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
default void onRemoved(BiometricAuthenticator.Identifier identifier,
int remaining) throws RemoteException {};
@@ -366,6 +368,37 @@
}
/**
+ * Wraps the callback interface from Service -> BiometricPrompt
+ */
+ protected abstract class BiometricServiceListener implements ServiceListener {
+ private IBiometricServiceReceiverInternal mWrapperReceiver;
+
+ public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
+ mWrapperReceiver = wrapperReceiver;
+ }
+
+ public IBiometricServiceReceiverInternal getWrapperReceiver() {
+ return mWrapperReceiver;
+ }
+
+ @Override
+ public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
+ }
+ }
+ }
+
+ /**
* Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
* subclasses.
*/
@@ -706,30 +739,6 @@
}
mHandler.post(() -> {
- if (client.isBiometricPrompt()) {
- try {
- final List<ActivityManager.RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- for (int i = 0; i < procs.size(); i++) {
- final ActivityManager.RunningAppProcessInfo info = procs.get(i);
- if (info.uid == callingUid && info.importance == IMPORTANCE_FOREGROUND) {
- PackageManager pm = getContext().getPackageManager();
- final CharSequence label = pm.getApplicationLabel(
- pm.getApplicationInfo(info.processName,
- PackageManager.GET_META_DATA));
- final String title = getContext()
- .getString(R.string.biometric_dialog_default_title, label);
- client.setTitleIfEmpty(title);
- break;
- }
- }
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to get application name", e);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(getTag(), "Unable to get application name", e);
- }
- }
-
mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
// Get performance stats object for this user.
@@ -751,29 +760,37 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
- cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId,
+ true /* fromClient */);
}
protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
- if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
- return;
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
+ if (fromClient) {
+ // Only check this if cancel was called from the client (app). If cancel was called
+ // from BiometricService, it means the dialog was dismissed due to user interaction.
+ if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
+ return;
+ }
}
mHandler.post(() -> {
ClientMonitor client = mCurrentClient;
if (client instanceof AuthenticationClient) {
- if (client.getToken() == token) {
- if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
+ if (client.getToken() == token || !fromClient) {
+ if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
+ + ", fromClient: " + fromClient);
+ // If cancel was from BiometricService, it means the dialog was dismissed
+ // and authentication should be canceled.
client.stop(client.getToken() == token);
} else {
- if (DEBUG) Slog.v(getTag(), "can't stop client "
- + client.getOwnerString() + " since tokens don't match");
+ if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
+ + " since tokens don't match. fromClient: " + fromClient);
}
} else if (client != null) {
- if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
+ if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client "
+ client.getOwnerString());
}
});
@@ -805,8 +822,7 @@
int lockoutMode = getLockoutMode();
if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(getTag(), "In lockout mode(" + lockoutMode +
- ") ; disallowing authentication");
+ Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
@@ -919,7 +935,6 @@
if (currentClient != null) {
if (DEBUG) Slog.v(getTag(), "request stop current client " +
currentClient.getOwnerString());
-
// This check only matters for FingerprintService, since enumerate may call back
// multiple times.
if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
@@ -940,17 +955,51 @@
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
} else if (newClient != null) {
- mCurrentClient = newClient;
- if (DEBUG) Slog.v(getTag(), "starting client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- notifyClientActiveCallbacks(true);
+ // For BiometricPrompt clients, do not start until
+ // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
+ // modalities are ready before initiating authentication.
+ if (newClient instanceof AuthenticationClient) {
+ AuthenticationClient client = (AuthenticationClient) newClient;
+ if (client.isBiometricPrompt()) {
+ if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
+ mCurrentClient = newClient;
+ if (mBiometricService == null) {
+ mBiometricService = IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE));
+ }
+ try {
+ mBiometricService.onReadyForAuthentication(client.getCookie(),
+ client.getRequireConfirmation(), client.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception", e);
+ }
+ return;
+ }
+ }
- newClient.start();
+ // We are not a BiometricPrompt client, start the client immediately
+ mCurrentClient = newClient;
+ startCurrentClient(mCurrentClient.getCookie());
}
}
+ protected void startCurrentClient(int cookie) {
+ if (mCurrentClient == null) {
+ Slog.e(getTag(), "Trying to start null client!");
+ return;
+ }
+ if (DEBUG) Slog.v(getTag(), "starting client "
+ + mCurrentClient.getClass().getSuperclass().getSimpleName()
+ + "(" + mCurrentClient.getOwnerString() + ")"
+ + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
+ if (cookie != mCurrentClient.getCookie()) {
+ Slog.e(getTag(), "Mismatched cookie");
+ return;
+ }
+ notifyClientActiveCallbacks(true);
+ mCurrentClient.start();
+ }
+
protected void removeClient(ClientMonitor client) {
if (client != null) {
client.destroy();
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index a7ada2f..d19aff6 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -58,6 +58,9 @@
private IBinder mToken;
private BiometricServiceBase.ServiceListener mListener;
+ // Currently only used for authentication client. The cookie generated by BiometricService
+ // is never 0.
+ private final int mCookie;
protected final MetricsLogger mMetricsLogger;
protected final Metrics mMetrics;
@@ -80,7 +83,7 @@
public ClientMonitor(Context context, Metrics metrics,
BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
- boolean restricted, String owner) {
+ boolean restricted, String owner, int cookie) {
mContext = context;
mMetrics = metrics;
mDaemon = daemon;
@@ -91,6 +94,7 @@
mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
+ mCookie = cookie;
mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
mMetricsLogger = new MetricsLogger();
@@ -107,6 +111,10 @@
return mMetrics.logTag();
}
+ public int getCookie() {
+ return mCookie;
+ }
+
/**
* Contacts the biometric's HAL to start the client.
* @return 0 on success, errno from driver on failure
@@ -174,7 +182,7 @@
public boolean onError(long deviceId, int error, int vendorCode) {
try {
if (mListener != null) {
- mListener.onError(deviceId, error, vendorCode);
+ mListener.onError(deviceId, error, vendorCode, getCookie());
}
} catch (RemoteException e) {
Slog.w(getLogTag(), "Failed to invoke sendError", e);
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index 76dc5a9..f858ef5 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -40,7 +40,7 @@
BiometricServiceBase.ServiceListener listener, int userId, int groupId,
byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
mBiometricUtils = utils;
mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
}
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 47dc7ff..df6220c 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -34,7 +34,7 @@
BiometricServiceBase.ServiceListener listener, int groupId, int userId,
boolean restricted, String owner) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 15b3773..be233ec 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -37,7 +37,7 @@
BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId,
boolean restricted, String owner, BiometricUtils utils) {
super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
- owner);
+ owner, 0 /* cookie */);
mBiometricId = biometricId;
mBiometricUtils = utils;
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 7aa2e47..557af04 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -27,9 +27,8 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.biometrics.face.V1_0.Status;
@@ -38,7 +37,6 @@
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -50,7 +48,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.BiometricServiceBase;
@@ -89,27 +86,9 @@
public FaceAuthClient(Context context,
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
- boolean requireConfirmation) {
+ boolean restricted, String owner, int cookie, boolean requireConfirmation) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
- restricted, owner, bundle, dialogReceiver, statusBarService,
- requireConfirmation);
- }
-
- @Override
- public String getErrorString(int error, int vendorCode) {
- return FaceManager.getErrorString(getContext(), error, vendorCode);
- }
-
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
- return FaceManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
- }
-
- @Override
- public int getBiometricType() {
- return BiometricAuthenticator.TYPE_FACE;
+ restricted, owner, cookie, requireConfirmation);
}
}
@@ -162,28 +141,33 @@
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- null /* bundle */, null /* dialogReceiver */, mStatusBarService,
- false /* requireConfirmation */);
+ 0 /* cookie */, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
- public void authenticateFromService(boolean requireConfirmation, IBinder token, long opId,
- int groupId, IBiometricServiceReceiver receiver, int flags,
- String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId) {
+ public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
+ int groupId, IBiometricServiceReceiverInternal wrapperReceiver,
+ String opPackageName, int cookie, int callingUid, int callingPid,
+ int callingUserId) {
checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
- new BiometricPromptServiceListenerImpl(receiver),
- mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
- bundle, dialogReceiver, mStatusBarService, true /* requireConfirmation */);
+ new BiometricPromptServiceListenerImpl(wrapperReceiver),
+ mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
+ true /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@Override // Binder call
+ public void startPreparedClient(int cookie) {
+ checkPermission(MANAGE_BIOMETRIC);
+ startCurrentClient(cookie);
+ }
+
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
cancelAuthenticationInternal(token, opPackageName);
@@ -191,10 +175,10 @@
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
checkPermission(USE_BIOMETRIC_INTERNAL);
- cancelAuthenticationInternal(token, opPackageName,
- callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
+ callingUserId, fromClient);
}
@Override // Binder call
@@ -405,12 +389,9 @@
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* BiometricPrompt.
*/
- private class BiometricPromptServiceListenerImpl implements ServiceListener {
-
- private IBiometricServiceReceiver mBiometricServiceReceiver;
-
- public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
- mBiometricServiceReceiver = receiver;
+ private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
+ BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
+ super(wrapperReceiver);
}
@Override
@@ -419,32 +400,18 @@
/**
* Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
*/
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAcquired(deviceId,
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAcquired(
FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
}
}
@Override
- public void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
- }
- }
-
- @Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
- }
- }
-
- @Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onError(deviceId, error,
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onError(cookie, error,
FaceManager.getErrorString(getContext(), error, vendorCode));
}
}
@@ -455,7 +422,6 @@
* the FaceManager.
*/
private class ServiceListenerImpl implements ServiceListener {
-
private IFaceServiceReceiver mFaceServiceReceiver;
public ServiceListenerImpl(IFaceServiceReceiver receiver) {
@@ -501,7 +467,8 @@
}
@Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
if (mFaceServiceReceiver != null) {
mFaceServiceReceiver.onError(deviceId, error, vendorCode);
}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index b0b788f..6a5bc61 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -30,9 +30,8 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
@@ -42,7 +41,6 @@
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.os.Build;
-import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -55,7 +53,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.biometrics.AuthenticationClient;
@@ -109,27 +106,10 @@
public FingerprintAuthClient(Context context,
DaemonWrapper daemon, long halDeviceId, IBinder token,
ServiceListener listener, int targetUserId, int groupId, long opId,
- boolean restricted, String owner, Bundle bundle,
- IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+ boolean restricted, String owner, int cookie,
boolean requireConfirmation) {
super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
- restricted, owner, bundle, dialogReceiver, statusBarService,
- requireConfirmation);
- }
-
- @Override
- public String getErrorString(int error, int vendorCode) {
- return FingerprintManager.getErrorString(getContext(), error, vendorCode);
- }
-
- @Override
- public String getAcquiredString(int acquireInfo, int vendorCode) {
- return FingerprintManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
- }
-
- @Override
- public int getBiometricType() {
- return BiometricAuthenticator.TYPE_FINGERPRINT;
+ restricted, owner, cookie, requireConfirmation);
}
}
@@ -182,38 +162,44 @@
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
- null /* dialogReceiver */, mStatusBarService, false /* requireConfirmation */);
+ mCurrentUserId, groupId, opId, restricted, opPackageName,
+ 0 /* cookie */, false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
- public void authenticateFromService(IBinder token, long opId, int groupId,
- IBiometricServiceReceiver receiver, int flags, String opPackageName,
- Bundle bundle, IBiometricPromptReceiver dialogReceiver,
- int callingUid, int callingPid, int callingUserId) {
+ public void prepareForAuthentication(IBinder token, long opId, int groupId,
+ IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+ int cookie, int callingUid, int callingPid, int callingUserId) {
checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = true; // BiometricPrompt is always restricted
final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
mDaemonWrapper, mHalDeviceId, token,
- new BiometricPromptServiceListenerImpl(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService, false /* requireConfirmation */);
+ new BiometricPromptServiceListenerImpl(wrapperReceiver),
+ mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
+ false /* requireConfirmation */);
authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
callingUserId);
}
@Override // Binder call
+ public void startPreparedClient(int cookie) {
+ checkPermission(MANAGE_BIOMETRIC);
+ startCurrentClient(cookie);
+ }
+
+
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
- int callingUid, int callingPid, int callingUserId) {
+ int callingUid, int callingPid, int callingUserId, boolean fromClient) {
checkPermission(MANAGE_BIOMETRIC);
- cancelAuthenticationInternal(token, opPackageName,
- callingUid, callingPid, callingUserId);
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
+ callingUserId, fromClient);
}
@Override // Binder call
@@ -388,43 +374,25 @@
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* BiometricPrompt.
*/
- private class BiometricPromptServiceListenerImpl implements ServiceListener {
-
- private IBiometricServiceReceiver mBiometricServiceReceiver;
-
- public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
- mBiometricServiceReceiver = receiver;
+ private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
+ BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
+ super(wrapperReceiver);
}
@Override
public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAcquired(deviceId, acquiredInfo,
- FingerprintManager.getAcquiredString(
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onAcquired(acquiredInfo, FingerprintManager.getAcquiredString(
getContext(), acquiredInfo, vendorCode));
}
}
@Override
- public void onAuthenticationSucceeded(long deviceId,
- BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
- }
- }
-
- @Override
- public void onAuthenticationFailed(long deviceId) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
- }
- }
-
- @Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
- if (mBiometricServiceReceiver != null) {
- mBiometricServiceReceiver.onError(deviceId, error,
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
+ if (getWrapperReceiver() != null) {
+ getWrapperReceiver().onError(cookie, error,
FingerprintManager.getErrorString(getContext(), error, vendorCode));
}
}
@@ -435,7 +403,6 @@
* the FingerprintManager.
*/
private class ServiceListenerImpl implements ServiceListener {
-
private IFingerprintServiceReceiver mFingerprintServiceReceiver;
public ServiceListenerImpl(IFingerprintServiceReceiver receiver) {
@@ -483,7 +450,8 @@
}
@Override
- public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ public void onError(long deviceId, int error, int vendorCode, int cookie)
+ throws RemoteException {
if (mFingerprintServiceReceiver != null) {
mFingerprintServiceReceiver.onError(deviceId, error, vendorCode);
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index c3e3842..bf95210 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -72,6 +72,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Protocol;
+import com.android.internal.util.RingBufferIndices;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
@@ -99,7 +100,7 @@
private static final String TAG = NetworkMonitor.class.getSimpleName();
private static final boolean DBG = true;
private static final boolean VDBG = false;
-
+ private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG);
// Default configuration values for captive portal detection probes.
// TODO: append a random length parameter to the default HTTPS url.
// TODO: randomize browser version ids in the default User-Agent String.
@@ -116,6 +117,15 @@
private static final int SOCKET_TIMEOUT_MS = 10000;
private static final int PROBE_TIMEOUT_MS = 3000;
+ // Default configuration values for data stall detection.
+ private static final int DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = 5;
+ private static final int DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS = 60 * 1000;
+ private static final int DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS = 30 * 60 * 1000;
+
+ private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
+ private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
+ (1 << DATA_STALL_EVALUATION_TYPE_DNS);
+
static enum EvaluationResult {
VALIDATED(true),
CAPTIVE_PORTAL(false);
@@ -233,6 +243,12 @@
*/
public static final int CMD_PROBE_COMPLETE = BASE + 16;
+ /**
+ * ConnectivityService notifies NetworkMonitor of DNS query responses event.
+ * arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query.
+ */
+ public static final int EVENT_DNS_NOTIFICATION = BASE + 17;
+
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -297,6 +313,7 @@
private final State mCaptivePortalState = new CaptivePortalState();
private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
private final State mProbingState = new ProbingState();
+ private final State mWaitingForNextProbeState = new WaitingForNextProbeState();
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -314,6 +331,12 @@
private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
private int mEvaluateAttempts = 0;
private volatile int mProbeToken = 0;
+ private final int mConsecutiveDnsTimeoutThreshold;
+ private final int mDataStallMinEvaluateTime;
+ private final int mDataStallValidDnsTimeThreshold;
+ private final int mDataStallEvaluationType;
+ private final DnsStallDetector mDnsStallDetector;
+ private long mLastProbeTime;
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
NetworkRequest defaultRequest) {
@@ -346,6 +369,7 @@
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
addState(mProbingState, mEvaluatingState);
+ addState(mWaitingForNextProbeState, mEvaluatingState);
addState(mCaptivePortalState, mMaybeNotifyState);
addState(mEvaluatingPrivateDnsState, mDefaultState);
addState(mValidatedState, mDefaultState);
@@ -359,6 +383,12 @@
mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
mRandom = deps.getRandom();
+ // TODO: Evaluate to move data stall configuration to a specific class.
+ mConsecutiveDnsTimeoutThreshold = getConsecutiveDnsTimeoutThreshold();
+ mDnsStallDetector = new DnsStallDetector(mConsecutiveDnsTimeoutThreshold);
+ mDataStallMinEvaluateTime = getDataStallMinEvaluateTime();
+ mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
+ mDataStallEvaluationType = getDataStallEvalutionType();
start();
}
@@ -507,6 +537,9 @@
sendMessage(CMD_EVALUATE_PRIVATE_DNS);
break;
}
+ case EVENT_DNS_NOTIFICATION:
+ mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+ break;
default:
break;
}
@@ -537,6 +570,13 @@
case CMD_EVALUATE_PRIVATE_DNS:
transitionTo(mEvaluatingPrivateDnsState);
break;
+ case EVENT_DNS_NOTIFICATION:
+ mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+ if (isDataStall()) {
+ validationLog("Suspecting data stall, reevaluate");
+ transitionTo(mEvaluatingState);
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -839,6 +879,11 @@
@Override
public void enter() {
+ if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
+ //Don't continue to blame UID forever.
+ TrafficStats.clearThreadStatsUid();
+ }
+
final int token = ++mProbeToken;
mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0,
isCaptivePortal())));
@@ -856,6 +901,7 @@
final CaptivePortalProbeResult probeResult =
(CaptivePortalProbeResult) message.obj;
+ mLastProbeTime = SystemClock.elapsedRealtime();
if (probeResult.isSuccessful()) {
// Transit EvaluatingPrivateDnsState to get to Validated
// state (even if no Private DNS validation required).
@@ -865,28 +911,16 @@
mLastPortalProbeResult = probeResult;
transitionTo(mCaptivePortalState);
} else {
- final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
- sendMessageDelayed(msg, mReevaluateDelayMs);
logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
notifyNetworkTestResultInvalid(probeResult.redirectUrl);
- if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
- // Don't continue to blame UID forever.
- TrafficStats.clearThreadStatsUid();
- }
- mReevaluateDelayMs *= 2;
- if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
- mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
- }
+ transitionTo(mWaitingForNextProbeState);
}
return HANDLED;
- case CMD_REEVALUATE:
- // Leave the event to EvaluatingState. Defer this message will result in reset
- // of mReevaluateDelayMs and mEvaluateAttempts.
- case CMD_NETWORK_DISCONNECTED:
+ case EVENT_DNS_NOTIFICATION:
+ // Leave the event to DefaultState to record correct dns timestamp.
return NOT_HANDLED;
default:
- // TODO: Some events may able to handle in this state, instead of deferring to
- // next state.
+ // Wait for probe result and defer events to next state by default.
deferMessage(message);
return HANDLED;
}
@@ -901,6 +935,29 @@
}
}
+ // Being in the WaitingForNextProbeState indicates that evaluating probes failed and state is
+ // transited from ProbingState. This ensures that the state machine is only in ProbingState
+ // while a probe is in progress, not while waiting to perform the next probe. That allows
+ // ProbingState to defer most messages until the probe is complete, which keeps the code simple
+ // and matches the pre-Q behaviour where probes were a blocking operation performed on the state
+ // machine thread.
+ private class WaitingForNextProbeState extends State {
+ @Override
+ public void enter() {
+ final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
+ sendMessageDelayed(msg, mReevaluateDelayMs);
+ mReevaluateDelayMs *= 2;
+ if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
+ mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ return NOT_HANDLED;
+ }
+ }
+
// Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
// most one per address family. This ensures we only wait up to 20 seconds for TCP connections
// to complete, regardless of how many IP addresses a host has.
@@ -947,6 +1004,29 @@
Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
}
+ private int getConsecutiveDnsTimeoutThreshold() {
+ return mDependencies.getSetting(mContext,
+ Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
+ DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD);
+ }
+
+ private int getDataStallMinEvaluateTime() {
+ return mDependencies.getSetting(mContext,
+ Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+ DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS);
+ }
+
+ private int getDataStallValidDnsTimeThreshold() {
+ return mDependencies.getSetting(mContext,
+ Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
+ DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS);
+ }
+
+ private int getDataStallEvalutionType() {
+ return mDependencies.getSetting(mContext, Settings.Global.DATA_STALL_EVALUATION_TYPE,
+ DEFAULT_DATA_STALL_EVALUATION_TYPES);
+ }
+
// Static for direct access by ConnectivityService
public static String getCaptivePortalServerHttpUrl(Context context) {
return getCaptivePortalServerHttpUrl(Dependencies.DEFAULT, context);
@@ -1462,4 +1542,127 @@
public static final Dependencies DEFAULT = new Dependencies();
}
+
+ /**
+ * Methods in this class perform no locking because all accesses are performed on the state
+ * machine's thread. Need to consider the thread safety if it ever could be accessed outside the
+ * state machine.
+ */
+ @VisibleForTesting
+ protected class DnsStallDetector {
+ private static final int DEFAULT_DNS_LOG_SIZE = 50;
+ private int mConsecutiveTimeoutCount = 0;
+ private int mSize;
+ final DnsResult[] mDnsEvents;
+ final RingBufferIndices mResultIndices;
+
+ DnsStallDetector(int size) {
+ mSize = Math.max(DEFAULT_DNS_LOG_SIZE, size);
+ mDnsEvents = new DnsResult[mSize];
+ mResultIndices = new RingBufferIndices(mSize);
+ }
+
+ @VisibleForTesting
+ protected void accumulateConsecutiveDnsTimeoutCount(int code) {
+ final DnsResult result = new DnsResult(code);
+ mDnsEvents[mResultIndices.add()] = result;
+ if (result.isTimeout()) {
+ mConsecutiveTimeoutCount++;
+ } else {
+ // Keep the event in mDnsEvents without clearing it so that there are logs to do the
+ // simulation and analysis.
+ mConsecutiveTimeoutCount = 0;
+ }
+ }
+
+ private boolean isDataStallSuspected(int timeoutCountThreshold, int validTime) {
+ if (timeoutCountThreshold <= 0) {
+ Log.wtf(TAG, "Timeout count threshold should be larger than 0.");
+ return false;
+ }
+
+ // Check if the consecutive timeout count reach the threshold or not.
+ if (mConsecutiveTimeoutCount < timeoutCountThreshold) {
+ return false;
+ }
+
+ // Check if the target dns event index is valid or not.
+ final int firstConsecutiveTimeoutIndex =
+ mResultIndices.indexOf(mResultIndices.size() - timeoutCountThreshold);
+
+ // If the dns timeout events happened long time ago, the events are meaningless for
+ // data stall evaluation. Thus, check if the first consecutive timeout dns event
+ // considered in the evaluation happened in defined threshold time.
+ final long now = SystemClock.elapsedRealtime();
+ final long firstTimeoutTime = now - mDnsEvents[firstConsecutiveTimeoutIndex].mTimeStamp;
+ return (firstTimeoutTime < validTime);
+ }
+
+ int getConsecutiveTimeoutCount() {
+ return mConsecutiveTimeoutCount;
+ }
+ }
+
+ private static class DnsResult {
+ // TODO: Need to move the DNS return code definition to a specific class once unify DNS
+ // response code is done.
+ private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+
+ private final long mTimeStamp;
+ private final int mReturnCode;
+
+ DnsResult(int code) {
+ mTimeStamp = SystemClock.elapsedRealtime();
+ mReturnCode = code;
+ }
+
+ private boolean isTimeout() {
+ return mReturnCode == RETURN_CODE_DNS_TIMEOUT;
+ }
+ }
+
+
+ @VisibleForTesting
+ protected DnsStallDetector getDnsStallDetector() {
+ return mDnsStallDetector;
+ }
+
+ private boolean dataStallEvaluateTypeEnabled(int type) {
+ return (mDataStallEvaluationType & (1 << type)) != 0;
+ }
+
+ @VisibleForTesting
+ protected long getLastProbeTime() {
+ return mLastProbeTime;
+ }
+
+ @VisibleForTesting
+ protected boolean isDataStall() {
+ boolean result = false;
+ // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
+ // possible traffic cost in metered network.
+ if (mNetworkAgentInfo.networkCapabilities.isMetered()
+ && (SystemClock.elapsedRealtime() - getLastProbeTime()
+ < mDataStallMinEvaluateTime)) {
+ return false;
+ }
+
+ // Check dns signal. Suspect it may be a data stall if both :
+ // 1. The number of consecutive DNS query timeouts > mConsecutiveDnsTimeoutThreshold.
+ // 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms.
+ if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) {
+ if (mDnsStallDetector.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold,
+ mDataStallValidDnsTimeThreshold)) {
+ result = true;
+ logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND);
+ }
+ }
+
+ if (VDBG_STALL) {
+ log("isDataStall: result=" + result + ", consecutive dns timeout count="
+ + mDnsStallDetector.getConsecutiveTimeoutCount());
+ }
+
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 33525fd..f2c539c 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -16,16 +16,7 @@
package com.android.server.display;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-
import android.content.Context;
-import android.graphics.PixelFormat;
import android.graphics.SurfaceTexture;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
@@ -34,20 +25,29 @@
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
-import android.opengl.GLES20;
import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
import android.util.Slog;
import android.view.DisplayInfo;
-import android.view.Surface.OutOfResourcesException;
import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
-import libcore.io.Streams;
-
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
/**
* <p>
* Animates a screen transition from on to off or off to on by applying
@@ -569,37 +569,31 @@
mSurfaceSession = new SurfaceSession();
}
- SurfaceControl.openTransaction();
- try {
- if (mSurfaceControl == null) {
- try {
- int flags;
- if (mMode == MODE_FADE) {
- flags = SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN;
- } else {
- flags = SurfaceControl.OPAQUE | SurfaceControl.HIDDEN;
- }
- mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
- .setName("ColorFade")
- .setSize(mDisplayWidth, mDisplayHeight)
- .setFlags(flags)
- .build();
- } catch (OutOfResourcesException ex) {
- Slog.e(TAG, "Unable to create surface.", ex);
- return false;
+ if (mSurfaceControl == null) {
+ Transaction t = new Transaction();
+ try {
+ final SurfaceControl.Builder builder =
+ new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade");
+ if (mMode == MODE_FADE) {
+ builder.setColorLayer(true);
+ } else {
+ builder.setBufferSize(mDisplayWidth, mDisplayHeight);
}
-
- mSurfaceControl.setLayerStack(mDisplayLayerStack);
- mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
- mSurface = new Surface();
- mSurface.copyFrom(mSurfaceControl);
-
- mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
- mDisplayId, mSurfaceControl);
- mSurfaceLayout.onDisplayTransaction();
+ mSurfaceControl = builder.build();
+ } catch (OutOfResourcesException ex) {
+ Slog.e(TAG, "Unable to create surface.", ex);
+ return false;
}
- } finally {
- SurfaceControl.closeTransaction();
+
+ t.setLayerStack(mSurfaceControl, mDisplayLayerStack);
+ t.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ mSurface = new Surface();
+ mSurface.copyFrom(mSurfaceControl);
+
+ mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
+ mDisplayId, mSurfaceControl);
+ mSurfaceLayout.onDisplayTransaction(t);
+ t.apply();
}
return true;
}
@@ -746,7 +740,7 @@
}
@Override
- public void onDisplayTransaction() {
+ public void onDisplayTransaction(Transaction t) {
synchronized (this) {
if (mSurfaceControl == null) {
return;
@@ -755,21 +749,21 @@
DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
switch (displayInfo.rotation) {
case Surface.ROTATION_0:
- mSurfaceControl.setPosition(0, 0);
- mSurfaceControl.setMatrix(1, 0, 0, 1);
+ t.setPosition(mSurfaceControl, 0, 0);
+ t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
break;
case Surface.ROTATION_90:
- mSurfaceControl.setPosition(0, displayInfo.logicalHeight);
- mSurfaceControl.setMatrix(0, -1, 1, 0);
+ t.setPosition(mSurfaceControl, 0, displayInfo.logicalHeight);
+ t.setMatrix(mSurfaceControl, 0, -1, 1, 0);
break;
case Surface.ROTATION_180:
- mSurfaceControl.setPosition(displayInfo.logicalWidth,
+ t.setPosition(mSurfaceControl, displayInfo.logicalWidth,
displayInfo.logicalHeight);
- mSurfaceControl.setMatrix(-1, 0, 0, -1);
+ t.setMatrix(mSurfaceControl, -1, 0, 0, -1);
break;
case Surface.ROTATION_270:
- mSurfaceControl.setPosition(displayInfo.logicalWidth, 0);
- mSurfaceControl.setMatrix(0, 1, -1, 0);
+ t.setPosition(mSurfaceControl, displayInfo.logicalWidth, 0);
+ t.setMatrix(mSurfaceControl, 0, 1, -1, 0);
break;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 7bfe9ce..6ee5665 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -225,6 +225,8 @@
viewport.deviceHeight = isRotated ? info.width : info.height;
viewport.uniqueId = info.uniqueId;
+ // TODO(b/112898898) Use an actual port here.
+ viewport.physicalPort = null;
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d04fa23..360a7d1 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -497,7 +497,7 @@
// List is self-synchronized copy-on-write.
for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
- listener.onDisplayTransaction();
+ listener.onDisplayTransaction(t);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b148a2f..c0d3fdf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -980,7 +980,7 @@
* @param sourceAddress a logical address of source device where sends polling message
* @param pickStrategy strategy how to pick polling candidates
* @param retryCount the number of retry used to send polling message to remote devices
- * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
+ * @throws IllegalArgumentException if {@code pickStrategy} is invalid value
*/
@ServiceThreadOnly
void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
diff --git a/services/core/java/com/android/server/input/ConfigurationProcessor.java b/services/core/java/com/android/server/input/ConfigurationProcessor.java
new file mode 100644
index 0000000..970e86a
--- /dev/null
+++ b/services/core/java/com/android/server/input/ConfigurationProcessor.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package com.android.server.input;
+
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+
+class ConfigurationProcessor {
+ private static final String TAG = "ConfigurationProcessor";
+
+ static List<String> processExcludedDeviceNames(InputStream xml) throws Exception {
+ List<String> names = new ArrayList<>();
+ try (InputStreamReader confReader = new InputStreamReader(xml)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(confReader);
+ XmlUtils.beginDocument(parser, "devices");
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (!"device".equals(parser.getName())) {
+ break;
+ }
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ names.add(name);
+ }
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Parse the configuration for input port associations.
+ *
+ * Configuration format:
+ * <code>
+ * <ports>
+ * <port display="0" input="usb-xhci-hcd.0.auto-1.4.3/input0" />
+ * <port display="1" input="usb-xhci-hcd.0.auto-1.4.2/input0" />
+ * </ports>
+ * </code>
+ *
+ * In this example, any input device that has physical port of
+ * "usb-xhci-hcd.0.auto-1.4.3/input0" will be associated with a display
+ * that has the physical port "0". If such a display does not exist, the input device
+ * will be disabled and no input events will be dispatched from that input device until a
+ * matching display appears. Likewise, an input device that has port "..1.4.2.." will have
+ * its input events forwarded to a display that has physical port of "1".
+ *
+ * Note: display port must be a numeric value, and this is checked at runtime for validity.
+ * At the same time, it is specified as a string for simplicity.
+ *
+ * Note: do not confuse "display id" with "display port".
+ * The "display port" is the physical port on which the display is connected. This could
+ * be something like HDMI0, HDMI1, etc. For virtual displays, "display port" will be null.
+ * The "display id" is a way to identify a particular display, and is not a stable API.
+ * All displays, including virtual ones, will have a display id.
+ *
+ * Return the pairs of associations. The first item in the pair is the input port,
+ * the second item in the pair is the display port.
+ */
+ @VisibleForTesting
+ static List<Pair<String, String>> processInputPortAssociations(InputStream xml)
+ throws Exception {
+ List<Pair<String, String>> associations = new ArrayList<>();
+ try (InputStreamReader confReader = new InputStreamReader(xml)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(confReader);
+ XmlUtils.beginDocument(parser, "ports");
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+ String entryName = parser.getName();
+ if (!"port".equals(entryName)) {
+ break;
+ }
+ String inputPort = parser.getAttributeValue(null, "input");
+ String displayPort = parser.getAttributeValue(null, "display");
+ if (TextUtils.isEmpty(inputPort) || TextUtils.isEmpty(displayPort)) {
+ // This is likely an error by an OEM during device configuration
+ Slog.wtf(TAG, "Ignoring incomplete entry");
+ continue;
+ }
+ try {
+ Integer.parseUnsignedInt(displayPort);
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Display port should be an integer");
+ continue;
+ }
+ associations.add(new Pair<>(inputPort, displayPort));
+ }
+ }
+ return associations;
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3a31c9c..d96b6cb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -64,18 +64,18 @@
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.Xml;
import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
import android.view.IWindow;
-import android.view.InputChannel;
import android.view.InputApplicationHandle;
-import android.view.InputWindowHandle;
+import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.InputWindowHandle;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.Surface;
@@ -97,14 +97,13 @@
import libcore.io.IoUtils;
import libcore.io.Streams;
-import org.xmlpull.v1.XmlPullParser;
-
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -124,6 +123,7 @@
static final boolean DEBUG = false;
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+ private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml";
private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
@@ -1443,25 +1443,10 @@
}
}
- public void setInputWindows(InputWindowHandle[] windowHandles, int displayId) {
- nativeSetInputWindows(mPtr, windowHandles, displayId);
- }
-
public void setFocusedApplication(int displayId, InputApplicationHandle application) {
nativeSetFocusedApplication(mPtr, displayId, application);
}
- public void setFocusedWindow(InputWindowHandle focusedWindowHandle) {
- final IWindow newFocusedWindow =
- focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
- if (mFocusedWindow != newFocusedWindow) {
- if (mFocusedWindowHasCapture) {
- setPointerCapture(false);
- }
- mFocusedWindow = newFocusedWindow;
- }
- }
-
public void setFocusedDisplay(int displayId) {
nativeSetFocusedDisplay(mPtr, displayId);
}
@@ -1799,11 +1784,22 @@
mWindowManagerCallbacks.notifyInputChannelBroken(token);
}
+ // Native callback
+ private void notifyFocusChanged(IBinder token) {
+ if (mFocusedWindow != token) {
+ if (mFocusedWindowHasCapture) {
+ setPointerCapture(false);
+ }
+ if (token instanceof IWindow) {
+ mFocusedWindow = (IWindow) token;
+ }
+ }
+ }
+
// Native callback.
- private long notifyANR(InputApplicationHandle inputApplicationHandle,
- IBinder token, String reason) {
+ private long notifyANR(IBinder token, String reason) {
return mWindowManagerCallbacks.notifyANR(
- inputApplicationHandle, token, reason);
+ token, reason);
}
// Native callback.
@@ -1834,14 +1830,12 @@
}
// Native callback.
- private long interceptKeyBeforeDispatching(IBinder focus,
- KeyEvent event, int policyFlags) {
+ private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
// Native callback.
- private KeyEvent dispatchUnhandledKey(IBinder focus,
- KeyEvent event, int policyFlags) {
+ private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
}
@@ -1858,11 +1852,9 @@
}
// Native callback.
- private String[] getExcludedDeviceNames() {
- ArrayList<String> names = new ArrayList<String>();
-
+ private static String[] getExcludedDeviceNames() {
+ List<String> names = new ArrayList<>();
// Read partner-provided list of excluded input devices
- XmlPullParser parser = null;
// Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
final File[] baseDirs = {
Environment.getRootDirectory(),
@@ -1870,33 +1862,52 @@
};
for (File baseDir: baseDirs) {
File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
- FileReader confreader = null;
try {
- confreader = new FileReader(confFile);
- parser = Xml.newPullParser();
- parser.setInput(confreader);
- XmlUtils.beginDocument(parser, "devices");
-
- while (true) {
- XmlUtils.nextElement(parser);
- if (!"device".equals(parser.getName())) {
- break;
- }
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- names.add(name);
- }
- }
+ InputStream stream = new FileInputStream(confFile);
+ names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
} catch (FileNotFoundException e) {
// It's ok if the file does not exist.
} catch (Exception e) {
- Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
- } finally {
- try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+ Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e);
}
}
+ return names.toArray(new String[0]);
+ }
- return names.toArray(new String[names.size()]);
+ /**
+ * Flatten a list of pairs into a list, with value positioned directly next to the key
+ * @return Flattened list
+ */
+ private static <T> List<T> flatten(@NonNull List<Pair<T, T>> pairs) {
+ List<T> list = new ArrayList<>(pairs.size() * 2);
+ for (Pair<T, T> pair : pairs) {
+ list.add(pair.first);
+ list.add(pair.second);
+ }
+ return list;
+ }
+
+ /**
+ * Ports are highly platform-specific, so only allow these to be specified in the vendor
+ * directory.
+ */
+ // Native callback
+ private static String[] getInputPortAssociations() {
+ File baseDir = Environment.getVendorDirectory();
+ File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
+
+ try {
+ InputStream stream = new FileInputStream(confFile);
+ List<Pair<String, String>> associations =
+ ConfigurationProcessor.processInputPortAssociations(stream);
+ List<String> associationList = flatten(associations);
+ return associationList.toArray(new String[0]);
+ } catch (FileNotFoundException e) {
+ // Most of the time, file will not exist, which is expected.
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e);
+ }
+ return new String[0];
}
// Native callback.
@@ -1993,8 +2004,7 @@
public void notifyInputChannelBroken(IBinder token);
- public long notifyANR(InputApplicationHandle inputApplicationHandle,
- IBinder token, String reason);
+ public long notifyANR(IBinder token, String reason);
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1dfb86a..a8da968 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2420,8 +2420,9 @@
.setContentText(summary)
.setContentIntent(mImeSwitchPendingIntent);
try {
+ // TODO(b/120076400): Figure out what is the best behavior
if ((mNotificationManager != null)
- && !mIWindowManager.hasNavigationBar()) {
+ && !mIWindowManager.hasNavigationBar(DEFAULT_DISPLAY)) {
if (DEBUG) {
Slog.d(TAG, "--- show notification: label = " + summary);
}
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
index 6fe6324..d5be26a 100644
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
@@ -27,12 +27,13 @@
*
* @hide Only for use within the system server.
*/
+//TODO(b/111276913): rename once the final name is defined
public abstract class IntelligenceManagerInternal {
/**
* Checks whether the given {@code uid} owns the
- * {@link android.service.intelligence.IntelligenceService} implementation associated with the
- * given {@code userId}.
+ * {@link android.service.intelligence.SmartSuggestionsService} implementation associated with
+ * the given {@code userId}.
*/
public abstract boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId);
diff --git a/services/core/java/com/android/server/location/gps_debug.conf b/services/core/java/com/android/server/location/gps_debug.conf
new file mode 100644
index 0000000..34ce96f
--- /dev/null
+++ b/services/core/java/com/android/server/location/gps_debug.conf
@@ -0,0 +1,52 @@
+# Sample file for use for on device debug override only
+# Prefer frameworks/base/core/res/res/values/config.xml and
+# frameworks/base/core/res/res/values-mcc*-mnc*/config.xml
+
+################################
+##### AGPS server settings #####
+################################
+# FOR SUPL SUPPORT, set the following
+# SUPL_HOST=supl.google.com or IP
+# SUPL_PORT=7275
+
+# supl version 2.0
+# SUPL_VER=0x20000
+
+#SUPL_MODE is a bit mask set in config.xml per carrier by default.
+#If it is uncommented here, this value will overwrite the value from
+#config.xml.
+#MSA=0X2
+#MSB=0X1
+#SUPL_MODE=1
+
+# Emergency SUPL, 1=enable, 0=disable
+#SUPL_ES=0
+
+#Choose PDN for Emergency SUPL
+#1 - Use emergency PDN
+#0 - Use regular SUPL PDN for Emergency SUPL
+#USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=0
+
+####################################
+# LTE Positioning Profile Settings
+####################################
+# 0: Enable RRLP on LTE(Default)
+# 1: Enable LPP_User_Plane on LTE
+# 2: Enable LPP_Control_Plane
+# 3: Enable both LPP_User_Plane and LPP_Control_Plane
+#LPP_PROFILE = 2
+
+##################################################
+# Select Positioning Protocol on A-GLONASS system
+##################################################
+# 0x1: RRC CPlane
+# 0x2: RRLP UPlane
+# 0x4: LLP Uplane
+#A_GLONASS_POS_PROTOCOL_SELECT = 0
+
+# Below bit mask configures how GPS functionalities
+# should be locked when user turns off GPS on Settings
+# Set bit 0x1 if MO GPS functionalities are to be locked
+# Set bit 0x2 if NI GPS functionalities are to be locked
+# default - non is locked for backward compatibility
+#GPS_LOCK = 0
diff --git a/services/core/java/com/android/server/locksettings/SP800Derive.java b/services/core/java/com/android/server/locksettings/SP800Derive.java
new file mode 100644
index 0000000..77561fc
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/SP800Derive.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.android.server.locksettings;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Implementation of NIST SP800-108
+ * "Recommendation for Key Derivation Using Pseudorandom Functions"
+ * Hardcoded:
+ * [PRF=HMAC_SHA256]
+ * [CTRLOCATION=BEFORE_FIXED]
+ * [RLEN=32_BITS]
+ * L = 256
+ * L suffix: 32 bits
+ */
+class SP800Derive {
+ private final byte[] mKeyBytes;
+
+ SP800Derive(byte[] keyBytes) {
+ mKeyBytes = keyBytes;
+ }
+
+ private Mac getMac() {
+ try {
+ final Mac m = Mac.getInstance("HmacSHA256");
+ m.init(new SecretKeySpec(mKeyBytes, m.getAlgorithm()));
+ return m;
+ } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void update32(Mac m, int v) {
+ m.update(ByteBuffer.allocate(Integer.BYTES).putInt(v).array());
+ }
+
+ /**
+ * Generate output from a single, fixed input.
+ */
+ public byte[] fixedInput(byte[] fixedInput) {
+ final Mac m = getMac();
+ update32(m, 1); // Hardwired counter value
+ m.update(fixedInput);
+ return m.doFinal();
+ }
+
+ /**
+ * Generate output from a label and context. We add a length field at the end of the context to
+ * disambiguate it from the length even in the presence of zero bytes.
+ */
+ public byte[] withContext(byte[] label, byte[] context) {
+ final Mac m = getMac();
+ // Hardwired counter value: 1
+ update32(m, 1); // Hardwired counter value
+ m.update(label);
+ m.update((byte) 0);
+ m.update(context);
+ update32(m, context.length * 8); // Disambiguate context
+ update32(m, 256); // Hardwired output length
+ return m.doFinal();
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 596daeb..d32c299 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -26,9 +26,9 @@
import android.hardware.weaver.V1_0.WeaverReadResponse;
import android.hardware.weaver.V1_0.WeaverReadStatus;
import android.hardware.weaver.V1_0.WeaverStatus;
-import android.security.GateKeeper;
import android.os.RemoteException;
import android.os.UserManager;
+import android.security.GateKeeper;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.util.ArrayMap;
@@ -102,7 +102,8 @@
private static final int INVALID_WEAVER_SLOT = -1;
private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
- private static final byte SYNTHETIC_PASSWORD_VERSION = 2;
+ private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
+ private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
@@ -128,6 +129,8 @@
private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
+ private static final byte[] PERSONALISATION_CONTEXT =
+ "android-synthetic-password-personalization-context".getBytes();
static class AuthenticationResult {
public AuthenticationToken authToken;
@@ -136,6 +139,7 @@
}
static class AuthenticationToken {
+ private final byte mVersion;
/*
* Here is the relationship between all three fields:
* P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
@@ -146,29 +150,38 @@
private @Nullable byte[] P1;
private @NonNull String syntheticPassword;
+ AuthenticationToken(byte version) {
+ mVersion = version;
+ }
+
+ private byte[] derivePassword(byte[] personalization) {
+ if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
+ return (new SP800Derive(syntheticPassword.getBytes()))
+ .withContext(personalization, PERSONALISATION_CONTEXT);
+ } else {
+ return SyntheticPasswordCrypto.personalisedHash(personalization,
+ syntheticPassword.getBytes());
+ }
+ }
+
public String deriveKeyStorePassword() {
- return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
- PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
+ return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
}
public byte[] deriveGkPassword() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_SP_GK_AUTH);
}
public byte[] deriveDiskEncryptionKey() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_FBE_KEY);
}
public byte[] deriveVendorAuthSecret() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
}
public byte[] derivePasswordHashFactor() {
- return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_PASSWORD_HASH,
- syntheticPassword.getBytes());
+ return derivePassword(PERSONALIZATION_PASSWORD_HASH);
}
private void initialize(byte[] P0, byte[] P1) {
@@ -185,7 +198,7 @@
}
protected static AuthenticationToken create() {
- AuthenticationToken result = new AuthenticationToken();
+ AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
secureRandom(SYNTHETIC_PASSWORD_LENGTH));
return result;
@@ -802,7 +815,16 @@
}
byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
byte[] blob = new byte[content.length + 1 + 1];
- blob[0] = SYNTHETIC_PASSWORD_VERSION;
+ /*
+ * We can upgrade from v1 to v2 because that's just a change in the way that
+ * the SP is stored. However, we can't upgrade to v3 because that is a change
+ * in the way that passwords are derived from the SP.
+ */
+ if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
+ blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
+ } else {
+ blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
+ }
blob[1] = type;
System.arraycopy(content, 0, blob, 2, content.length);
saveState(SP_BLOB_NAME, blob, handle, userId);
@@ -940,7 +962,9 @@
return null;
}
final byte version = blob[0];
- if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
+ if (version != SYNTHETIC_PASSWORD_VERSION_V3
+ && version != SYNTHETIC_PASSWORD_VERSION_V2
+ && version != SYNTHETIC_PASSWORD_VERSION_V1) {
throw new RuntimeException("Unknown blob version");
}
if (blob[1] != type) {
@@ -958,7 +982,7 @@
Log.e(TAG, "Fail to decrypt SP for user " + userId);
return null;
}
- AuthenticationToken result = new AuthenticationToken();
+ AuthenticationToken result = new AuthenticationToken(version);
if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
if (!loadEscrowData(result, userId)) {
Log.e(TAG, "User is not escrowable: " + userId);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c18a79f..93b6620 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -923,7 +923,7 @@
@Override
public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
KeyEvent keyEvent, boolean needWakeLock) {
- if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+ if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
Log.w(TAG, "Attempted to dispatch null or non-media key event.");
return;
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4f4b6bf..4bd8f45 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -64,6 +64,7 @@
private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11;
private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
+ private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
static final int NTWK_BLOCKED_POWER = 0;
static final int NTWK_ALLOWED_NON_METERED = 1;
@@ -145,6 +146,13 @@
}
}
+ void appIdleWlChanged(int uid, boolean isWhitelisted) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getAppIdleWlChangedLog(uid, isWhitelisted));
+ mEventsBuffer.appIdleWlChanged(uid, isWhitelisted);
+ }
+ }
+
void paroleStateChanged(boolean paroleOn) {
synchronized (mLock) {
if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn));
@@ -259,6 +267,10 @@
return "App idle state of uid " + uid + ": " + idle;
}
+ private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) {
+ return "App idle whitelist state of uid " + uid + ": " + isWhitelisted;
+ }
+
private static String getParoleStateChanged(boolean paroleOn) {
return "Parole state: " + paroleOn;
}
@@ -409,6 +421,17 @@
data.timeStamp = System.currentTimeMillis();
}
+ public void appIdleWlChanged(int uid, boolean isWhitelisted) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_APP_IDLE_WL_CHANGED;
+ data.ifield1 = uid;
+ data.bfield1 = isWhitelisted;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
public void paroleStateChanged(boolean paroleOn) {
final Data data = getNextSlot();
if (data == null) return;
@@ -487,6 +510,8 @@
return getDeviceIdleModeEnabled(data.bfield1);
case EVENT_APP_IDLE_STATE_CHANGED:
return getAppIdleChangedLog(data.ifield1, data.bfield1);
+ case EVENT_APP_IDLE_WL_CHANGED:
+ return getAppIdleWlChangedLog(data.ifield1, data.bfield1);
case EVENT_PAROLE_STATE_CHANGED:
return getParoleStateChanged(data.bfield1);
case EVENT_TEMP_POWER_SAVE_WL_CHANGED:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 099671d..7f650ee 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -105,6 +105,12 @@
public abstract void onAdminDataAvailable();
/**
+ * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may
+ * still be in effect.
+ */
+ public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist);
+
+ /**
* Sets a list of packages which are restricted by admin from accessing metered data.
*
* @param packageNames the list of restricted packages.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d799642..0d6dadf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -464,6 +464,10 @@
@GuardedBy("mUidRulesFirstLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
+ // "Power save mode" is the concept used in the DeviceIdleController that includes various
+ // features including Doze and Battery Saver. It include Battery Saver, but "power save mode"
+ // and "battery saver" are not equivalent.
+
/**
* UIDs that have been white-listed to always be able to have network access
* in power save mode, except device idle (doze) still applies.
@@ -484,6 +488,13 @@
private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
/**
+ * UIDs that have been white-listed temporarily to be able to have network access despite being
+ * idle. Other power saving restrictions still apply.
+ */
+ @GuardedBy("mUidRulesFirstLock")
+ private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray();
+
+ /**
* UIDs that have been initially white-listed by system to avoid restricted background.
*/
@GuardedBy("mUidRulesFirstLock")
@@ -543,7 +554,7 @@
final Handler mHandler;
@VisibleForTesting
- public final Handler mUidEventHandler;
+ final Handler mUidEventHandler;
private final ServiceThread mUidEventThread;
@@ -1454,7 +1465,7 @@
}
@VisibleForTesting
- public void updateNetworks() throws InterruptedException {
+ void updateNetworks() throws InterruptedException {
updateNetworksInternal();
final CountDownLatch latch = new CountDownLatch(1);
mHandler.post(() -> {
@@ -1499,7 +1510,7 @@
* @return cycleDay to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
+ int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
int fallbackCycleDay) {
if (config == null) {
return fallbackCycleDay;
@@ -1531,7 +1542,7 @@
* @return warningBytes to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
+ long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
long fallbackWarningBytes) {
if (config == null) {
return fallbackWarningBytes;
@@ -1564,7 +1575,7 @@
* @return limitBytes to use in the mobile NetworkPolicy.
*/
@VisibleForTesting
- public long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
+ long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
long fallbackLimitBytes) {
if (config == null) {
return fallbackLimitBytes;
@@ -2028,7 +2039,7 @@
}
@VisibleForTesting
- public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
+ NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
final RecurrenceRule cycleRule = NetworkPolicy
.buildRule(ZonedDateTime.now().getDayOfMonth(), ZoneId.systemDefault());
@@ -3372,6 +3383,20 @@
fout.decreaseIndent();
}
+ size = mAppIdleTempWhitelistAppIds.size();
+ if (size > 0) {
+ fout.println("App idle whitelist app ids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mAppIdleTempWhitelistAppIds.keyAt(i));
+ fout.print(": ");
+ fout.print(mAppIdleTempWhitelistAppIds.valueAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
size = mDefaultRestrictBackgroundWhitelistUids.size();
if (size > 0) {
fout.println("Default restrict background whitelist uids:");
@@ -3464,7 +3489,7 @@
}
@VisibleForTesting
- public boolean isUidForeground(int uid) {
+ boolean isUidForeground(int uid) {
synchronized (mUidRulesFirstLock) {
return isUidStateForeground(
mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY));
@@ -3640,12 +3665,15 @@
}
/**
+ * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze
+ * mode, and app idle).
+ *
* @param deviceIdleMode if true then we don't consider
* {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
* whitelisted.
*/
@GuardedBy("mUidRulesFirstLock")
- private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) {
+ private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
final int appId = UserHandle.getAppId(uid);
boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
|| mPowerSaveWhitelistAppIds.get(appId);
@@ -3660,7 +3688,7 @@
@GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
if (enabled) {
- final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid,
+ final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid,
chain == FIREWALL_CHAIN_DOZABLE);
if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
@@ -3712,8 +3740,10 @@
if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
&& !isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
+ if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
} else {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+ if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT");
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -3896,7 +3926,61 @@
return UserHandle.isApp(uid) && hasInternetPermissions(uid);
}
- private boolean isUidIdle(int uid) {
+ /**
+ * Set whether or not an app should be whitelisted for network access while in app idle. Other
+ * power saving restrictions may still apply.
+ */
+ @VisibleForTesting
+ void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mUidRulesFirstLock) {
+ if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) {
+ // No change.
+ return;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mLogger.appIdleWlChanged(uid, shouldWhitelist);
+ if (shouldWhitelist) {
+ mAppIdleTempWhitelistAppIds.put(uid, true);
+ } else {
+ mAppIdleTempWhitelistAppIds.delete(uid);
+ }
+ updateRuleForAppIdleUL(uid);
+ updateRulesForPowerRestrictionsUL(uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ /** Return the list of UIDs currently in the app idle whitelist. */
+ @VisibleForTesting
+ int[] getAppIdleWhitelist() {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mUidRulesFirstLock) {
+ final int len = mAppIdleTempWhitelistAppIds.size();
+ int[] uids = new int[len];
+ for (int i = 0; i < len; ++i) {
+ uids[i] = mAppIdleTempWhitelistAppIds.keyAt(i);
+ }
+ return uids;
+ }
+ }
+
+ /** Returns if the UID is currently considered idle. */
+ @VisibleForTesting
+ boolean isUidIdle(int uid) {
+ synchronized (mUidRulesFirstLock) {
+ if (mAppIdleTempWhitelistAppIds.get(uid)) {
+ // UID is temporarily whitelisted.
+ return false;
+ }
+ }
+
final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
final int userId = UserHandle.getUserId(uid);
@@ -3940,6 +4024,7 @@
mPowerSaveWhitelistExceptIdleAppIds.delete(uid);
mPowerSaveWhitelistAppIds.delete(uid);
mPowerSaveTempWhitelistAppIds.delete(uid);
+ mAppIdleTempWhitelistAppIds.delete(uid);
// ...then update iptables asynchronously.
mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -3984,7 +4069,7 @@
* <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're
* also blacklisted.
* <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}),
- * no UIDs other those whitelisted will have access.
+ * no UIDs other than those whitelisted will have access.
* <ul>
*
* <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the
@@ -4194,7 +4279,7 @@
final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
- final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode);
+ final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
final int oldRule = oldUidRules & MASK_ALL_NETWORKS;
int newRule = RULE_NONE;
@@ -4761,13 +4846,13 @@
}
@VisibleForTesting
- public void addIdleHandler(IdleHandler handler) {
+ void addIdleHandler(IdleHandler handler) {
mHandler.getLooper().getQueue().addIdleHandler(handler);
}
@GuardedBy("mUidRulesFirstLock")
@VisibleForTesting
- public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
+ void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
mRestrictBackgroundPowerState = result;
boolean restrictBackground = result.batterySaverEnabled;
@@ -5023,6 +5108,11 @@
}
@Override
+ public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+ NetworkPolicyManagerService.this.setAppIdleWhitelist(uid, shouldWhitelist);
+ }
+
+ @Override
public void setMeteredRestrictedPackages(Set<String> packageNames, int userId) {
setMeteredRestrictedPackagesInternal(packageNames, userId);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 56d41c5..156c01d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -78,6 +78,8 @@
pw.println(" Adds a UID to the whitelist for restrict background usage.");
pw.println(" add restrict-background-blacklist UID");
pw.println(" Adds a UID to the blacklist for restrict background usage.");
+ pw.println(" add app-idle-whitelist UID");
+ pw.println(" Adds a UID to the temporary app idle whitelist.");
pw.println(" get restrict-background");
pw.println(" Gets the global restrict background usage status.");
pw.println(" list wifi-networks [true|false]");
@@ -92,6 +94,8 @@
pw.println(" Removes a UID from the whitelist for restrict background usage.");
pw.println(" remove restrict-background-blacklist UID");
pw.println(" Removes a UID from the blacklist for restrict background usage.");
+ pw.println(" remove app-idle-whitelist UID");
+ pw.println(" Removes a UID from the temporary app idle whitelist.");
pw.println(" set metered-network ID [undefined|true|false]");
pw.println(" Toggles whether the given wi-fi network is metered.");
pw.println(" set restrict-background BOOLEAN");
@@ -142,6 +146,8 @@
return -1;
}
switch(type) {
+ case "app-idle-whitelist":
+ return listAppIdleWhitelist();
case "wifi-networks":
return listWifiNetworks();
case "restrict-background-whitelist":
@@ -165,6 +171,8 @@
return addRestrictBackgroundWhitelist();
case "restrict-background-blacklist":
return addRestrictBackgroundBlacklist();
+ case "app-idle-whitelist":
+ return addAppIdleWhitelist();
}
pw.println("Error: unknown add type '" + type + "'");
return -1;
@@ -182,14 +190,20 @@
return removeRestrictBackgroundWhitelist();
case "restrict-background-blacklist":
return removeRestrictBackgroundBlacklist();
+ case "app-idle-whitelist":
+ return removeAppIdleWhitelist();
}
pw.println("Error: unknown remove type '" + type + "'");
return -1;
}
private int listUidPolicies(String msg, int policy) throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
final int[] uids = mInterface.getUidsWithPolicy(policy);
+ return listUidList(msg, uids);
+ }
+
+ private int listUidList(String msg, int[] uids) {
+ final PrintWriter pw = getOutPrintWriter();
pw.print(msg); pw.print(": ");
if (uids.length == 0) {
pw.println("none");
@@ -214,6 +228,12 @@
POLICY_REJECT_METERED_BACKGROUND);
}
+ private int listAppIdleWhitelist() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final int[] uids = mInterface.getAppIdleWhitelist();
+ return listUidList("App Idle whitelisted UIDs", uids);
+ }
+
private int getRestrictBackground() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
pw.print("Restrict background status: ");
@@ -277,6 +297,23 @@
return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND);
}
+ private int setAppIdleWhitelist(boolean isWhitelisted) {
+ final int uid = getUidFromNextArg();
+ if (uid < 0) {
+ return uid;
+ }
+ mInterface.setAppIdleWhitelist(uid, isWhitelisted);
+ return 0;
+ }
+
+ private int addAppIdleWhitelist() throws RemoteException {
+ return setAppIdleWhitelist(true);
+ }
+
+ private int removeAppIdleWhitelist() throws RemoteException {
+ return setAppIdleWhitelist(false);
+ }
+
private int listWifiNetworks() {
final PrintWriter pw = getOutPrintWriter();
final String arg = getNextArg();
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 9222740..84bb13e 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import android.app.Notification;
import android.service.notification.NotificationStats;
import com.android.internal.statusbar.NotificationVisibility;
@@ -26,7 +27,7 @@
void onNotificationClick(int callingUid, int callingPid, String key,
NotificationVisibility nv);
void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex,
- NotificationVisibility nv);
+ Notification.Action action, NotificationVisibility nv, boolean generatedByAssistant);
void onNotificationClear(int callingUid, int callingPid,
String pkg, String tag, int id, int userId, String key,
@NotificationStats.DismissalSurface int dismissalSurface,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6005872..25a1626 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -198,6 +198,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -206,6 +207,7 @@
import com.android.internal.util.XmlUtils;
import com.android.server.DeviceIdleController;
import com.android.server.EventLogTags;
+import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.lights.Light;
@@ -264,11 +266,12 @@
// message codes
static final int MESSAGE_DURATION_REACHED = 2;
- static final int MESSAGE_SAVE_POLICY_FILE = 3;
+ // 3: removed to a different handler
static final int MESSAGE_SEND_RANKING_UPDATE = 4;
static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7;
+ static final int MESSAGE_ON_PACKAGE_CHANGED = 8;
// ranking thread messages
private static final int MESSAGE_RECONSIDER_RANKING = 1000;
@@ -425,6 +428,7 @@
private GroupHelper mGroupHelper;
private int mAutoGroupAtCount;
private boolean mIsTelevision;
+ private boolean mIsAutomotive;
private MetricsLogger mMetricsLogger;
private Predicate<String> mAllowedManagedServicePackages;
@@ -570,7 +574,7 @@
mListeners.migrateToXml();
mAssistants.migrateToXml();
mConditionProviders.migrateToXml();
- savePolicyFile();
+ handleSavePolicyFile();
}
mAssistants.ensureAssistant();
@@ -600,31 +604,28 @@
}
}
- public void savePolicyFile() {
- mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
- mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
- }
-
private void handleSavePolicyFile() {
- if (DBG) Slog.d(TAG, "handleSavePolicyFile");
- synchronized (mPolicyFile) {
- final FileOutputStream stream;
- try {
- stream = mPolicyFile.startWrite();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file", e);
- return;
- }
+ IoThread.getHandler().post(() -> {
+ if (DBG) Slog.d(TAG, "handleSavePolicyFile");
+ synchronized (mPolicyFile) {
+ final FileOutputStream stream;
+ try {
+ stream = mPolicyFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file", e);
+ return;
+ }
- try {
- writePolicyXml(stream, false /*forBackup*/);
- mPolicyFile.finishWrite(stream);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file, restoring backup", e);
- mPolicyFile.failWrite(stream);
+ try {
+ writePolicyXml(stream, false /*forBackup*/);
+ mPolicyFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file, restoring backup", e);
+ mPolicyFile.failWrite(stream);
+ }
}
- }
- BackupManager.dataChanged(getContext().getPackageName());
+ BackupManager.dataChanged(getContext().getPackageName());
+ });
}
private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
@@ -752,7 +753,8 @@
@Override
public void onNotificationActionClick(int callingUid, int callingPid, String key,
- int actionIndex, NotificationVisibility nv) {
+ int actionIndex, Notification.Action action, NotificationVisibility nv,
+ boolean generatedByAssistant) {
exitIdle();
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
@@ -772,6 +774,8 @@
nv.rank, nv.count);
nv.recycle();
reportUserInteraction(r);
+ mAssistants.notifyAssistantActionClicked(
+ r.sbn, actionIndex, action, generatedByAssistant);
}
}
@@ -1130,12 +1134,7 @@
}
}
- mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
- mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
- mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
- mPreferencesHelper.onPackagesChanged(
- removingPackage, changeUserId, pkgList, uidList);
- savePolicyFile();
+ mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
}
}
};
@@ -1202,7 +1201,7 @@
mListeners.onUserRemoved(userId);
mConditionProviders.onUserRemoved(userId);
mAssistants.onUserRemoved(userId);
- savePolicyFile();
+ handleSavePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
@@ -1390,6 +1389,11 @@
}
@VisibleForTesting
+ void setIsAutomotive(boolean isAutomotive) {
+ mIsAutomotive = isAutomotive;
+ }
+
+ @VisibleForTesting
void setIsTelevision(boolean isTelevision) {
mIsTelevision = isTelevision;
}
@@ -1453,7 +1457,7 @@
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -1545,6 +1549,9 @@
mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
|| mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
+
+ mIsAutomotive =
+ mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
}
@Override
@@ -1752,7 +1759,7 @@
modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate,
@@ -2220,7 +2227,7 @@
Slog.w(TAG, "Can't notify app about app block change", e);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
/**
@@ -2277,7 +2284,7 @@
public void setShowBadge(String pkg, int uid, boolean showBadge) {
checkCallerIsSystem();
mPreferencesHelper.setShowBadge(pkg, uid, showBadge);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2293,7 +2300,7 @@
if (info != null) {
mPreferencesHelper.setNotificationDelegate(
callingPkg, callingUid, delegate, info.uid);
- savePolicyFile();
+ handleSavePolicyFile();
}
} catch (RemoteException e) {
// :(
@@ -2304,7 +2311,7 @@
public void revokeNotificationDelegate(String callingPkg) {
checkCallerIsSameApp(callingPkg);
mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid());
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2339,7 +2346,7 @@
NotificationChannelGroup group) throws RemoteException {
enforceSystemOrSystemUI("Caller not system or systemui");
createNotificationChannelGroup(pkg, uid, group, false, false);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2352,7 +2359,7 @@
final NotificationChannelGroup group = groups.get(i);
createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
private void createNotificationChannelsImpl(String pkg, int uid,
@@ -2370,7 +2377,7 @@
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2415,7 +2422,7 @@
UserHandle.getUserHandleForUid(callingUid),
mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -2457,7 +2464,7 @@
mListeners.notifyNotificationChannelGroupChanged(
pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -2590,7 +2597,7 @@
true, UserHandle.getCallingUserId(), packages, uids);
}
- savePolicyFile();
+ handleSavePolicyFile();
}
@@ -3378,7 +3385,7 @@
final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
try {
readPolicyXml(bais, true /*forRestore*/);
- savePolicyFile();
+ handleSavePolicyFile();
} catch (NumberFormatException | XmlPullParserException | IOException e) {
Slog.w(TAG, "applyRestore: error reading payload", e);
}
@@ -3419,7 +3426,7 @@
.setPackage(pkg)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3563,7 +3570,7 @@
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3589,7 +3596,7 @@
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
UserHandle.of(userId), null);
- savePolicyFile();
+ handleSavePolicyFile();
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -3672,7 +3679,7 @@
verifyPrivilegedListener(token, user, false);
createNotificationChannelGroup(
pkg, getUidForPackageAndUser(pkg, user), group, false, true);
- savePolicyFile();
+ handleSavePolicyFile();
}
@Override
@@ -3721,7 +3728,7 @@
}
if (allow != mLockScreenAllowSecureNotifications) {
mLockScreenAllowSecureNotifications = allow;
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -4244,18 +4251,7 @@
// Fix the notification as best we can.
try {
- final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
- pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
- Notification.addFieldsFromContext(ai, notification);
-
- int canColorize = mPackageManagerClient.checkPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
- if (canColorize == PERMISSION_GRANTED) {
- notification.flags |= Notification.FLAG_CAN_COLORIZE;
- } else {
- notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
- }
+ fixNotification(notification, pkg, userId);
} catch (NameNotFoundException e) {
Slog.e(TAG, "Cannot create a context for sending app", e);
@@ -4356,6 +4352,33 @@
mHandler.post(new EnqueueNotificationRunnable(userId, r));
}
+ @VisibleForTesting
+ protected void fixNotification(Notification notification, String pkg, int userId)
+ throws NameNotFoundException {
+ final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
+ pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
+ Notification.addFieldsFromContext(ai, notification);
+
+ int canColorize = mPackageManagerClient.checkPermission(
+ android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
+ if (canColorize == PERMISSION_GRANTED) {
+ notification.flags |= Notification.FLAG_CAN_COLORIZE;
+ } else {
+ notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
+ }
+
+ if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ int fullscreenIntentPermission = mPackageManagerClient.checkPermission(
+ android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg);
+ if (fullscreenIntentPermission != PERMISSION_GRANTED) {
+ notification.fullScreenIntent = null;
+ Log.w(TAG, "Package " + pkg +
+ ": Use of fullScreenIntent requires the USE_FULL_SCREEN_INTENT permission");
+ }
+ }
+ }
+
private void doChannelWarningToast(CharSequence toastText) {
Binder.withCleanCallingIdentity(() -> {
final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
@@ -4464,7 +4487,7 @@
Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
}
mSnoozeHelper.update(userId, r);
- savePolicyFile();
+ handleSavePolicyFile();
return false;
}
@@ -4595,7 +4618,7 @@
mSnoozeHelper.snooze(r, mDuration);
}
r.recordSnoozed();
- savePolicyFile();
+ handleSavePolicyFile();
}
}
@@ -4673,7 +4696,7 @@
if (mReason != REASON_SNOOZED) {
final boolean wasSnoozed = mSnoozeHelper.cancel(mUserId, mPkg, mTag, mId);
if (wasSnoozed) {
- savePolicyFile();
+ handleSavePolicyFile();
}
}
}
@@ -5117,7 +5140,9 @@
// Should this notification make noise, vibe, or use the LED?
final boolean aboveThreshold =
- record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
+ mIsAutomotive
+ ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
+ : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
// Remember if this notification already owns the notification channels.
boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
@@ -5688,6 +5713,16 @@
}
}
+ private void handleOnPackageChanged(boolean removingPackage, int changeUserId,
+ String[] pkgList, int[] uidList) {
+ mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
+ mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
+ mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
+ mPreferencesHelper.onPackagesChanged(
+ removingPackage, changeUserId, pkgList, uidList);
+ handleSavePolicyFile();
+ }
+
protected class WorkerHandler extends Handler
{
public WorkerHandler(Looper looper) {
@@ -5700,13 +5735,10 @@
switch (msg.what)
{
case MESSAGE_DURATION_REACHED:
- handleDurationReached((ToastRecord)msg.obj);
+ handleDurationReached((ToastRecord) msg.obj);
break;
case MESSAGE_FINISH_TOKEN_TIMEOUT:
- handleKillTokenTimeout((ToastRecord)msg.obj);
- break;
- case MESSAGE_SAVE_POLICY_FILE:
- handleSavePolicyFile();
+ handleKillTokenTimeout((ToastRecord) msg.obj);
break;
case MESSAGE_SEND_RANKING_UPDATE:
handleSendRankingUpdate();
@@ -5717,6 +5749,12 @@
case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
handleListenerInterruptionFilterChanged(msg.arg1);
break;
+ case MESSAGE_ON_PACKAGE_CHANGED:
+ SomeArgs args = (SomeArgs) msg.obj;
+ handleOnPackageChanged((boolean) args.arg1, args.argi1, (String[]) args.arg2,
+ (int[]) args.arg3);
+ args.recycle();
+ break;
}
}
@@ -5732,6 +5770,16 @@
sendMessage(Message.obtain(this, cancelRunnable));
}
}
+
+ protected void scheduleOnPackageChanged(boolean removingPackage, int changeUserId,
+ String[] pkgList, int[] uidList) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = removingPackage;
+ args.argi1 = changeUserId;
+ args.arg2 = pkgList;
+ args.arg3 = uidList;
+ sendMessage(Message.obtain(this, MESSAGE_ON_PACKAGE_CHANGED, args));
+ }
}
private final class RankingHandlerWorker extends Handler implements RankingHandler
@@ -6209,7 +6257,7 @@
Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
}
mSnoozeHelper.repost(key);
- savePolicyFile();
+ handleSavePolicyFile();
}
@GuardedBy("mNotificationLock")
@@ -6923,6 +6971,27 @@
});
}
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantActionClicked(
+ final StatusBarNotification sbn, int actionIndex, Notification.Action action,
+ boolean generatedByAssistant) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onActionClicked(
+ key,
+ action,
+ generatedByAssistant
+ ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+ : NotificationAssistantService.SOURCE_FROM_APP);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+ }
+ });
+ }
/**
* asynchronously notify the assistant that a notification has been snoozed until a
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f279af0..94d276c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -309,6 +309,7 @@
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
+ newConfig.automaticRules.put(rule.id, rule);
if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
} else {
diff --git a/services/core/java/com/android/server/oemlock/OemLock.java b/services/core/java/com/android/server/oemlock/OemLock.java
index ee70c29..352884b 100644
--- a/services/core/java/com/android/server/oemlock/OemLock.java
+++ b/services/core/java/com/android/server/oemlock/OemLock.java
@@ -19,6 +19,9 @@
import android.annotation.Nullable;
abstract class OemLock {
+ @Nullable
+ abstract String getLockName();
+
abstract void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature);
abstract boolean isOemUnlockAllowedByCarrier();
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index a6200bf..6e82c24 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -113,6 +113,19 @@
*/
private final IBinder mService = new IOemLockService.Stub() {
@Override
+ @Nullable
+ public String getLockName() {
+ enforceManageCarrierOemUnlockPermission();
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mOemLock.getLockName();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
enforceManageCarrierOemUnlockPermission();
enforceUserIsAdmin();
diff --git a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
index d9362d4..a1c27d6 100644
--- a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
+++ b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
@@ -40,6 +40,12 @@
}
@Override
+ @Nullable
+ String getLockName() {
+ return "";
+ }
+
+ @Override
void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
// Note: this implementation does not require a signature
if (signature != null) {
diff --git a/services/core/java/com/android/server/oemlock/VendorLock.java b/services/core/java/com/android/server/oemlock/VendorLock.java
index 1b9de36..37540d0 100644
--- a/services/core/java/com/android/server/oemlock/VendorLock.java
+++ b/services/core/java/com/android/server/oemlock/VendorLock.java
@@ -22,8 +22,6 @@
import android.hardware.oemlock.V1_0.OemLockSecureStatus;
import android.hardware.oemlock.V1_0.OemLockStatus;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.util.Slog;
import java.util.ArrayList;
@@ -55,14 +53,49 @@
}
@Override
+ @Nullable
+ String getLockName() {
+ final Integer[] requestStatus = new Integer[1];
+ final String[] lockName = new String[1];
+
+ try {
+ mOemLock.getName((status, name) -> {
+ requestStatus[0] = status;
+ lockName[0] = name;
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get name from HAL", e);
+ throw e.rethrowFromSystemServer();
+ }
+
+ switch (requestStatus[0]) {
+ case OemLockStatus.OK:
+ // Success
+ return lockName[0];
+
+ case OemLockStatus.FAILED:
+ Slog.e(TAG, "Failed to get OEM lock name.");
+ return null;
+
+ default:
+ Slog.e(TAG, "Unknown return value indicates code is out of sync with HAL");
+ return null;
+ }
+ }
+
+ @Override
void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
try {
- switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, toByteArrayList(signature))) {
+ ArrayList<Byte> signatureBytes = toByteArrayList(signature);
+ switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, signatureBytes)) {
case OemLockSecureStatus.OK:
Slog.i(TAG, "Updated carrier allows OEM lock state to: " + allowed);
return;
case OemLockSecureStatus.INVALID_SIGNATURE:
+ if (signatureBytes.isEmpty()) {
+ throw new IllegalArgumentException("Signature required for carrier unlock");
+ }
throw new SecurityException(
"Invalid signature used in attempt to carrier unlock");
@@ -154,9 +187,9 @@
}
}
- private ArrayList toByteArrayList(byte[] data) {
+ private ArrayList<Byte> toByteArrayList(byte[] data) {
if (data == null) {
- return null;
+ return new ArrayList<Byte>();
}
ArrayList<Byte> result = new ArrayList<Byte>(data.length);
for (final byte b : data) {
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 731e6bc..6d59827 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -46,7 +46,7 @@
* Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
*/
class IdmapManager {
- private static final boolean FEATURE_FLAG_IDMAP2 = false;
+ private static final boolean FEATURE_FLAG_IDMAP2 = true;
private final Installer mInstaller;
private IIdmap2 mIdmap2Service;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b490381..7f1fb6c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -310,7 +310,7 @@
.setPackage(packageName),
user);
if (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 0) == 0) {
+ Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
return launcherActivities;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6038e24..dab4e79 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -53,12 +53,12 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -117,8 +117,7 @@
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.permission.PermissionsState
- .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import android.Manifest;
import android.annotation.IntDef;
@@ -198,6 +197,7 @@
import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UsesPermissionInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
@@ -314,8 +314,7 @@
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy
- .DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
import com.android.server.pm.permission.PermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionManagerService;
@@ -374,6 +373,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -5397,7 +5397,7 @@
synchronized (mPackages) {
Signature[] s1;
Signature[] s2;
- Object obj = mSettings.getUserIdLPr(uid1);
+ Object obj = mSettings.getSettingLPr(uid1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
@@ -5416,7 +5416,7 @@
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- obj = mSettings.getUserIdLPr(uid2);
+ obj = mSettings.getSettingLPr(uid2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
@@ -5475,7 +5475,7 @@
// reader
synchronized (mPackages) {
final PackageParser.SigningDetails signingDetails;
- final Object obj = mSettings.getUserIdLPr(uid);
+ final Object obj = mSettings.getSettingLPr(uid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
@@ -5588,7 +5588,7 @@
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getSettingLPr(uid);
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return null;
@@ -5624,7 +5624,7 @@
return null;
}
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
@@ -5652,7 +5652,7 @@
synchronized (mPackages) {
for (int i = uids.length - 1; i >= 0; i--) {
final int uid = uids[i];
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
names[i] = "shared:" + sus.name;
@@ -5701,7 +5701,7 @@
return 0;
}
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.pkgFlags;
@@ -5723,7 +5723,7 @@
return 0;
}
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+ Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.pkgPrivateFlags;
@@ -5746,7 +5746,7 @@
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getSettingLPr(uid);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
final Iterator<PackageSetting> it = sus.packages.iterator();
@@ -6386,7 +6386,7 @@
callingUid = mIsolatedOwners.get(callingUid);
}
final int appId = UserHandle.getAppId(callingUid);
- final Object obj = mSettings.getUserIdLPr(appId);
+ final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
@@ -8716,7 +8716,13 @@
| SCAN_UPDATE_SIGNATURE, currentTime, user);
if (scanResult.success) {
synchronized (mPackages) {
- commitScanResultLocked(scanResult);
+ try {
+ prepareScanResultLocked(scanResult);
+ commitScanResultLocked(scanResult);
+ } catch (PackageManagerException e) {
+ unprepareScanResultLocked(scanResult);
+ throw e;
+ }
}
}
@@ -10115,7 +10121,37 @@
}
}
for (ScanResult result : results) {
- commitScanResultLocked(result);
+ try {
+ prepareScanResultLocked(result);
+ commitScanResultLocked(result);
+ } catch (PackageManagerException e) {
+ unprepareScanResultLocked(result);
+ throw e;
+ }
+ }
+ }
+ }
+
+ /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */
+ private void prepareScanResultLocked(@NonNull ScanResult result)
+ throws PackageManagerException {
+ if (!result.existingSettingCopied) {
+ // THROWS: when we can't allocate a user id. add call to check if there's
+ // enough space to ensure we won't throw; otherwise, don't modify state
+ mSettings.registerAppIdLPw(result.pkgSetting);
+ }
+ }
+
+ /**
+ * Reverts any changes to the system that were made by
+ * {@link #prepareScanResultLocked(ScanResult)}
+ */
+ private void unprepareScanResultLocked(@NonNull ScanResult result) {
+ if (!result.existingSettingCopied) {
+ // iff we've acquired an app ID for a new package setting, remove it so that it can be
+ // acquired by another request.
+ if (result.pkgSetting.appId > 0) {
+ mSettings.removeAppIdLPw(result.pkgSetting.appId);
}
}
}
@@ -10154,10 +10190,6 @@
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
}
- // THROWS: when we can't allocate a user id. add call to check if there's
- // enough space to ensure we won't throw; otherwise, don't modify state
- mSettings.addUserToSettingLPw(pkgSetting);
-
if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
mTransferedPackages.add(originalPkgSetting.name);
}
@@ -10268,12 +10300,21 @@
compareSignatures(
signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
- // Treat mismatched signatures on system packages using a shared UID as
- // fatal for the system overall, rather than just failing to install
- // whichever package happened to be scanned later.
- throw new IllegalStateException(
- "Signature mismatch on system package " + pkg.packageName
- + " for shared user " + pkgSetting.sharedUser);
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
+ // Mismatched signatures is an error and silently skipping system
+ // packages will likely break the device in unforeseen ways. However,
+ // we allow the device to boot anyway because, prior to P, vendors were
+ // not expecting the platform to crash in this situation.
+ throw new PackageManagerException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Signature mismatch for shared user: " + pkgSetting.sharedUser);
+ } else {
+ // Treat mismatched signatures on system packages using a shared UID as
+ // fatal for the system overall, rather than just failing to install
+ // whichever package happened to be scanned later.
+ throw new IllegalStateException("Signature mismatch on system package "
+ + pkg.packageName + " for shared user " + pkgSetting.sharedUser);
+ }
}
signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
@@ -11211,6 +11252,26 @@
}
}
}
+
+ // Check permission usage info requirements.
+ if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ for (UsesPermissionInfo upi : pkg.usesPermissionInfos) {
+ if (!mPermissionManager.isPermissionUsageInfoRequired(upi.getPermission())) {
+ continue;
+ }
+ if (upi.getDataSentOffDevice() == UsesPermissionInfo.USAGE_UNDEFINED
+ || upi.getDataSharedWithThirdParty()
+ == UsesPermissionInfo.USAGE_UNDEFINED
+ || upi.getDataUsedForMonetization()
+ == UsesPermissionInfo.USAGE_UNDEFINED
+ || upi.getDataRetention() == UsesPermissionInfo.RETENTION_UNDEFINED) {
+ // STOPSHIP: Make this throw
+ Slog.wtf(TAG, "Package " + pkg.packageName + " does not provide usage "
+ + "information for permission " + upi.getPermission()
+ + ". This will be a fatal error in Q.");
+ }
+ }
+ }
}
}
@@ -13479,7 +13540,7 @@
}
Signature[] callerSignature;
- Object obj = mSettings.getUserIdLPr(callingUid);
+ Object obj = mSettings.getSettingLPr(callingUid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature =
@@ -15159,8 +15220,11 @@
}
}
private static class ReconcileFailure extends PackageManagerException {
- public ReconcileFailure(String message) {
- super("Invalid reconcile request: " + message);
+ ReconcileFailure(String message) {
+ super("Reconcile failed: " + message);
+ }
+ ReconcileFailure(int reason, String message) {
+ super(reason, "Reconcile failed: " + message);
}
}
@@ -15179,10 +15243,12 @@
@PackageManager.InstallFlags
public final int installFlags;
public final InstallArgs installArgs;
+ public final DeletePackageAction deletePackageAction;
private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
- String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) {
+ String volumeUuid, PrepareResult prepareResult, ScanResult scanResult,
+ DeletePackageAction deletePackageAction) {
this.installArgs = installArgs;
this.pkgSetting = pkgSetting;
this.installForUser = installForUser;
@@ -15191,6 +15257,7 @@
this.volumeUuid = volumeUuid;
this.prepareResult = prepareResult;
this.scanResult = scanResult;
+ this.deletePackageAction = deletePackageAction;
}
}
@@ -15203,14 +15270,30 @@
final ScanResult scanResult = request.scannedPackages.get(installPackageName);
final InstallArgs installArgs = request.installArgs.get(installPackageName);
final PackageInstalledInfo res = request.installResults.get(installPackageName);
+ final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
if (scanResult == null || installArgs == null || res == null) {
throw new ReconcileFailure(
"inputs not balanced; missing argument for " + installPackageName);
}
+ final DeletePackageAction deletePackageAction;
+ // we only want to try to delete for non system apps
+ if (prepareResult.replace && !prepareResult.system) {
+ deletePackageAction = mayDeletePackageLocked(res.removedInfo,
+ prepareResult.originalPs, prepareResult.disabledPs,
+ prepareResult.childPackageSettings);
+ if (deletePackageAction == null) {
+ throw new ReconcileFailure(
+ PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+ "May not delete " + installPackageName + " to replace");
+ }
+ } else {
+ deletePackageAction = null;
+ }
result.put(installPackageName,
new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
res, installArgs.installFlags, installArgs.volumeUuid,
- request.preparedPackages.get(installPackageName), scanResult));
+ request.preparedPackages.get(installPackageName), scanResult,
+ deletePackageAction));
}
return result;
}
@@ -15282,29 +15365,29 @@
final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA
| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
- // First delete the existing package while retaining the data directory
- if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags,
- res.removedInfo, true, pkg)) {
- // If the existing package wasn't successfully deleted
- res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "replaceNonSystemPackageLI");
- return false;
- } else {
- // Successfully deleted the old package; proceed with replace.
-
- // If deleted package lived in a container, give users a chance to
- // relinquish resources before killing.
- if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + oldPackage
- + " is ASEC-hosted -> UNAVAILABLE");
- }
- final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
- final ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(oldPackage.applicationInfo.packageName);
- sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ try {
+ executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
+ null, true, request.mAllUsers, deleteFlags, true, pkg);
+ } catch (SystemDeleteException e) {
+ if (Build.IS_ENG) {
+ throw new RuntimeException("Unexpected failure", e);
+ // ignore; not possible for non-system app
}
}
+ // Successfully deleted the old package; proceed with replace.
+
+ // If deleted package lived in a container, give users a chance to
+ // relinquish resources before killing.
+ if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + oldPackage
+ + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
+ final ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(oldPackage.applicationInfo.packageName);
+ sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ }
// Update the in-memory copy of the previous code paths.
PackageSetting ps1 = mSettings.mPackages.get(
@@ -15358,8 +15441,10 @@
try {
+ prepareScanResultLocked(scanResult);
commitScanResultLocked(scanResult);
} catch (PackageManagerException e) {
+ unprepareScanResultLocked(scanResult);
res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
res.setError("Package couldn't be installed in " + pkg.codePath, e);
return false;
@@ -15710,13 +15795,16 @@
@Nullable
public final String renamedPackage;
public final PackageFreezer freezer;
+ public final PackageSetting originalPs;
+ public final PackageSetting disabledPs;
+ public final PackageSetting[] childPackageSettings;
private PrepareResult(int installReason, String volumeUuid,
String installerPackageName, UserHandle user, boolean replace, int scanFlags,
int parseFlags, PackageParser.Package existingPackage,
PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
- String renamedPackage,
- PackageFreezer freezer) {
+ String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
+ PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
this.installReason = installReason;
this.volumeUuid = volumeUuid;
this.installerPackageName = installerPackageName;
@@ -15730,6 +15818,9 @@
this.system = system;
this.renamedPackage = renamedPackage;
this.freezer = freezer;
+ this.originalPs = originalPs;
+ this.disabledPs = disabledPs;
+ this.childPackageSettings = childPackageSettings;
}
}
@@ -16233,7 +16324,9 @@
String targetVolumeUuid = volumeUuid;
int targetScanFlags = scanFlags;
int targetParseFlags = parseFlags;
-
+ final PackageSetting ps;
+ final PackageSetting disabledPs;
+ final PackageSetting[] childPackages;
if (replace) {
targetVolumeUuid = null;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
@@ -16253,7 +16346,6 @@
final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final PackageParser.Package oldPackage;
- final PackageSetting ps;
final String pkgName11 = pkg.packageName;
final int[] allUsers;
final int[] installedUsers;
@@ -16280,6 +16372,7 @@
}
ps = mSettings.mPackages.get(pkgName11);
+ disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
// verify signatures are valid
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -16376,49 +16469,52 @@
res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
}
- final int childCount = (oldPackage.childPackages != null)
- ? oldPackage.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- boolean childPackageUpdated = false;
- PackageParser.Package childPkg = oldPackage.childPackages.get(i);
- final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
- if (res.addedChildPackages != null) {
- PackageInstalledInfo childRes = res.addedChildPackages.get(
- childPkg.packageName);
- if (childRes != null) {
- childRes.removedInfo.uid = childPkg.applicationInfo.uid;
- childRes.removedInfo.removedPackage = childPkg.packageName;
- if (childPs != null) {
- childRes.removedInfo.installerPackageName =
- childPs.installerPackageName;
- }
- childRes.removedInfo.isUpdate = true;
- childRes.removedInfo.installReasons = res.removedInfo.installReasons;
- childPackageUpdated = true;
- }
- }
- if (!childPackageUpdated) {
- PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
- childRemovedRes.removedPackage = childPkg.packageName;
- if (childPs != null) {
- childRemovedRes.installerPackageName = childPs.installerPackageName;
- }
- childRemovedRes.isUpdate = false;
- childRemovedRes.dataRemoved = true;
- synchronized (mPackages) {
- if (childPs != null) {
- childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers,
- true);
+ childPackages = mSettings.getChildSettingsLPr(ps);
+ if (childPackages != null) {
+ for (PackageSetting childPs : childPackages) {
+ boolean childPackageUpdated = false;
+ PackageParser.Package childPkg = (childPs == null) ? null : childPs.pkg;
+ if (res.addedChildPackages != null) {
+ PackageInstalledInfo childRes = res.addedChildPackages.get(
+ childPkg.packageName);
+ if (childRes != null) {
+ childRes.removedInfo.uid = childPkg.applicationInfo.uid;
+ childRes.removedInfo.removedPackage = childPkg.packageName;
+ if (childPs != null) {
+ childRes.removedInfo.installerPackageName =
+ childPs.installerPackageName;
+ }
+ childRes.removedInfo.isUpdate = true;
+ childRes.removedInfo.installReasons =
+ res.removedInfo.installReasons;
+ childPackageUpdated = true;
}
}
- if (res.removedInfo.removedChildPackages == null) {
- res.removedInfo.removedChildPackages = new ArrayMap<>();
+ if (!childPackageUpdated) {
+ PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
+ childRemovedRes.removedPackage = childPkg.packageName;
+ if (childPs != null) {
+ childRemovedRes.installerPackageName = childPs.installerPackageName;
+ }
+ childRemovedRes.isUpdate = false;
+ childRemovedRes.dataRemoved = true;
+ synchronized (mPackages) {
+ if (childPs != null) {
+ childRemovedRes.origUsers = childPs.queryInstalledUsers(
+ allUsers,
+ true);
+ }
+ }
+ if (res.removedInfo.removedChildPackages == null) {
+ res.removedInfo.removedChildPackages = new ArrayMap<>();
+ }
+ res.removedInfo.removedChildPackages.put(childPkg.packageName,
+ childRemovedRes);
}
- res.removedInfo.removedChildPackages.put(childPkg.packageName,
- childRemovedRes);
}
}
+
sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
// Set the system/privileged/oem/vendor/product flags as needed
@@ -16461,6 +16557,9 @@
}
} else { // new package install
+ ps = null;
+ childPackages = null;
+ disabledPs = null;
replace = false;
existingPackage = null;
// Remember this for later, in case we need to rollback this install
@@ -16491,9 +16590,11 @@
}
// we're passing the freezer back to be closed in a later phase of install
shouldCloseFreezerBeforeReturn = false;
+
return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
- replace /* clearCodeCache */, sysPkg, renamedPackage, freezer);
+ replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+ ps, disabledPs, childPackages);
} finally {
if (shouldCloseFreezerBeforeReturn) {
freezer.close();
@@ -17443,34 +17544,20 @@
/*
* Tries to delete system package.
*/
- private boolean deleteSystemPackageLIF(PackageParser.Package deletedPkg,
- PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
- boolean writeSettings) {
- if (deletedPs.parentPackageName != null) {
- Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
- return false;
- }
-
+ private void deleteSystemPackageLIF(DeletePackageAction action,
+ PackageParser.Package deletedPkg, PackageSetting deletedPs, int[] allUserHandles,
+ int flags, PackageRemovedInfo outInfo, boolean writeSettings)
+ throws SystemDeleteException {
final boolean applyUserRestrictions
= (allUserHandles != null) && (outInfo.origUsers != null);
- final PackageSetting disabledPs;
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition
// reader
- synchronized (mPackages) {
- disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name);
- }
-
+ final PackageSetting disabledPs = action.disabledPs;
if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
+ " disabledPs=" + disabledPs);
-
- if (disabledPs == null) {
- Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName);
- return false;
- } else if (DEBUG_REMOVE) {
- Slog.d(TAG, "Deleting system pkg from data partition");
- }
+ Slog.d(TAG, "Deleting system pkg from data partition");
if (DEBUG_REMOVE) {
if (applyUserRestrictions) {
@@ -17508,11 +17595,8 @@
flags |= PackageManager.DELETE_KEEP_DATA;
}
- boolean ret = deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
+ deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
outInfo, writeSettings, disabledPs.pkg);
- if (!ret) {
- return false;
- }
// writer
synchronized (mPackages) {
@@ -17529,25 +17613,25 @@
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
try {
- installPackageFromSystemLIF(disabledPs.codePathString, false, allUserHandles,
+ installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
+ e.getMessage());
- return false;
+ // TODO(patb): can we avoid this; throw would come from scan...
+ throw new SystemDeleteException(e);
} finally {
if (disabledPs.pkg.isStub) {
mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);
}
}
- return true;
}
/**
* Installs a package that's already on the system partition.
*/
private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString,
- boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
+ @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
@Nullable PermissionsState origPermissionState, boolean writeSettings)
throws PackageManagerException {
@ParseFlags int parseFlags =
@@ -17555,7 +17639,7 @@
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- if (isPrivileged || locationIsPrivileged(codePathString)) {
+ if (locationIsPrivileged(codePathString)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
if (locationIsOem(codePathString)) {
@@ -17631,7 +17715,7 @@
return pkg;
}
- private boolean deleteInstalledPackageLIF(PackageSetting ps,
+ private void deleteInstalledPackageLIF(PackageSetting ps,
boolean deleteCodeAndResources, int flags, int[] allUserHandles,
PackageRemovedInfo outInfo, boolean writeSettings,
PackageParser.Package replacingPackage) {
@@ -17646,9 +17730,6 @@
for (int i = 0; i < childCount; i++) {
String childPackageName = ps.childPackageNames.get(i);
PackageSetting childPs = mSettings.mPackages.get(childPackageName);
- if (childPs == null) {
- return false;
- }
PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
childPackageName);
if (childInfo != null) {
@@ -17689,8 +17770,6 @@
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
}
-
- return true;
}
@Override
@@ -17746,26 +17825,59 @@
private static class DeletePackageAction {
public final PackageSetting deletingPs;
+ public final PackageSetting disabledPs;
+ public final PackageRemovedInfo outInfo;
- private DeletePackageAction(PackageSetting deletingPs) {
+ private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
+ PackageRemovedInfo outInfo) {
this.deletingPs = deletingPs;
+ this.disabledPs = disabledPs;
+ this.outInfo = outInfo;
}
}
/**
- * @return a {@link DeletePackageAction} if the provided package may be deleted, {@code null}
- * otherwise.
+ * @return a {@link DeletePackageAction} if the provided package and related state may be
+ * deleted, {@code null} otherwise.
*/
@Nullable
- private DeletePackageAction mayDeletePackageLIF(@NonNull String packageName) {
- synchronized (mPackages) {
- final PackageSetting ps;
- ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
+ @GuardedBy("mPackages")
+ private static DeletePackageAction mayDeletePackageLocked(
+ PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
+ @Nullable PackageSetting[] children) {
+ if (ps == null) {
+ return null;
+ }
+ if (isSystemApp(ps)) {
+ if (ps.parentPackageName != null) {
+ Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
return null;
}
- return new DeletePackageAction(ps);
+
+ // Confirm if the system package has been updated
+ // An updated system app can be deleted. This will also have to restore
+ // the system pkg from system partition
+ // reader
+ if (disabledPs == null) {
+ Slog.w(TAG,
+ "Attempt to delete unknown system package " + ps.pkg.packageName);
+ return null;
+ }
}
+ final int parentReferenceCount =
+ (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
+ final int childCount = children != null ? children.length : 0;
+ if (childCount != parentReferenceCount) {
+ return null;
+ }
+ if (childCount != 0 && outInfo != null && outInfo.removedChildPackages != null) {
+ for (PackageSetting child : children) {
+ if (child == null || !ps.childPackageNames.contains(child.name)) {
+ return null;
+ }
+ }
+ }
+ return new DeletePackageAction(ps, disabledPs, outInfo);
}
/*
@@ -17775,22 +17887,43 @@
boolean deleteCodeAndResources, int[] allUserHandles, int flags,
PackageRemovedInfo outInfo, boolean writeSettings,
PackageParser.Package replacingPackage) {
- final DeletePackageAction action = mayDeletePackageLIF(packageName);
+ final DeletePackageAction action;
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
+ PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
+ action = mayDeletePackageLocked(outInfo, ps, disabledPs, children);
+ }
if (null == action) {
return false;
}
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
- return executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
- allUserHandles, flags, outInfo, writeSettings, replacingPackage);
+ try {
+ executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
+ allUserHandles, flags, writeSettings, replacingPackage);
+ } catch (SystemDeleteException e) {
+ return false;
+ }
+ return true;
}
- private boolean executeDeletePackageLIF(DeletePackageAction action,
+ private static class SystemDeleteException extends Exception {
+ public final PackageManagerException reason;
+
+ private SystemDeleteException(PackageManagerException reason) {
+ this.reason = reason;
+ }
+ }
+
+ /** Deletes a package. Only throws when install of a disabled package fails. */
+ private void executeDeletePackageLIF(DeletePackageAction action,
String packageName, UserHandle user, boolean deleteCodeAndResources,
- int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
- boolean writeSettings, PackageParser.Package replacingPackage) {
+ int[] allUserHandles, int flags, boolean writeSettings,
+ PackageParser.Package replacingPackage) throws SystemDeleteException {
final PackageSetting ps = action.deletingPs;
+ final PackageRemovedInfo outInfo = action.outInfo;
final boolean systemApp = isSystemApp(ps);
synchronized (mPackages) {
@@ -17806,7 +17939,7 @@
clearPackageStateForUserLIF(ps, removedUserId, outInfo);
markPackageUninstalledForUserLPw(ps, user);
scheduleWritePackageRestrictionsLocked(user);
- return true;
+ return;
}
}
@@ -17835,7 +17968,7 @@
if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
scheduleWritePackageRestrictionsLocked(user);
- return true;
+ return;
} else {
// We need to set it back to 'installed' so the uninstall
// broadcasts will be sent correctly.
@@ -17851,7 +17984,7 @@
if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
scheduleWritePackageRestrictionsLocked(user);
- return true;
+ return;
}
}
@@ -17876,15 +18009,15 @@
}
// TODO(b/109941548): break reasons for ret = false out into mayDelete method
- final boolean ret;
if (systemApp) {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
// When an updated system application is deleted we delete the existing resources
// as well and fall back to existing code in system partition
- ret = deleteSystemPackageLIF(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
+ deleteSystemPackageLIF(
+ action, ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
- ret = deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
+ deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
outInfo, writeSettings, replacingPackage);
}
@@ -17933,8 +18066,6 @@
}
}
}
-
- return ret;
}
@GuardedBy("mPackages")
@@ -18395,7 +18526,7 @@
@GuardedBy("mPackages")
private int getUidTargetSdkVersionLockedLPr(int uid) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getSettingLPr(uid);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -19660,7 +19791,7 @@
enableSystemPackageLPw(deletedPkg);
}
installPackageFromSystemLIF(deletedPkg.codePath,
- false /*isPrivileged*/, null /*allUserHandles*/,
+ /*isPrivileged*/ null /*allUserHandles*/,
null /*origUserHandles*/, null /*origPermissionsState*/,
true /*writeSettings*/);
} catch (PackageManagerException pme) {
@@ -20014,7 +20145,7 @@
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (StorageManager.hasIsolatedStorage()) {
return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED
? Zygote.MOUNT_EXTERNAL_FULL
: Zygote.MOUNT_EXTERNAL_WRITE;
@@ -22492,7 +22623,7 @@
private SigningDetails getSigningDetails(int uid) {
synchronized (mPackages) {
final int appId = UserHandle.getAppId(uid);
- final Object obj = mSettings.getUserIdLPr(appId);
+ final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
return ((SharedUserSetting) obj).signatures.mSigningDetails;
@@ -23085,6 +23216,45 @@
throws IOException {
PackageManagerService.this.freeStorage(volumeUuid, bytes, storageFlags);
}
+
+ @Override
+ public void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+ PackageManagerService.this.forEachPackage(actionLocked);
+ }
+
+ @Override
+ public ArraySet<String> getEnabledComponents(String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return new ArraySet<>();
+ }
+ return setting.getEnabledComponents(userId);
+ }
+ }
+
+ @Override
+ public ArraySet<String> getDisabledComponents(String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return new ArraySet<>();
+ }
+ return setting.getDisabledComponents(userId);
+ }
+ }
+
+ @Override
+ public @PackageManager.EnabledState int getApplicationEnabledState(
+ String packageName, int userId) {
+ synchronized (mPackages) {
+ PackageSetting setting = mSettings.getPackageLPr(packageName);
+ if (setting == null) {
+ return COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+ return setting.getEnabled(userId);
+ }
+ }
}
@GuardedBy("mPackages")
@@ -23207,6 +23377,15 @@
}
}
+ void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+ synchronized (mPackages) {
+ int numPackages = mPackages.size();
+ for (int i = 0; i < numPackages; i++) {
+ actionLocked.accept(mPackages.valueAt(i));
+ }
+ }
+ }
+
private static void enforceSystemOrPhoneCaller(String tag) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
@@ -23494,6 +23673,30 @@
return mProtectedPackages.isPackageStateProtected(userId, packageName);
}
+ @Override
+ public void sendDeviceCustomizationReadyBroadcast() {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,
+ "sendDeviceCustomizationReadyBroadcast");
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
+ intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ final IActivityManager am = ActivityManager.getService();
+ final String[] requiredPermissions = {
+ Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
+ };
+ try {
+ am.broadcastIntent(null, intent, null, null, 0, null, null, requiredPermissions,
+ android.app.AppOpsManager.OP_NONE, null, false, false, UserHandle.USER_ALL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
static class ActiveInstallSession {
private final String mPackageName;
private final File mStagedDir;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c334b6e..c524dba 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -381,11 +381,9 @@
final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
new SparseArray<CrossProfileIntentResolver>();
- final ArrayMap<String, SharedUserSetting> mSharedUsers =
- new ArrayMap<String, SharedUserSetting>();
- private final ArrayList<Object> mUserIds = new ArrayList<Object>();
- private final SparseArray<Object> mOtherUserIds =
- new SparseArray<Object>();
+ final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();
+ private final ArrayList<SettingBase> mAppIds = new ArrayList<>();
+ private final SparseArray<SettingBase> mOtherAppIds = new SparseArray<>();
// For reading/writing settings file.
private final ArrayList<Signature> mPastSignatures =
@@ -519,7 +517,7 @@
SharedUserSetting s = mSharedUsers.get(name);
if (s == null && create) {
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
- s.userId = newUserIdLPw(s);
+ s.userId = acquireAndRegisterNewAppIdLPw(s);
if (s.userId < 0) {
// < 0 means we couldn't assign a userid; throw exception
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
@@ -612,7 +610,7 @@
cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
childPackageNames, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);
p.appId = uid;
- if (addUserIdLPw(uid, p, name)) {
+ if (registerExistingAppIdLPw(uid, p, name)) {
mPackages.put(name, p);
return p;
}
@@ -635,7 +633,7 @@
}
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
- if (addUserIdLPw(uid, s, name)) {
+ if (registerExistingAppIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
@@ -885,13 +883,13 @@
* Registers a user ID with the system. Potentially allocates a new user ID.
* @throws PackageManagerException If a user ID could not be allocated.
*/
- void addUserToSettingLPw(PackageSetting p) throws PackageManagerException {
+ void registerAppIdLPw(PackageSetting p) throws PackageManagerException {
if (p.appId == 0) {
// Assign new user ID
- p.appId = newUserIdLPw(p);
+ p.appId = acquireAndRegisterNewAppIdLPw(p);
} else {
// Add new setting to list of user IDs
- addUserIdLPw(p.appId, p, p.name);
+ registerExistingAppIdLPw(p.appId, p, p.name);
}
if (p.appId < 0) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -972,14 +970,14 @@
// If the we know about this user id, we have to update it as it
// has to point to the same PackageSetting instance as the package.
- Object userIdPs = getUserIdLPr(p.appId);
+ Object userIdPs = getSettingLPr(p.appId);
if (sharedUser == null) {
if (userIdPs != null && userIdPs != p) {
- replaceUserIdLPw(p.appId, p);
+ replaceAppIdLPw(p.appId, p);
}
} else {
if (userIdPs != null && userIdPs != sharedUser) {
- replaceUserIdLPw(p.appId, sharedUser);
+ replaceAppIdLPw(p.appId, sharedUser);
}
}
@@ -1083,11 +1081,11 @@
p.sharedUser.removePackage(p);
if (p.sharedUser.packages.size() == 0) {
mSharedUsers.remove(p.sharedUser.name);
- removeUserIdLPw(p.sharedUser.userId);
+ removeAppIdLPw(p.sharedUser.userId);
return p.sharedUser.userId;
}
} else {
- removeUserIdLPw(p.appId);
+ removeAppIdLPw(p.appId);
return p.appId;
}
}
@@ -1115,65 +1113,69 @@
mInstallerPackages.remove(packageName);
}
- private boolean addUserIdLPw(int uid, Object obj, Object name) {
- if (uid > Process.LAST_APPLICATION_UID) {
+ /** Returns true if the requested AppID was valid and not already registered. */
+ private boolean registerExistingAppIdLPw(int appId, SettingBase obj, Object name) {
+ if (appId > Process.LAST_APPLICATION_UID) {
return false;
}
- if (uid >= Process.FIRST_APPLICATION_UID) {
- int N = mUserIds.size();
- final int index = uid - Process.FIRST_APPLICATION_UID;
- while (index >= N) {
- mUserIds.add(null);
- N++;
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ int size = mAppIds.size();
+ final int index = appId - Process.FIRST_APPLICATION_UID;
+ // fill the array until our index becomes valid
+ while (index >= size) {
+ mAppIds.add(null);
+ size++;
}
- if (mUserIds.get(index) != null) {
+ if (mAppIds.get(index) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate user id: " + uid
+ "Adding duplicate app id: " + appId
+ " name=" + name);
return false;
}
- mUserIds.set(index, obj);
+ mAppIds.set(index, obj);
} else {
- if (mOtherUserIds.get(uid) != null) {
+ if (mOtherAppIds.get(appId) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate shared id: " + uid
+ "Adding duplicate shared id: " + appId
+ " name=" + name);
return false;
}
- mOtherUserIds.put(uid, obj);
+ mOtherAppIds.put(appId, obj);
}
return true;
}
- public Object getUserIdLPr(int uid) {
- if (uid >= Process.FIRST_APPLICATION_UID) {
- final int N = mUserIds.size();
- final int index = uid - Process.FIRST_APPLICATION_UID;
- return index < N ? mUserIds.get(index) : null;
+ /** Gets the setting associated with the provided App ID */
+ public SettingBase getSettingLPr(int appId) {
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ final int size = mAppIds.size();
+ final int index = appId - Process.FIRST_APPLICATION_UID;
+ return index < size ? mAppIds.get(index) : null;
} else {
- return mOtherUserIds.get(uid);
+ return mOtherAppIds.get(appId);
}
}
- private void removeUserIdLPw(int uid) {
- if (uid >= Process.FIRST_APPLICATION_UID) {
- final int N = mUserIds.size();
- final int index = uid - Process.FIRST_APPLICATION_UID;
- if (index < N) mUserIds.set(index, null);
+ /** Unregisters the provided app ID. */
+ void removeAppIdLPw(int appId) {
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ final int size = mAppIds.size();
+ final int index = appId - Process.FIRST_APPLICATION_UID;
+ if (index < size) mAppIds.set(index, null);
} else {
- mOtherUserIds.remove(uid);
+ mOtherAppIds.remove(appId);
}
- setFirstAvailableUid(uid+1);
+ setFirstAvailableUid(appId + 1);
}
- private void replaceUserIdLPw(int uid, Object obj) {
- if (uid >= Process.FIRST_APPLICATION_UID) {
- final int N = mUserIds.size();
- final int index = uid - Process.FIRST_APPLICATION_UID;
- if (index < N) mUserIds.set(index, obj);
+ private void replaceAppIdLPw(int appId, SettingBase obj) {
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ final int size = mAppIds.size();
+ final int index = appId - Process.FIRST_APPLICATION_UID;
+ if (index < size) mAppIds.set(index, obj);
} else {
- mOtherUserIds.put(uid, obj);
+ mOtherAppIds.put(appId, obj);
}
}
@@ -3157,7 +3159,7 @@
for (int i = 0; i < N; i++) {
final PackageSetting p = mPendingPackages.get(i);
final int sharedUserId = p.getSharedUserId();
- final Object idObj = getUserIdLPr(sharedUserId);
+ final Object idObj = getSettingLPr(sharedUserId);
if (idObj instanceof SharedUserSetting) {
final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
p.sharedUser = sharedUser;
@@ -3202,7 +3204,7 @@
final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
while (disabledIt.hasNext()) {
final PackageSetting disabledPs = disabledIt.next();
- final Object id = getUserIdLPr(disabledPs.appId);
+ final Object id = getSettingLPr(disabledPs.appId);
if (id != null && id instanceof SharedUserSetting) {
disabledPs.sharedUser = (SharedUserSetting) id;
}
@@ -4201,24 +4203,24 @@
}
}
- // Returns -1 if we could not find an available UserId to assign
- private int newUserIdLPw(Object obj) {
+ /** Returns a new AppID or -1 if we could not find an available AppID to assign */
+ private int acquireAndRegisterNewAppIdLPw(SettingBase obj) {
// Let's be stupidly inefficient for now...
- final int N = mUserIds.size();
- for (int i = mFirstAvailableUid; i < N; i++) {
- if (mUserIds.get(i) == null) {
- mUserIds.set(i, obj);
+ final int size = mAppIds.size();
+ for (int i = mFirstAvailableUid; i < size; i++) {
+ if (mAppIds.get(i) == null) {
+ mAppIds.set(i, obj);
return Process.FIRST_APPLICATION_UID + i;
}
}
// None left?
- if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
+ if (size > (Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID)) {
return -1;
}
- mUserIds.add(obj);
- return Process.FIRST_APPLICATION_UID + N;
+ mAppIds.add(obj);
+ return Process.FIRST_APPLICATION_UID + size;
}
public VerifierDeviceIdentity getVerifierDeviceIdentityLPw() {
@@ -4253,11 +4255,48 @@
return false;
}
+ /**
+ * Returns the disabled {@link PackageSetting} for the provided package name if one exists,
+ * {@code null} otherwise.
+ */
+ @Nullable
public PackageSetting getDisabledSystemPkgLPr(String name) {
PackageSetting ps = mDisabledSysPackages.get(name);
return ps;
}
+ /**
+ * Returns the disabled {@link PackageSetting} for the provided enabled {@link PackageSetting}
+ * if one exists, {@code null} otherwise.
+ */
+ @Nullable
+ public PackageSetting getDisabledSystemPkgLPr(PackageSetting enabledPackageSetting) {
+ if (enabledPackageSetting == null) {
+ return null;
+ }
+ return getDisabledSystemPkgLPr(enabledPackageSetting.name);
+ }
+
+ /**
+ * Fetches an array of the child {@link PackageSetting}s for all child package names referenced
+ * by the provided parent {@link PackageSetting} or {@code null} if no children are referenced.
+ *
+ * Note: Any child packages not found will be null in the returned array.
+ */
+ @Nullable
+ public PackageSetting[] getChildSettingsLPr(PackageSetting parentPackageSetting) {
+ if (parentPackageSetting == null || !parentPackageSetting.hasChildPackages()) {
+ return null;
+ }
+ final int childCount = parentPackageSetting.childPackageNames.size();
+ PackageSetting[] children =
+ new PackageSetting[childCount];
+ for (int i = 0; i < childCount; i++) {
+ children[i] = mPackages.get(parentPackageSetting.childPackageNames.get(i));
+ }
+ return children;
+ }
+
boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
final PackageSetting ps = mPackages.get(componentInfo.packageName);
if (ps == null) return false;
diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java
new file mode 100644
index 0000000..9e8b73e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 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.
+ */
+package com.android.server.pm;
+
+import android.text.TextUtils;
+
+/**
+ * Represents a Share Target definition, read from the application's manifest (shortcuts.xml)
+ */
+class ShareTargetInfo {
+ static class TargetData {
+ final String mScheme;
+ final String mHost;
+ final String mPort;
+ final String mPath;
+ final String mPathPattern;
+ final String mPathPrefix;
+ final String mMimeType;
+
+ TargetData(String scheme, String host, String port, String path, String pathPattern,
+ String pathPrefix, String mimeType) {
+ mScheme = scheme;
+ mHost = host;
+ mPort = port;
+ mPath = path;
+ mPathPattern = pathPattern;
+ mPathPrefix = pathPrefix;
+ mMimeType = mimeType;
+ }
+
+ public void toStringInner(StringBuilder strBuilder) {
+ if (!TextUtils.isEmpty(mScheme)) {
+ strBuilder.append(" scheme=").append(mScheme);
+ }
+ if (!TextUtils.isEmpty(mHost)) {
+ strBuilder.append(" host=").append(mHost);
+ }
+ if (!TextUtils.isEmpty(mPort)) {
+ strBuilder.append(" port=").append(mPort);
+ }
+ if (!TextUtils.isEmpty(mPath)) {
+ strBuilder.append(" path=").append(mPath);
+ }
+ if (!TextUtils.isEmpty(mPathPattern)) {
+ strBuilder.append(" pathPattern=").append(mPathPattern);
+ }
+ if (!TextUtils.isEmpty(mPathPrefix)) {
+ strBuilder.append(" pathPrefix=").append(mPathPrefix);
+ }
+ if (!TextUtils.isEmpty(mMimeType)) {
+ strBuilder.append(" mimeType=").append(mMimeType);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strBuilder = new StringBuilder();
+ toStringInner(strBuilder);
+ return strBuilder.toString();
+ }
+ }
+
+ final TargetData[] mTargetData;
+ final String mTargetClass;
+ final String[] mCategories;
+
+ ShareTargetInfo(TargetData[] data, String targetClass, String[] categories) {
+ mTargetData = data;
+ mTargetClass = targetClass;
+ mCategories = categories;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strBuilder = new StringBuilder();
+ strBuilder.append("targetClass=").append(mTargetClass);
+ for (int i = 0; i < mTargetData.length; i++) {
+ strBuilder.append(" data={");
+ mTargetData[i].toStringInner(strBuilder);
+ strBuilder.append("}");
+ }
+ for (int i = 0; i < mCategories.length; i++) {
+ strBuilder.append(" category=").append(mCategories[i]);
+ }
+
+ return strBuilder.toString();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 92e261a..83f0fde 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -111,6 +111,11 @@
final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
+ * All the share targets from the package
+ */
+ private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
+
+ /**
* # of times the package has called rate-limited APIs.
*/
private int mApiCallCount;
@@ -739,15 +744,16 @@
List<ShortcutInfo> newManifestShortcutList = null;
try {
newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
- getPackageName(), getPackageUserId());
+ getPackageName(), getPackageUserId(), mShareTargets);
} catch (IOException|XmlPullParserException e) {
Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
}
final int manifestShortcutSize = newManifestShortcutList == null ? 0
: newManifestShortcutList.size();
if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Package %s has %d manifest shortcut(s)",
- getPackageName(), manifestShortcutSize));
+ Slog.d(TAG,
+ String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
+ getPackageName(), manifestShortcutSize, mShareTargets.size()));
}
if (isNewApp && (manifestShortcutSize == 0)) {
// If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
@@ -1657,6 +1663,11 @@
return new ArrayList<>(mShortcuts.values());
}
+ @VisibleForTesting
+ List<ShareTargetInfo> getAllShareTargetsForTest() {
+ return new ArrayList<>(mShareTargets);
+ }
+
@Override
public void verifyStates() {
super.verifyStates();
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 866c46c..90f08c3 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
@@ -55,10 +56,14 @@
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_INTENT = "intent";
private static final String TAG_CATEGORIES = "categories";
+ private static final String TAG_SHARE_TARGET = "share-target";
+ private static final String TAG_DATA = "data";
+ private static final String TAG_CATEGORY = "category";
@Nullable
- public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
- String packageName, @UserIdInt int userId) throws IOException, XmlPullParserException {
+ public static List<ShortcutInfo> parseShortcuts(ShortcutService service, String packageName,
+ @UserIdInt int userId, @NonNull List<ShareTargetInfo> outShareTargets)
+ throws IOException, XmlPullParserException {
if (ShortcutService.DEBUG) {
Slog.d(TAG, String.format("Scanning package %s for manifest shortcuts on user %d",
packageName, userId));
@@ -69,6 +74,7 @@
}
List<ShortcutInfo> result = null;
+ outShareTargets.clear();
try {
final int size = activities.size();
@@ -82,8 +88,8 @@
service.getActivityInfoWithMetadata(
activityInfoNoMetadata.getComponentName(), userId);
if (activityInfoWithMetadata != null) {
- result = parseShortcutsOneFile(
- service, activityInfoWithMetadata, packageName, userId, result);
+ result = parseShortcutsOneFile(service, activityInfoWithMetadata, packageName,
+ userId, result, outShareTargets);
}
}
} catch (RuntimeException e) {
@@ -99,7 +105,8 @@
private static List<ShortcutInfo> parseShortcutsOneFile(
ShortcutService service,
ActivityInfo activityInfo, String packageName, @UserIdInt int userId,
- List<ShortcutInfo> result) throws IOException, XmlPullParserException {
+ List<ShortcutInfo> result, @NonNull List<ShareTargetInfo> outShareTargets)
+ throws IOException, XmlPullParserException {
if (ShortcutService.DEBUG) {
Slog.d(TAG, String.format(
"Checking main activity %s", activityInfo.getComponentName()));
@@ -126,9 +133,19 @@
// after parsing <intent>. We keep the current one in here.
ShortcutInfo currentShortcut = null;
+ // We instantiate ShareTargetInfo at <share-target>, but add it to outShareTargets at
+ // </share-target>, after parsing <data> and <category>. We keep the current one here.
+ ShareTargetInfo currentShareTarget = null;
+
+ // Keeps parsed categories for both ShortcutInfo and ShareTargetInfo
Set<String> categories = null;
+
+ // Keeps parsed intents for ShortcutInfo
final ArrayList<Intent> intents = new ArrayList<>();
+ // Keeps parsed data fields for ShareTargetInfo
+ final ArrayList<ShareTargetInfo.TargetData> dataList = new ArrayList<>();
+
outer:
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
@@ -194,6 +211,32 @@
continue;
}
+ // When a share-target tag is closing, publish.
+ if ((type == XmlPullParser.END_TAG) && (depth == 2)
+ && (TAG_SHARE_TARGET.equals(tag))) {
+ if (currentShareTarget == null) {
+ // ShareTarget was invalid.
+ continue;
+ }
+ final ShareTargetInfo sti = currentShareTarget;
+ currentShareTarget = null; // Make sure to null out for the next iteration.
+
+ if (categories == null || categories.isEmpty() || dataList.isEmpty()) {
+ // Incomplete ShareTargetInfo.
+ continue;
+ }
+
+ final ShareTargetInfo newShareTarget = new ShareTargetInfo(
+ dataList.toArray(new ShareTargetInfo.TargetData[dataList.size()]),
+ sti.mTargetClass, categories.toArray(new String[categories.size()]));
+ outShareTargets.add(newShareTarget);
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "ShareTarget added: " + newShareTarget.toString());
+ }
+ categories = null;
+ dataList.clear();
+ }
+
// Otherwise, just look at start tags.
if (type != XmlPullParser.START_TAG) {
continue;
@@ -224,6 +267,17 @@
categories = null;
continue;
}
+ if (depth == 2 && TAG_SHARE_TARGET.equals(tag)) {
+ final ShareTargetInfo sti = parseShareTargetAttributes(service, attrs);
+ if (sti == null) {
+ // ShareTarget was invalid.
+ continue;
+ }
+ currentShareTarget = sti;
+ categories = null;
+ dataList.clear();
+ continue;
+ }
if (depth == 3 && TAG_INTENT.equals(tag)) {
if ((currentShortcut == null)
|| !currentShortcut.isEnabled()) {
@@ -258,6 +312,34 @@
categories.add(name);
continue;
}
+ if (depth == 3 && TAG_CATEGORY.equals(tag)) {
+ if ((currentShareTarget == null)) {
+ continue;
+ }
+ final String name = parseCategory(service, attrs);
+ if (TextUtils.isEmpty(name)) {
+ Log.e(TAG, "Empty category found. activity=" + activity);
+ continue;
+ }
+
+ if (categories == null) {
+ categories = new ArraySet<>();
+ }
+ categories.add(name);
+ continue;
+ }
+ if (depth == 3 && TAG_DATA.equals(tag)) {
+ if ((currentShareTarget == null)) {
+ continue;
+ }
+ final ShareTargetInfo.TargetData data = parseShareTargetData(service, attrs);
+ if (data == null) {
+ Log.e(TAG, "Invalid data tag found. activity=" + activity);
+ continue;
+ }
+ dataList.add(data);
+ continue;
+ }
Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
}
@@ -369,4 +451,57 @@
null, // bitmap path
disabledReason);
}
+
+ private static String parseCategory(ShortcutService service, AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.IntentCategory);
+ try {
+ if (sa.getType(R.styleable.IntentCategory_name) != TypedValue.TYPE_STRING) {
+ Log.w(TAG, "android:name must be string literal.");
+ return null;
+ }
+ return sa.getString(R.styleable.IntentCategory_name);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShareTargetInfo parseShareTargetAttributes(ShortcutService service,
+ AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.Intent);
+ try {
+ String targetClass = sa.getString(R.styleable.Intent_targetClass);
+ if (TextUtils.isEmpty(targetClass)) {
+ Log.w(TAG, "android:targetClass must be provided.");
+ return null;
+ }
+ return new ShareTargetInfo(null, targetClass, null);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShareTargetInfo.TargetData parseShareTargetData(ShortcutService service,
+ AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.AndroidManifestData);
+ try {
+ if (sa.getType(R.styleable.AndroidManifestData_mimeType) != TypedValue.TYPE_STRING) {
+ Log.w(TAG, "android:mimeType must be string literal.");
+ return null;
+ }
+ String scheme = sa.getString(R.styleable.AndroidManifestData_scheme);
+ String host = sa.getString(R.styleable.AndroidManifestData_host);
+ String port = sa.getString(R.styleable.AndroidManifestData_port);
+ String path = sa.getString(R.styleable.AndroidManifestData_path);
+ String pathPattern = sa.getString(R.styleable.AndroidManifestData_pathPattern);
+ String pathPrefix = sa.getString(R.styleable.AndroidManifestData_pathPrefix);
+ String mimeType = sa.getString(R.styleable.AndroidManifestData_mimeType);
+ return new ShareTargetInfo.TargetData(scheme, host, port, path, pathPattern, pathPrefix,
+ mimeType);
+ } finally {
+ sa.recycle();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index b9c3048..2b773f4 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -41,8 +41,8 @@
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -98,8 +98,8 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
import com.android.internal.util.StatLogger;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
@@ -1172,7 +1172,7 @@
return true;
}
}
-
+
// If the local copy says the user is locked, check with AM for the actual state, since
// the user might just have been unlocked.
// Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 505e4ee..1fd9b69 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -294,13 +294,14 @@
*/
public void detectLocaleChange() {
final String currentLocales = mService.injectGetLocaleTagsForUser(mUserId);
- if (getKnownLocales().equals(currentLocales)) {
+ if (!TextUtils.isEmpty(mKnownLocales) && mKnownLocales.equals(currentLocales)) {
return;
}
if (ShortcutService.DEBUG) {
- Slog.d(TAG, "Locale changed from " + currentLocales + " to " + mKnownLocales
+ Slog.d(TAG, "Locale changed from " + mKnownLocales + " to " + currentLocales
+ " for user " + mUserId);
}
+
mKnownLocales = currentLocales;
forAllPackages(pkg -> {
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index e194d15..2d583ca3 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -105,6 +105,8 @@
*/
private boolean perUser;
+ boolean usageInfoRequired;
+
public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
name = _name;
sourcePackageName = _sourcePackageName;
@@ -351,6 +353,7 @@
}
if (bp.perm == p) {
bp.protectionLevel = p.info.protectionLevel;
+ bp.usageInfoRequired = p.info.usageInfoRequired;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
@@ -430,6 +433,7 @@
permissionInfo.packageName = sourcePackageName;
permissionInfo.nonLocalizedLabel = name;
permissionInfo.protectionLevel = protectionLevel;
+ permissionInfo.usageInfoRequired = usageInfoRequired;
return permissionInfo;
}
@@ -458,6 +462,7 @@
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+ bp.usageInfoRequired = readInt(parser, null, "usageInfoRequired", 0) != 0;
if (dynamic) {
final PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
@@ -465,6 +470,7 @@
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
pi.protectionLevel = bp.protectionLevel;
+ pi.usageInfoRequired = bp.usageInfoRequired;
bp.pendingPermissionInfo = pi;
}
out.put(bp.name, bp);
@@ -497,6 +503,7 @@
if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
serializer.attribute(null, "protection", Integer.toString(protectionLevel));
}
+ serializer.attribute(null, "usageInfoRequired", usageInfoRequired ? "1" : "0");
if (type == BasePermission.TYPE_DYNAMIC) {
final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
if (pi != null) {
@@ -533,6 +540,7 @@
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+ if (pi1.usageInfoRequired != pi2.usageInfoRequired) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -580,6 +588,8 @@
pw.print(" enforced=");
pw.println(readEnforced);
}
+ pw.print(" usageInfoRequired=");
+ pw.println(usageInfoRequired);
return true;
}
}
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 21cc14e..51619cf 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -46,7 +46,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.permission.PermissionManager;
@@ -144,6 +143,11 @@
LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
+ private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>();
+ static {
+ ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
+ }
+
private static final Set<String> COARSE_LOCATION_PERMISSIONS = new ArraySet<>();
static {
COARSE_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
@@ -184,7 +188,7 @@
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (!StorageManager.hasIsolatedStorage()) {
STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
@@ -193,7 +197,7 @@
private static final Set<String> MEDIA_AURAL_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (StorageManager.hasIsolatedStorage()) {
MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
}
}
@@ -201,7 +205,7 @@
private static final Set<String> MEDIA_VISUAL_PERMISSIONS = new ArraySet<>();
static {
// STOPSHIP(b/112545973): remove once feature enabled by default
- if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ if (StorageManager.hasIsolatedStorage()) {
MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
}
@@ -624,7 +628,7 @@
PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
grantSystemFixedPermissionsToSystemPackage(packageName, userId,
- LOCATION_PERMISSIONS);
+ LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
}
}
@@ -1054,6 +1058,17 @@
return;
}
+ // Intersect the requestedPermissions for a factory image with that of its current update
+ // in case the latter one removed a <uses-permission>
+ String[] requestedByNonSystemPackage = getPackageInfo(pkg.packageName).requestedPermissions;
+ int size = requestedPermissions.length;
+ for (int i = 0; i < size; i++) {
+ if (!ArrayUtils.contains(requestedByNonSystemPackage, requestedPermissions[i])) {
+ requestedPermissions[i] = null;
+ }
+ }
+ requestedPermissions = ArrayUtils.filterNotNull(requestedPermissions, String[]::new);
+
PackageManager pm = mContext.getPackageManager();
final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
ApplicationInfo applicationInfo = pkg.applicationInfo;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index ec15c16..189d0f4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -181,4 +181,9 @@
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
-}
\ No newline at end of file
+
+ /**
+ * Returns {@code true} if {@code permName} has {@code usageInfoRequired} set.
+ */
+ public abstract boolean isPermissionUsageInfoRequired(@NonNull String permName);
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index c5d38db..4406fdd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2644,5 +2644,12 @@
return mSettings.getPermissionLocked(permName);
}
}
+ @Override
+ public boolean isPermissionUsageInfoRequired(String permName) {
+ synchronized (PermissionManagerService.this.mLock) {
+ BasePermission bp = mSettings.getPermissionLocked(permName);
+ return bp != null && bp.usageInfoRequired;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 79e2688..07bebad 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -29,8 +29,12 @@
import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
import android.os.Temperature;
import android.util.ArrayMap;
import android.util.Slog;
@@ -77,6 +81,10 @@
@GuardedBy("mLock")
private int mStatus;
+ /** If override status takes effect*/
+ @GuardedBy("mLock")
+ private boolean mIsStatusOverride;
+
/** Current thermal map, key as name */
@GuardedBy("mLock")
private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
@@ -184,13 +192,19 @@
newStatus = t.getStatus();
}
}
+ // Do not update if override from shell
+ if (!mIsStatusOverride) {
+ setStatusLocked(newStatus);
+ }
+ }
+
+ private void setStatusLocked(int newStatus) {
if (newStatus != mStatus) {
mStatus = newStatus;
notifyStatusListenersLocked();
}
}
-
private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
@Nullable Integer type) {
synchronized (mLock) {
@@ -241,12 +255,7 @@
// Thermal Shutdown for Skin temperature
if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
&& temperature.getType() == Temperature.TYPE_SKIN) {
- final long token = Binder.clearCallingIdentity();
- try {
- mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
}
Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
@@ -263,8 +272,14 @@
}
}
+ /* HwBinder callback **/
private void onTemperatureChangedCallback(Temperature temperature) {
- onTemperatureChanged(temperature, true);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ onTemperatureChanged(temperature, true);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
@@ -393,7 +408,7 @@
}
@Override
- public int getCurrentStatus() {
+ public int getCurrentThermalStatus() {
synchronized (mLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -434,8 +449,93 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ private boolean isCallerShell() {
+ final int callingUid = Binder.getCallingUid();
+ return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ if (!isCallerShell()) {
+ Slog.w(TAG, "Only shell is allowed to call thermalservice shell commands");
+ return;
+ }
+ (new ThermalShellCommand()).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+
};
+ class ThermalShellCommand extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ switch(cmd != null ? cmd : "") {
+ case "override-status":
+ return runOverrideStatus();
+ case "reset":
+ return runReset();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runReset() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mIsStatusOverride = false;
+ onTemperatureMapChangedLocked();
+ return 0;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private int runOverrideStatus() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final PrintWriter pw = getOutPrintWriter();
+ int status;
+ try {
+ status = Integer.parseInt(getNextArgRequired());
+ } catch (RuntimeException ex) {
+ pw.println("Error: " + ex.toString());
+ return -1;
+ }
+ if (!Temperature.isValidStatus(status)) {
+ pw.println("Invalid status: " + Integer.toString(status));
+ return -1;
+ }
+ synchronized (mLock) {
+ mIsStatusOverride = true;
+ setStatusLocked(status);
+ }
+ return 0;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Thermal service (thermalservice) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" override-status STATUS");
+ pw.println(" sets and locks the thermal status of the device to STATUS.");
+ pw.println(" status code is defined in android.os.Temperature.");
+ pw.println(" reset");
+ pw.println(" unlocks the thermal status of the device.");
+ pw.println();
+ }
+ }
+
abstract static class ThermalHalWrapper {
protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 4124210..b5ad235 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -30,21 +30,32 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.Signature;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.PackageUtils;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.BitUtils;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -96,7 +107,7 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
getContext().registerReceiverAsUser(new BroadcastReceiver() {
@Override
- public void onReceive(Context context, Intent intent) {
+ public void onReceive(@NonNull Context context, @NonNull Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_USER_REMOVED)) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
onRemoveUser(userId);
@@ -114,13 +125,19 @@
@Override
public void onStartUser(@UserIdInt int userId) {
+ RoleUserState userState;
synchronized (mLock) {
- //TODO only call into PermissionController if it or system upgreaded (for boot time)
- getUserStateLocked(userId);
+ userState = getUserStateLocked(userId);
}
- //TODO consider calling grants only when certain conditions are met
- // such as OS or PermissionController upgrade
- if (RemoteRoleControllerService.DEBUG) {
+ String packagesHash = computeComponentStateHash(userId);
+ String lastGrantPackagesHash;
+ synchronized (mLock) {
+ lastGrantPackagesHash = userState.getLastGrantPackagesHashLocked();
+ }
+ boolean needGrant = !Objects.equals(packagesHash, lastGrantPackagesHash);
+ if (needGrant) {
+ // Some vital packages state has changed since last role grant
+ // Run grants again
Slog.i(LOG_TAG, "Granting default permissions...");
CompletableFuture<Void> result = new CompletableFuture<>();
getControllerService(userId).onGrantDefaultRoles(
@@ -129,7 +146,6 @@
public void onSuccess() {
result.complete(null);
}
-
@Override
public void onFailure() {
result.completeExceptionally(new RuntimeException());
@@ -137,19 +153,54 @@
});
try {
result.get(5, TimeUnit.SECONDS);
+ synchronized (mLock) {
+ userState.setLastGrantPackagesHashLocked(packagesHash);
+ }
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
}
+ } else if (RemoteRoleControllerService.DEBUG) {
+ Slog.i(LOG_TAG, "Already ran grants for package state " + packagesHash);
}
}
+ @Nullable
+ private String computeComponentStateHash(@UserIdInt int userId) {
+ PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ pm.forEachPackage(FunctionalUtils.uncheckExceptions(pkg -> {
+ out.write(pkg.packageName.getBytes());
+ out.write(BitUtils.toBytes(pkg.getLongVersionCode()));
+ out.write(pm.getApplicationEnabledState(pkg.packageName, userId));
+
+ ArraySet<String> enabledComponents =
+ pm.getEnabledComponents(pkg.packageName, userId);
+ int numComponents = CollectionUtils.size(enabledComponents);
+ for (int i = 0; i < numComponents; i++) {
+ out.write(enabledComponents.valueAt(i).getBytes());
+ }
+
+ ArraySet<String> disabledComponents =
+ pm.getDisabledComponents(pkg.packageName, userId);
+ numComponents = CollectionUtils.size(disabledComponents);
+ for (int i = 0; i < numComponents; i++) {
+ out.write(disabledComponents.valueAt(i).getBytes());
+ }
+ for (Signature signature : pkg.mSigningDetails.signatures) {
+ out.write(signature.toByteArray());
+ }
+ }));
+
+ return PackageUtils.computeSha256Digest(out.toByteArray());
+ }
+
@GuardedBy("mLock")
@NonNull
private RoleUserState getUserStateLocked(@UserIdInt int userId) {
RoleUserState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new RoleUserState(userId);
- userState.readSyncLocked();
+ userState = RoleUserState.newInstanceLocked(userId);
mUserStates.put(userId, userState);
}
return userState;
@@ -334,5 +385,13 @@
return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false, true, name, null);
}
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+ new RoleManagerShellCommand(this).exec(this, in, out, err, args, callback,
+ resultReceiver);
+ }
}
}
diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
new file mode 100644
index 0000000..336b311
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+package com.android.server.role;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.role.IRoleManager;
+import android.app.role.IRoleManagerCallback;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+class RoleManagerShellCommand extends ShellCommand {
+
+ @NonNull
+ private final IRoleManager mRoleManager;
+
+ RoleManagerShellCommand(@NonNull IRoleManager roleManager) {
+ mRoleManager = roleManager;
+ }
+
+ private class Callback extends IRoleManagerCallback.Stub {
+
+ @NonNull
+ private final CompletableFuture<Void> mResult = new CompletableFuture<>();
+
+ public int waitForResult() {
+ try {
+ mResult.get(5, TimeUnit.SECONDS);
+ return 0;
+ } catch (Exception e) {
+ getErrPrintWriter().println("Error: " + e.toString());
+ return -1;
+ }
+ }
+
+ @Override
+ public void onSuccess() {
+ mResult.complete(null);
+ }
+
+ @Override
+ public void onFailure() {
+ mResult.completeExceptionally(new RuntimeException("Failed"));
+ }
+ }
+
+ @Override
+ public int onCommand(@Nullable String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "add-role-holder":
+ return runAddRoleHolder();
+ case "remove-role-holder":
+ return runRemoveRoleHolder();
+ case "clear-role-holders":
+ return runClearRoleHolders();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int getUserIdMaybe() {
+ int userId = UserHandle.USER_SYSTEM;
+ String option = getNextOption();
+ if (option != null && option.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ }
+ return userId;
+ }
+
+ private int runAddRoleHolder() throws RemoteException {
+ int userId = getUserIdMaybe();
+ String roleName = getNextArgRequired();
+ String packageName = getNextArgRequired();
+
+ Callback callback = new Callback();
+ mRoleManager.addRoleHolderAsUser(roleName, packageName, userId, callback);
+ return callback.waitForResult();
+ }
+
+ private int runRemoveRoleHolder() throws RemoteException {
+ int userId = getUserIdMaybe();
+ String roleName = getNextArgRequired();
+ String packageName = getNextArgRequired();
+
+ Callback callback = new Callback();
+ mRoleManager.removeRoleHolderAsUser(roleName, packageName, userId, callback);
+ return callback.waitForResult();
+ }
+
+ private int runClearRoleHolders() throws RemoteException {
+ int userId = getUserIdMaybe();
+ String roleName = getNextArgRequired();
+
+ Callback callback = new Callback();
+ mRoleManager.clearRoleHoldersAsUser(roleName, userId, callback);
+ return callback.waitForResult();
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Role manager (role) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE");
+ pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE");
+ pw.println(" clear-role-holders [--user USER_ID] ROLE");
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 9c43f4d..3e3e156 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -31,6 +31,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import libcore.io.IoUtils;
@@ -46,6 +47,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Objects;
/**
* Stores the state of roles for a user.
@@ -63,26 +65,47 @@
private static final String TAG_HOLDER = "holder";
private static final String ATTRIBUTE_VERSION = "version";
private static final String ATTRIBUTE_NAME = "name";
+ private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
@UserIdInt
private final int mUserId;
@GuardedBy("RoleManagerService.mLock")
- private int mVersion;
+ private int mVersion = VERSION_UNDEFINED;
+
+ @GuardedBy("RoleManagerService.mLock")
+ @Nullable
+ private String mLastGrantPackagesHash;
/**
* Maps role names to its holders' package names. The values should never be null.
*/
@GuardedBy("RoleManagerService.mLock")
- private ArrayMap<String, ArraySet<String>> mRoles;
+ @NonNull
+ private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
@GuardedBy("RoleManagerService.mLock")
private boolean mDestroyed;
+ @NonNull
private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
- public RoleUserState(@UserIdInt int userId) {
+ private RoleUserState(@UserIdInt int userId) {
mUserId = userId;
+
+ readSyncLocked();
+ }
+
+ /**
+ * Create a new instance of user state, and read its state from disk if previously persisted.
+ *
+ * @param userId the user id for the new user state
+ *
+ * @return the new user state
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public static RoleUserState newInstanceLocked(@UserIdInt int userId) {
+ return new RoleUserState(userId);
}
/**
@@ -110,6 +133,31 @@
}
/**
+ * Get the hash representing the state of packages during the last time initial grants was run.
+ *
+ * @return the hash representing the state of packages
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public String getLastGrantPackagesHashLocked() {
+ return mLastGrantPackagesHash;
+ }
+
+ /**
+ * Set the hash representing the state of packages during the last time initial grants was run.
+ *
+ * @param lastGrantPackagesHash the hash representing the state of packages
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public void setLastGrantPackagesHashLocked(@Nullable String lastGrantPackagesHash) {
+ throwIfDestroyedLocked();
+ if (Objects.equals(mLastGrantPackagesHash, lastGrantPackagesHash)) {
+ return;
+ }
+ mLastGrantPackagesHash = lastGrantPackagesHash;
+ writeAsyncLocked();
+ }
+
+ /**
* Get whether the role is available.
*
* @param roleName the name of the role to get the holders for
@@ -229,22 +277,24 @@
@GuardedBy("RoleManagerService.mLock")
private void writeAsyncLocked() {
throwIfDestroyedLocked();
- int version = mVersion;
+
ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
- for (int i = 0, size = mRoles.size(); i < size; ++i) {
+ for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
String roleName = mRoles.keyAt(i);
ArraySet<String> roleHolders = mRoles.valueAt(i);
roleHolders = new ArraySet<>(roleHolders);
roles.put(roleName, roleHolders);
}
+
mWriteHandler.removeCallbacksAndMessages(null);
// TODO: Throttle writes.
- mWriteHandler.sendMessage(PooledLambda.obtainMessage(
- RoleUserState::writeSync, this, version, roles));
+ mWriteHandler.sendMessage(PooledLambda.obtainMessage(RoleUserState::writeSync, this,
+ mVersion, mLastGrantPackagesHash, roles));
}
@WorkerThread
- private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles) {
+ private void writeSync(int version, @Nullable String packagesHash,
+ @NonNull ArrayMap<String, ArraySet<String>> roles) {
AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
FileOutputStream out = null;
try {
@@ -256,7 +306,7 @@
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startDocument(null, true);
- serializeRoles(serializer, version, roles);
+ serializeRoles(serializer, version, packagesHash, roles);
serializer.endDocument();
atomicFile.finishWrite(out);
@@ -272,17 +322,26 @@
@WorkerThread
private void serializeRoles(@NonNull XmlSerializer serializer, int version,
- @NonNull ArrayMap<String, ArraySet<String>> roles) throws IOException {
+ @Nullable String packagesHash, @NonNull ArrayMap<String, ArraySet<String>> roles)
+ throws IOException {
serializer.startTag(null, TAG_ROLES);
+
serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
+
+ if (packagesHash != null) {
+ serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
+ }
+
for (int i = 0, size = roles.size(); i < size; ++i) {
String roleName = roles.keyAt(i);
ArraySet<String> roleHolders = roles.valueAt(i);
+
serializer.startTag(null, TAG_ROLE);
serializer.attribute(null, ATTRIBUTE_NAME, roleName);
serializeRoleHolders(serializer, roleHolders);
serializer.endTag(null, TAG_ROLE);
}
+
serializer.endTag(null, TAG_ROLES);
}
@@ -291,6 +350,7 @@
@NonNull ArraySet<String> roleHolders) throws IOException {
for (int i = 0, size = roleHolders.size(); i < size; ++i) {
String roleHolder = roleHolders.valueAt(i);
+
serializer.startTag(null, TAG_HOLDER);
serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
serializer.endTag(null, TAG_HOLDER);
@@ -301,11 +361,7 @@
* Read the state from file.
*/
@GuardedBy("RoleManagerService.mLock")
- public void readSyncLocked() {
- if (mRoles != null) {
- throw new IllegalStateException("This RoleUserState has already read the roles.xml");
- }
-
+ private void readSyncLocked() {
File file = getFile(mUserId);
try (FileInputStream in = new AtomicFile(file).openRead()) {
XmlPullParser parser = Xml.newPullParser();
@@ -313,8 +369,6 @@
parseXmlLocked(parser);
} catch (FileNotFoundException e) {
Slog.i(LOG_TAG, "roles.xml not found");
- mRoles = new ArrayMap<>();
- mVersion = VERSION_UNDEFINED;
} catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
}
@@ -336,12 +390,14 @@
return;
}
}
+ Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml");
}
private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
XmlPullParserException {
mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
- mRoles = new ArrayMap<>();
+ mLastGrantPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
+ mRoles.clear();
int type;
int depth;
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
similarity index 69%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
index 27d25b8..7ce071f 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.server.signedconfig;
+
+import android.content.Context;
+
+class SignedConfigApplicator {
+
+ static void applyConfig(Context context, String config, String signature) {
+ //TODO verify signature
+ //TODO parse & apply config
+ }
+
}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
new file mode 100644
index 0000000..1485686
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package com.android.server.signedconfig;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+
+/**
+ * Signed config service. This is not an Android Service, but just owns a broadcast receiver for
+ * receiving package install and update notifications from the package manager.
+ */
+public class SignedConfigService {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "SignedConfig";
+
+ // TODO should these be elsewhere? In a public API?
+ private static final String KEY_CONFIG = "android.signedconfig";
+ private static final String KEY_CONFIG_SIGNATURE = "android.signedconfig.signature";
+
+ private static class UpdateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ new SignedConfigService(context).handlePackageBroadcast(intent);
+ }
+ }
+
+ private final Context mContext;
+ private final PackageManagerInternal mPacMan;
+
+ public SignedConfigService(Context context) {
+ mContext = context;
+ mPacMan = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ void handlePackageBroadcast(Intent intent) {
+ if (DBG) Slog.d(TAG, "handlePackageBroadcast " + intent);
+ Uri packageData = intent.getData();
+ String packageName = packageData == null ? null : packageData.getSchemeSpecificPart();
+ if (DBG) Slog.d(TAG, "handlePackageBroadcast package=" + packageName);
+ if (packageName == null) {
+ return;
+ }
+ int userId = mContext.getUser().getIdentifier();
+ PackageInfo pi = mPacMan.getPackageInfo(packageName, PackageManager.GET_META_DATA,
+ android.os.Process.SYSTEM_UID, userId);
+ if (pi == null) {
+ Slog.w(TAG, "Got null PackageInfo for " + packageName + "; user " + userId);
+ return;
+ }
+ Bundle metaData = pi.applicationInfo.metaData;
+ if (metaData == null) {
+ if (DBG) Slog.d(TAG, "handlePackageBroadcast: no metadata");
+ return;
+ }
+ if (metaData.containsKey(KEY_CONFIG)
+ && metaData.containsKey(KEY_CONFIG_SIGNATURE)) {
+ String config = metaData.getString(KEY_CONFIG);
+ String signature = metaData.getString(KEY_CONFIG_SIGNATURE);
+ if (DBG) {
+ Slog.d(TAG, "Got signed config: " + config);
+ Slog.d(TAG, "Got config signature: " + signature);
+ }
+ SignedConfigApplicator.applyConfig(mContext, config, signature);
+ } else {
+ if (DBG) Slog.d(TAG, "Package has no config/signature.");
+ }
+ }
+
+ /**
+ * Register to receive broadcasts from the package manager.
+ */
+ public static void registerUpdateReceiver(Context context) {
+ if (DBG) Slog.d(TAG, "Registering receiver");
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addDataScheme("package");
+ context.registerReceiver(new UpdateReceiver(), filter);
+ }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index aa11e1e..f0ebb75 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -195,12 +195,11 @@
"/system/bin/traced", // Perfetto.
"/system/bin/traced_probes", // Perfetto.
"webview_zygote",
- // Temporarily excluded zygote to investigate its forking consequences in
- // NativeProcessMemoryState.
- // "zygote",
- // "zygote64",
+ "zygote",
+ "zygote64",
};
+ private static final int CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES = 8;
static final class CompanionHandler extends Handler {
CompanionHandler(Looper looper) {
@@ -1080,7 +1079,7 @@
e.writeLong(processMemoryState.rssInBytes);
e.writeLong(processMemoryState.cacheInBytes);
e.writeLong(processMemoryState.swapInBytes);
- e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+ e.writeLong(0); // unused
e.writeLong(processMemoryState.startTimeNanos);
pulledData.add(e);
}
@@ -1089,6 +1088,7 @@
private void pullNativeProcessMemoryState(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
+ final List<String> processNames = Arrays.asList(MEMORY_INTERESTING_NATIVE_PROCESSES);
int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
for (int i = 0; i < pids.length; i++) {
int pid = pids[i];
@@ -1098,13 +1098,19 @@
}
int uid = getUidForPid(pid);
String processName = readCmdlineFromProcfs(pid);
+ // Sometimes we get here processName that is not included in the whitelist. It comes
+ // from forking the zygote for an app. We can ignore that sample because this process
+ // is collected by ProcessMemoryState.
+ if (!processNames.contains(processName)) {
+ continue;
+ }
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
e.writeString(processName);
e.writeLong(memoryStat.pgfault);
e.writeLong(memoryStat.pgmajfault);
e.writeLong(memoryStat.rssInBytes);
- e.writeLong(memoryStat.rssHighWatermarkInBytes);
+ e.writeLong(0); // unused
e.writeLong(memoryStat.startTimeNanos);
pulledData.add(e);
}
@@ -1654,6 +1660,11 @@
return;
}
int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
+ if (cpuFrequencies.length != CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES) {
+ Slog.w(TAG, "Expected " + CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES
+ + " frequencies, but got " + cpuFrequencies.length);
+ return;
+ }
for (int i = 0; i < processCpuUsages.size(); i++) {
KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
@@ -1667,23 +1678,18 @@
continue;
}
- for (int k = 0; k < threadCpuUsage.usageTimesMillis.length; k++) {
- // Do not report CPU usage at a frequency when it's zero
- if (threadCpuUsage.usageTimesMillis[k] == 0) {
- continue;
- }
-
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(processCpuUsage.uid);
- e.writeInt(processCpuUsage.processId);
- e.writeInt(threadCpuUsage.threadId);
- e.writeString(processCpuUsage.processName);
- e.writeString(threadCpuUsage.threadName);
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(processCpuUsage.uid);
+ e.writeInt(processCpuUsage.processId);
+ e.writeInt(threadCpuUsage.threadId);
+ e.writeString(processCpuUsage.processName);
+ e.writeString(threadCpuUsage.threadName);
+ for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES; k++) {
e.writeInt(cpuFrequencies[k]);
e.writeInt(threadCpuUsage.usageTimesMillis[k]);
- pulledData.add(e);
}
+ pulledData.add(e);
}
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 361622f..6e4c00e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -17,13 +17,17 @@
package com.android.server.statusbar;
import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityThread;
+import android.app.Notification;
import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -39,7 +43,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
-import android.view.Display;
+import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.statusbar.IStatusBar;
@@ -62,7 +66,7 @@
* A note on locking: We rely on the fact that calls onto mBar are oneway or
* if they are local, that they just enqueue messages to not deadlock.
*/
-public class StatusBarManagerService extends IStatusBarService.Stub {
+public class StatusBarManagerService extends IStatusBarService.Stub implements DisplayListener {
private static final String TAG = "StatusBarManagerService";
private static final boolean SPEW = false;
@@ -78,24 +82,13 @@
private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
private GlobalActionsProvider.GlobalActionsListener mGlobalActionListener;
private IBinder mSysUiVisToken = new Binder();
- private int mDisabled1 = 0;
- private int mDisabled2 = 0;
private final Object mLock = new Object();
private final DeathRecipient mDeathRecipient = new DeathRecipient();
- // encompasses lights-out mode and other flags defined on View
- private int mSystemUiVisibility = 0;
- private int mFullscreenStackSysUiVisibility;
- private int mDockedStackSysUiVisibility;
- private final Rect mFullscreenStackBounds = new Rect();
- private final Rect mDockedStackBounds = new Rect();
- private boolean mMenuVisible = false;
- private int mImeWindowVis = 0;
- private int mImeBackDisposition;
- private boolean mShowImeSwitcher;
- private IBinder mImeToken = null;
private int mCurrentUserId;
+ private SparseArray<UiState> mDisplayUiState = new SparseArray<>();
+
private class DeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
mBar.asBinder().unlinkToDeath(this,0);
@@ -184,8 +177,29 @@
LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
+
+ // We always have a default display.
+ final UiState state = new UiState();
+ mDisplayUiState.put(DEFAULT_DISPLAY, state);
+
+ final DisplayManager displayManager =
+ (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ displayManager.registerDisplayListener(this, mHandler);
}
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mDisplayUiState.remove(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {}
+
/**
* Private API used by NotificationManagerService.
*/
@@ -239,22 +253,14 @@
@Override
public void topAppWindowChanged(int displayId, boolean menuVisible) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
- StatusBarManagerService.this.topAppWindowChanged(menuVisible);
+ StatusBarManagerService.this.topAppWindowChanged(displayId, menuVisible);
}
@Override
public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
String cause) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
- StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
+ StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
}
@@ -271,13 +277,9 @@
@Override
public void appTransitionFinished(int displayId) {
enforceStatusBarService();
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
- mBar.appTransitionFinished();
+ mBar.appTransitionFinished(displayId);
} catch (RemoteException ex) {}
}
}
@@ -373,39 +375,27 @@
@Override
public void setWindowState(int displayId, int window, int state) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
- mBar.setWindowState(window, state);
+ mBar.setWindowState(displayId, window, state);
} catch (RemoteException ex) {}
}
}
@Override
public void appTransitionPending(int displayId) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
- mBar.appTransitionPending();
+ mBar.appTransitionPending(displayId);
} catch (RemoteException ex) {}
}
}
@Override
public void appTransitionCancelled(int displayId) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
- mBar.appTransitionCancelled();
+ mBar.appTransitionCancelled(displayId);
} catch (RemoteException ex) {}
}
}
@@ -413,14 +403,10 @@
@Override
public void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
long statusBarAnimationsDuration) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
- return;
- }
if (mBar != null) {
try {
mBar.appTransitionStarting(
- statusBarAnimationsStartTime, statusBarAnimationsDuration);
+ displayId, statusBarAnimationsStartTime, statusBarAnimationsDuration);
} catch (RemoteException ex) {}
}
}
@@ -448,6 +434,7 @@
return false;
}
+ // TODO(b/118592525): support it per display if necessary.
@Override
public void onProposedRotationChanged(int rotation, boolean isValid) {
if (mBar != null){
@@ -461,7 +448,9 @@
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
@Override
public boolean isGlobalActionsDisabled() {
- return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
+ // TODO(b/118592525): support global actions for multi-display.
+ final int disabled2 = mDisplayUiState.get(DEFAULT_DISPLAY).getDisabled2();
+ return (disabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
}
@Override
@@ -597,8 +586,8 @@
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
- boolean requireConfirmation, int userId) {
+ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int type, boolean requireConfirmation, int userId) {
enforceBiometricDialog();
if (mBar != null) {
try {
@@ -653,19 +642,33 @@
}
@Override
+ public void showBiometricTryAgain() {
+ enforceBiometricDialog();
+ if (mBar != null) {
+ try {
+ mBar.showBiometricTryAgain();
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ // TODO(b/117478341): make it aware of multi-display if needed.
+ @Override
public void disable(int what, IBinder token, String pkg) {
disableForUser(what, token, pkg, mCurrentUserId);
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
@Override
public void disableForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
synchronized (mLock) {
- disableLocked(userId, what, token, pkg, 1);
+ disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
}
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
/**
* Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
* To re-enable everything, pass {@link #DISABLE_NONE}.
@@ -677,6 +680,7 @@
disable2ForUser(what, token, pkg, mCurrentUserId);
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
/**
* Disable additional status bar features for a given user. Pass the bitwise-or of the
* DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
@@ -688,11 +692,12 @@
enforceStatusBar();
synchronized (mLock) {
- disableLocked(userId, what, token, pkg, 2);
+ disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2);
}
}
- private void disableLocked(int userId, int what, IBinder token, String pkg, int whichFlag) {
+ private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg,
+ int whichFlag) {
// It's important that the the callback and the call to mBar get done
// in the same order when multiple threads are calling this function
// so they are paired correctly. The messages on the handler will be
@@ -711,22 +716,19 @@
disabledData += " ([" + i + "] " + tok + "), ";
}
disabledData += " }";
- Log.d(TAG, "disabledlocked (b/113914868): net1=" + net1 + ", mDisabled1=" + mDisabled1
- + ", token=" + token + ", mDisableRecords=" + mDisableRecords.size() + " => "
- + disabledData);
- }
+ final UiState state = getUiState(displayId);
- if (net1 != mDisabled1 || net2 != mDisabled2) {
- mDisabled1 = net1;
- mDisabled2 = net2;
- mHandler.post(new Runnable() {
- public void run() {
- mNotificationDelegate.onSetDisabled(net1);
- }
- });
+ Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1
+ + ", mDisabled1=" + state.mDisabled1 + ", token=" + token
+ + ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
+ }
+ final UiState state = getUiState(displayId);
+ if (state.disableEquals(net1, net2)) {
+ state.setDisabled(net1, net2);
+ mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
if (mBar != null) {
try {
- mBar.disable(net1, net2);
+ mBar.disable(displayId, net1, net2);
} catch (RemoteException ex) {
}
}
@@ -796,26 +798,27 @@
* response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
* to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
*/
- private void topAppWindowChanged(final boolean menuVisible) {
+ private void topAppWindowChanged(int displayId, final boolean menuVisible) {
enforceStatusBar();
- if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
-
+ if (SPEW) {
+ Slog.d(TAG, "display#" + displayId + ": "
+ + (menuVisible ? "showing" : "hiding") + " MENU key");
+ }
synchronized(mLock) {
- mMenuVisible = menuVisible;
- mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.topAppWindowChanged(menuVisible);
- } catch (RemoteException ex) {
- }
+ getUiState(displayId).setMenuVisible(menuVisible);
+ mHandler.post(() -> {
+ if (mBar != null) {
+ try {
+ mBar.topAppWindowChanged(displayId, menuVisible);
+ } catch (RemoteException ex) {
}
}
});
}
}
+ // TODO(b/117478341): support back button change when IME is showing on a external display.
@Override
public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
final boolean showImeSwitcher) {
@@ -829,39 +832,42 @@
// In case of IME change, we need to call up setImeWindowStatus() regardless of
// mImeWindowVis because mImeWindowVis may not have been set to false when the
// previous IME was destroyed.
- mImeWindowVis = vis;
- mImeBackDisposition = backDisposition;
- mImeToken = token;
- mShowImeSwitcher = showImeSwitcher;
- mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
- } catch (RemoteException ex) {
- }
- }
- }
+ // TODO(b/117478341): support back button change when IME is showing on a external
+ // display.
+ getUiState(DEFAULT_DISPLAY)
+ .setImeWindowState(vis, backDisposition, showImeSwitcher, token);
+
+ mHandler.post(() -> {
+ if (mBar == null) return;
+ try {
+ // TODO(b/117478341): support back button change when IME is showing on a
+ // external display.
+ mBar.setImeWindowStatus(
+ DEFAULT_DISPLAY, token, vis, backDisposition, showImeSwitcher);
+ } catch (RemoteException ex) { }
});
}
}
@Override
- public void setSystemUiVisibility(int vis, int mask, String cause) {
- setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
+ public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) {
+ final UiState state = getUiState(displayId);
+ setSystemUiVisibility(displayId, vis, 0, 0, mask,
+ state.mFullscreenStackBounds, state.mDockedStackBounds, cause);
}
- private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
- Rect fullscreenBounds, Rect dockedBounds, String cause) {
+ private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+ int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) {
// also allows calls from window manager which is in this process.
enforceStatusBarService();
if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
synchronized (mLock) {
- updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
+ updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask,
fullscreenBounds, dockedBounds);
disableLocked(
+ displayId,
mCurrentUserId,
vis & StatusBarManager.DISABLE_MASK,
mSysUiVisToken,
@@ -869,30 +875,107 @@
}
}
- private void updateUiVisibilityLocked(final int vis,
+ private void updateUiVisibilityLocked(final int displayId, final int vis,
final int fullscreenStackVis, final int dockedStackVis, final int mask,
final Rect fullscreenBounds, final Rect dockedBounds) {
- if (mSystemUiVisibility != vis
- || mFullscreenStackSysUiVisibility != fullscreenStackVis
- || mDockedStackSysUiVisibility != dockedStackVis
- || !mFullscreenStackBounds.equals(fullscreenBounds)
- || !mDockedStackBounds.equals(dockedBounds)) {
+ final UiState state = getUiState(displayId);
+ if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis,
+ fullscreenBounds, dockedBounds)) {
+ state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds,
+ dockedBounds);
+ mHandler.post(() -> {
+ if (mBar != null) {
+ try {
+ mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
+ dockedStackVis, mask, fullscreenBounds, dockedBounds);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Can not get StatusBar!");
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * @return {@link UiState} specified by {@code displayId}.
+ *
+ * <p>
+ * Note: If {@link UiState} specified by {@code displayId} does not exist, {@link UiState}
+ * will be allocated and {@code mDisplayUiState} will be updated accordingly.
+ * <p/>
+ */
+ private UiState getUiState(int displayId) {
+ UiState state = mDisplayUiState.get(displayId);
+ if (state == null) {
+ state = new UiState();
+ mDisplayUiState.put(displayId, state);
+ }
+ return state;
+ }
+
+ private class UiState {
+ private int mSystemUiVisibility = 0;
+ private int mFullscreenStackSysUiVisibility = 0;
+ private int mDockedStackSysUiVisibility = 0;
+ private final Rect mFullscreenStackBounds = new Rect();
+ private final Rect mDockedStackBounds = new Rect();
+ private boolean mMenuVisible = false;
+ private int mDisabled1 = 0;
+ private int mDisabled2 = 0;
+ private int mImeWindowVis = 0;
+ private int mImeBackDisposition = 0;
+ private boolean mShowImeSwitcher = false;
+ private IBinder mImeToken = null;
+
+ private int getDisabled1() {
+ return mDisabled1;
+ }
+
+ private int getDisabled2() {
+ return mDisabled2;
+ }
+
+ private void setDisabled(int disabled1, int disabled2) {
+ mDisabled1 = disabled1;
+ mDisabled2 = disabled2;
+ }
+
+ private boolean isMenuVisible() {
+ return mMenuVisible;
+ }
+
+ private void setMenuVisible(boolean menuVisible) {
+ mMenuVisible = menuVisible;
+ }
+
+ private boolean disableEquals(int disabled1, int disabled2) {
+ return mDisabled1 == disabled1 && mDisabled2 == disabled2;
+ }
+
+ private void setSystemUiState(final int vis, final int fullscreenStackVis,
+ final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
mSystemUiVisibility = vis;
mFullscreenStackSysUiVisibility = fullscreenStackVis;
mDockedStackSysUiVisibility = dockedStackVis;
mFullscreenStackBounds.set(fullscreenBounds);
mDockedStackBounds.set(dockedBounds);
- mHandler.post(new Runnable() {
- public void run() {
- if (mBar != null) {
- try {
- mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
- mask, fullscreenBounds, dockedBounds);
- } catch (RemoteException ex) {
- }
- }
- }
- });
+ }
+
+ private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis,
+ final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
+ return mSystemUiVisibility == vis
+ && mFullscreenStackSysUiVisibility == fullscreenStackVis
+ && mDockedStackSysUiVisibility == dockedStackVis
+ && mFullscreenStackBounds.equals(fullscreenBounds)
+ && mDockedStackBounds.equals(dockedBounds);
+ }
+
+ private void setImeWindowState(final int vis, final int backDisposition,
+ final boolean showImeSwitcher, final IBinder token) {
+ mImeWindowVis = vis;
+ mImeBackDisposition = backDisposition;
+ mShowImeSwitcher = showImeSwitcher;
+ mImeToken = token;
}
}
@@ -927,6 +1010,7 @@
// ================================================================================
// Callbacks from the status bar service.
// ================================================================================
+ // TODO(b/118592525): refactor it as an IStatusBar API.
@Override
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
@@ -944,18 +1028,21 @@
}
}
synchronized (mLock) {
+ // TODO(b/118592525): Currently, status bar only works on the default display.
+ // Make it aware of multi-display if needed.
+ final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
- switches[1] = mSystemUiVisibility;
- switches[2] = mMenuVisible ? 1 : 0;
- switches[3] = mImeWindowVis;
- switches[4] = mImeBackDisposition;
- switches[5] = mShowImeSwitcher ? 1 : 0;
+ switches[1] = state.mSystemUiVisibility;
+ switches[2] = state.mMenuVisible ? 1 : 0;
+ switches[3] = state.mImeWindowVis;
+ switches[4] = state.mImeBackDisposition;
+ switches[5] = state.mShowImeSwitcher ? 1 : 0;
switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
- switches[7] = mFullscreenStackSysUiVisibility;
- switches[8] = mDockedStackSysUiVisibility;
- binders.add(mImeToken);
- fullscreenStackBounds.set(mFullscreenStackBounds);
- dockedStackBounds.set(mDockedStackBounds);
+ switches[7] = state.mFullscreenStackSysUiVisibility;
+ switches[8] = state.mDockedStackSysUiVisibility;
+ binders.add(state.mImeToken);
+ fullscreenStackBounds.set(state.mFullscreenStackBounds);
+ dockedStackBounds.set(state.mDockedStackBounds);
}
}
@@ -1080,14 +1167,16 @@
}
@Override
- public void onNotificationActionClick(String key, int actionIndex, NotificationVisibility nv) {
+ public void onNotificationActionClick(
+ String key, int actionIndex, Notification.Action action, NotificationVisibility nv,
+ boolean generatedByAssistant) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
- actionIndex, nv);
+ actionIndex, action, nv, generatedByAssistant);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1295,8 +1384,13 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
synchronized (mLock) {
- pw.println(" mDisabled1=0x" + Integer.toHexString(mDisabled1));
- pw.println(" mDisabled2=0x" + Integer.toHexString(mDisabled2));
+ for (int i = 0; i < mDisplayUiState.size(); i++) {
+ final int key = mDisplayUiState.keyAt(i);
+ final UiState state = mDisplayUiState.get(key);
+ pw.println(" displayId=" + key);
+ pw.println(" mDisabled1=0x" + Integer.toHexString(state.getDisabled1()));
+ pw.println(" mDisabled2=0x" + Integer.toHexString(state.getDisabled2()));
+ }
final int N = mDisableRecords.size();
pw.println(" mDisableRecords.size=" + N);
for (int i=0; i<N; i++) {
diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index 6a0b648..9d6a647 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -16,6 +16,7 @@
package com.android.server.storage;
+import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.Os;
@@ -25,8 +26,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnectorException;
import libcore.io.IoUtils;
-import java.io.File;
-import java.io.FileNotFoundException;
import java.util.concurrent.CountDownLatch;
/**
@@ -87,7 +86,7 @@
}
}
- public ParcelFileDescriptor openFile(int pid, int mountId, int fileId, int mode)
+ public ParcelFileDescriptor openFile(int mountId, int fileId, int mode)
throws FuseUnavailableMountException, InterruptedException {
final MountScope scope;
synchronized (this) {
@@ -96,17 +95,14 @@
throw new FuseUnavailableMountException(mountId);
}
}
- if (scope.pid != pid) {
- throw new SecurityException("PID does not match");
- }
final boolean result = scope.waitForMount();
if (result == false) {
throw new FuseUnavailableMountException(mountId);
}
try {
- return ParcelFileDescriptor.open(
- new File(scope.mountPoint, String.valueOf(fileId)), mode);
- } catch (FileNotFoundException error) {
+ int flags = FileUtils.translateModePfdToPosix(mode);
+ return scope.openFile(mountId, fileId, flags);
+ } catch (NativeDaemonConnectorException error) {
throw new FuseUnavailableMountException(mountId);
}
}
@@ -131,17 +127,13 @@
public static abstract class MountScope implements AutoCloseable {
public final int uid;
- public final int pid;
public final int mountId;
- public final File mountPoint;
private final CountDownLatch mMounted = new CountDownLatch(1);
private boolean mMountResult = false;
- public MountScope(int uid, int pid, int mountId) {
+ public MountScope(int uid, int mountId) {
this.uid = uid;
- this.pid = pid;
this.mountId = mountId;
- this.mountPoint = new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE, uid, mountId));
}
@GuardedBy("AppFuseBridge.this")
@@ -159,6 +151,8 @@
}
public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException;
+ public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
+ throws NativeDaemonConnectorException;
}
private native long native_new();
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 8d27d1e..c8a68b4 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -402,7 +402,7 @@
throws RemoteException {
try {
final int uid = context.getPackageManager()
- .getPackageUid(packageName, 0);
+ .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
Preconditions.checkArgument(Binder.getCallingUid() == uid);
} catch (IllegalArgumentException | NullPointerException |
PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index da0a794..4b413e5 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -49,7 +49,7 @@
import libcore.icu.ICU;
import libcore.timezone.TzDataSetVersion;
import libcore.timezone.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d5e59c8..1163d39 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -626,7 +626,7 @@
updateServiceConnectionLocked(serviceState.component, userId);
}
- private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
+ private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
@@ -638,6 +638,7 @@
// Set up a callback to send the session token.
ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
+ boolean created = true;
// Create a session. When failed, send a null token immediately.
try {
if (sessionState.isRecordingSession) {
@@ -647,11 +648,12 @@
}
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
- removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
null, sessionState.seq);
+ created = false;
}
channels[1].dispose();
+ return created;
}
private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
@@ -1193,8 +1195,10 @@
serviceState.sessionTokens.add(sessionToken);
if (serviceState.service != null) {
- createSessionInternalLocked(serviceState.service, sessionToken,
- resolvedUserId);
+ if (!createSessionInternalLocked(serviceState.service, sessionToken,
+ resolvedUserId)) {
+ removeSessionStateLocked(sessionToken, resolvedUserId);
+ }
} else {
updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
}
@@ -2282,9 +2286,17 @@
}
}
+ List<IBinder> tokensToBeRemoved = new ArrayList<>();
+
// And create sessions, if any.
for (IBinder sessionToken : serviceState.sessionTokens) {
- createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
+ if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
+ tokensToBeRemoved.add(sessionToken);
+ }
+ }
+
+ for (IBinder sessionToken : tokensToBeRemoved) {
+ removeSessionStateLocked(sessionToken, mUserId);
}
for (TvInputState inputState : userState.inputMap.values()) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6ede423..64ff9cf 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1181,7 +1181,7 @@
// TODO(multi-display) TBD.
if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
try {
- connector.mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
+ connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set ambient mode state", e);
}
@@ -2023,11 +2023,17 @@
}
}
- // TODO(b/115486823) Extends this method with specific display.
- public void setInAmbientMode(boolean inAmbienMode, boolean animated) {
+ /**
+ * TODO(b/115486823) Extends this method with specific display.
+ * Propagate ambient state to wallpaper engine.
+ *
+ * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
+ * @param animationDuration Duration of the animation, or 0 when immediate.
+ */
+ public void setInAmbientMode(boolean inAmbientMode, long animationDuration) {
final IWallpaperEngine engine;
synchronized (mLock) {
- mInAmbientMode = inAmbienMode;
+ mInAmbientMode = inAmbientMode;
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
if (data != null && data.connection != null && data.connection.mInfo != null
&& data.connection.mInfo.supportsAmbientMode()) {
@@ -2040,7 +2046,7 @@
if (engine != null) {
try {
- engine.setInAmbientMode(inAmbienMode, animated);
+ engine.setInAmbientMode(inAmbientMode, animationDuration);
} catch (RemoteException e) {
// Cannot talk to wallpaper engine.
}
@@ -2344,7 +2350,7 @@
return false;
}
if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
- String msg = "Selected service does not require "
+ String msg = "Selected service does not have "
+ android.Manifest.permission.BIND_WALLPAPER
+ ": " + componentName;
if (fromUser) {
@@ -2396,6 +2402,22 @@
}
}
+ if (wi != null && wi.supportsAmbientMode()) {
+ final int hasPrivilege = mIPackageManager.checkPermission(
+ android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
+ serviceUserId);
+ if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Selected service does not have "
+ + android.Manifest.permission.AMBIENT_WALLPAPER
+ + ": " + componentName;
+ if (fromUser) {
+ throw new SecurityException(msg);
+ }
+ Slog.w(TAG, msg);
+ return false;
+ }
+ }
+
// Bind the service!
if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
@@ -2800,8 +2822,6 @@
IoUtils.closeQuietly(stream);
if (!success) {
- wpdData.mWidth = -1;
- wpdData.mHeight = -1;
wallpaper.cropHint.set(0, 0, 0, 0);
wpdData.mPadding.set(0, 0, 0, 0);
wallpaper.name = "";
@@ -2817,6 +2837,7 @@
}
}
+ ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
if (lockWallpaper != null) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e4d1cfe..fe0b5c2 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,9 +16,9 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -706,7 +706,7 @@
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
.setName(SURFACE_TITLE)
- .setSize(mTempPoint.x, mTempPoint.y) // not a typo
+ .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
.build();
} catch (OutOfResourcesException oore) {
@@ -784,7 +784,7 @@
public void updateSize() {
synchronized (mService.mGlobalLock) {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
+ mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y);
invalidate(mDirtyRect);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index ed36645..7bf2c94 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -39,8 +39,6 @@
import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.wm.ActivityStackSupervisor.TAG_STATES;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
@@ -48,6 +46,8 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.RootActivityContainer.FindTaskResult;
+import static com.android.server.wm.RootActivityContainer.TAG_STATES;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -84,7 +84,8 @@
*/
private static int sNextFreeStackId = 0;
- private ActivityStackSupervisor mSupervisor;
+ private ActivityTaskManagerService mService;
+ private RootActivityContainer mRootActivityContainer;
/** Actual Display this object tracks. */
int mDisplayId;
Display mDisplay;
@@ -141,8 +142,9 @@
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
- ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
- mSupervisor = supervisor;
+ ActivityDisplay(RootActivityContainer root, Display display) {
+ mRootActivityContainer = root;
+ mService = root.mService;
mDisplayId = display.getDisplayId();
mDisplay = display;
mWindowContainerController = createWindowContainerController();
@@ -168,7 +170,7 @@
if (displayId != DEFAULT_DISPLAY) {
final int displayState = mDisplay.getState();
if (displayState == Display.STATE_OFF && mOffToken == null) {
- mOffToken = mSupervisor.mService.acquireSleepToken("Display-off", displayId);
+ mOffToken = mService.acquireSleepToken("Display-off", displayId);
} else if (displayState == Display.STATE_ON && mOffToken != null) {
mOffToken.release();
mOffToken = null;
@@ -179,6 +181,11 @@
mWindowContainerController.onDisplayChanged();
}
+ @Override
+ public void onInitializeOverrideConfiguration(Configuration config) {
+ getOverrideConfiguration().updateFrom(config);
+ }
+
void addChild(ActivityStack stack, int position) {
if (position == POSITION_BOTTOM) {
position = 0;
@@ -189,7 +196,7 @@
+ " to displayId=" + mDisplayId + " position=" + position);
addStackReferenceIfNeeded(stack);
positionChildAt(stack, position);
- mSupervisor.mService.updateSleepIfNeededLocked();
+ mService.updateSleepIfNeededLocked();
}
void removeChild(ActivityStack stack) {
@@ -201,7 +208,7 @@
}
removeStackReferenceIfNeeded(stack);
releaseSelfIfNeeded();
- mSupervisor.mService.updateSleepIfNeededLocked();
+ mService.updateSleepIfNeededLocked();
onStackOrderChanged();
}
@@ -252,7 +259,7 @@
final ActivityStack currentFocusedStack = getFocusedStack();
if (currentFocusedStack != prevFocusedStack) {
mLastFocusedStack = prevFocusedStack;
- EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId,
+ EventLogTags.writeAmFocusedStack(mRootActivityContainer.mCurrentUser, mDisplayId,
currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
updateLastFocusedStackReason);
@@ -409,10 +416,10 @@
}
}
- final ActivityTaskManagerService service = mSupervisor.mService;
- if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
- service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
- service.mSupportsPictureInPicture, activityType)) {
+ if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow,
+ mService.mSupportsSplitScreenMultiWindow,
+ mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture,
+ activityType)) {
throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
+ windowingMode);
}
@@ -425,10 +432,12 @@
<T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop) {
if (windowingMode == WINDOWING_MODE_PINNED) {
- return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+ return (T) new PinnedActivityStack(this, stackId,
+ mRootActivityContainer.mStackSupervisor, onTop);
}
- return (T) new ActivityStack(
- this, stackId, mSupervisor, windowingMode, activityType, onTop);
+ return (T) new ActivityStack(this, stackId,
+ mRootActivityContainer.mStackSupervisor, windowingMode, activityType,
+ onTop);
}
/**
@@ -543,7 +552,7 @@
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
// TODO(b/111541062): Check if resumed activity on this display instead
- if (!mSupervisor.isTopDisplayFocusedStack(stack)
+ if (!mRootActivityContainer.isTopDisplayFocusedStack(stack)
&& stack.getResumedActivity() != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.getResumedActivity());
@@ -608,7 +617,7 @@
if (stack.getWindowingMode() != windowingMode) {
continue;
}
- mSupervisor.removeStack(stack);
+ mRootActivityContainer.mStackSupervisor.removeStack(stack);
}
}
}
@@ -623,7 +632,7 @@
for (int i = mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = mStacks.get(i);
if (stack.getActivityType() == activityType) {
- mSupervisor.removeStack(stack);
+ mRootActivityContainer.mStackSupervisor.removeStack(stack);
}
}
}
@@ -685,7 +694,7 @@
}
private void onSplitScreenModeDismissed() {
- mSupervisor.mWindowManager.deferSurfaceLayout();
+ mRootActivityContainer.mWindowManager.deferSurfaceLayout();
try {
// Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -709,12 +718,12 @@
mHomeStack.moveToFront("onSplitScreenModeDismissed");
topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
}
- mSupervisor.mWindowManager.continueSurfaceLayout();
+ mRootActivityContainer.mWindowManager.continueSurfaceLayout();
}
}
private void onSplitScreenModeActivated() {
- mSupervisor.mWindowManager.deferSurfaceLayout();
+ mRootActivityContainer.mWindowManager.deferSurfaceLayout();
try {
// Adjust the windowing mode of any affected by split-screen to split-screen secondary.
for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -729,7 +738,7 @@
false /* creating */);
}
} finally {
- mSupervisor.mWindowManager.continueSurfaceLayout();
+ mRootActivityContainer.mWindowManager.continueSurfaceLayout();
}
}
@@ -824,11 +833,10 @@
int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
@Nullable TaskRecord task, int activityType) {
// Make sure the windowing mode we are trying to use makes sense for what is supported.
- final ActivityTaskManagerService service = mSupervisor.mService;
- boolean supportsMultiWindow = service.mSupportsMultiWindow;
- boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
- boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
- boolean supportsPip = service.mSupportsPictureInPicture;
+ boolean supportsMultiWindow = mService.mSupportsMultiWindow;
+ boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
+ boolean supportsPip = mService.mSupportsPictureInPicture;
if (supportsMultiWindow) {
if (task != null) {
supportsMultiWindow = task.isResizeable();
@@ -932,7 +940,7 @@
// This activity can be considered the top running activity if we are not considering
// the locked state, the keyguard isn't locked, or we can show when locked.
if (topRunning != null && considerKeyguardState
- && mSupervisor.getKeyguardController().isKeyguardLocked()
+ && mRootActivityContainer.mStackSupervisor.getKeyguardController().isKeyguardLocked()
&& !topRunning.canShowWhenLocked()) {
return null;
}
@@ -1010,7 +1018,7 @@
@Override
protected ConfigurationContainer getParent() {
- return mSupervisor;
+ return mRootActivityContainer;
}
boolean isPrivate() {
@@ -1043,8 +1051,8 @@
// released (no more ActivityStack). But, we cannot release it at that moment or the
// related WindowContainer and WindowContainerController will also be removed. So, we
// set display as removed after reparenting stack finished.
- final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay();
- mSupervisor.beginDeferResume();
+ final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay();
+ mRootActivityContainer.mStackSupervisor.beginDeferResume();
try {
int numStacks = mStacks.size();
// Keep the order from bottom to top.
@@ -1070,7 +1078,7 @@
numStacks = mStacks.size();
}
} finally {
- mSupervisor.endDeferResume();
+ mRootActivityContainer.mStackSupervisor.endDeferResume();
}
mRemoved = true;
@@ -1082,9 +1090,9 @@
releaseSelfIfNeeded();
if (!mAllSleepTokens.isEmpty()) {
- mSupervisor.mSleepTokens.removeAll(mAllSleepTokens);
+ mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens);
mAllSleepTokens.clear();
- mSupervisor.mService.updateSleepIfNeededLocked();
+ mService.updateSleepIfNeededLocked();
}
}
@@ -1092,8 +1100,9 @@
if (mStacks.isEmpty() && mRemoved) {
mWindowContainerController.removeContainer();
mWindowContainerController = null;
- mSupervisor.removeChild(this);
- mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
+ mRootActivityContainer.removeChild(this);
+ mRootActivityContainer.mStackSupervisor
+ .getKeyguardController().onDisplayRemoved(mDisplayId);
}
}
@@ -1122,7 +1131,7 @@
boolean shouldSleep() {
return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
- && (mSupervisor.mService.mRunningVoice == null);
+ && (mService.mRunningVoice == null);
}
void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
@@ -1213,7 +1222,7 @@
@Nullable
ActivityRecord getHomeActivity() {
- return getHomeActivityForUser(mSupervisor.mCurrentUser);
+ return getHomeActivityForUser(mRootActivityContainer.mCurrentUser);
}
@Nullable
@@ -1233,7 +1242,7 @@
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.isActivityTypeHome()
- && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
+ && ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
return r;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index e3133ef..eff0f75 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -42,7 +42,7 @@
* It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
* or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
*
- * Note that the {@link ActivityRecord} provided as a parameter to some state transitions isn't
+ * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
* necessarily the same within a single launch sequence: it is only the top-most activity at the
* time (if any). Trampoline activities coalesce several activity starts into a single launch
* sequence.
@@ -94,6 +94,14 @@
public static final int TEMPERATURE_HOT = 3;
/**
+ * Typedef marker that a {@code byte[]} actually contains an
+ * <a href="proto/android/server/activitymanagerservice.proto">ActivityRecordProto</a>
+ * in the protobuf format.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ActivityRecordProto {}
+
+ /**
* Notifies the observer that a new launch sequence has begun as a result of a new intent.
*
* Once a launch sequence begins, the resolved activity will either subsequently start with
@@ -135,7 +143,7 @@
* Multiple calls to this method cannot occur without first terminating the current
* launch sequence.
*/
- public void onActivityLaunched(@NonNull ActivityRecord activity,
+ public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
@Temperature int temperature);
/**
@@ -157,7 +165,7 @@
* in the case of a trampoline, multiple activities could've been started
* and only the latest activity is reported here.
*/
- public void onActivityLaunchCancelled(@Nullable ActivityRecord abortingActivity);
+ public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] abortingActivity);
/**
* Notifies the observer that the current launch sequence has been successfully finished.
@@ -178,5 +186,5 @@
* and only the latest activity that was top-most during first-frame drawn
* is reported here.
*/
- public void onActivityLaunchFinished(@NonNull ActivityRecord finalActivity);
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java
new file mode 100644
index 0000000..fa90dc5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+
+/**
+ * Multi-cast delegate implementation for {@link ActivityMetricsLaunchObserver}.
+ *
+ * <br/><br/>
+ * This enables multiple launch observers to subscribe to {@link ActivityMetricsLogger}
+ * independently of each other.
+ *
+ * <br/><br/>
+ * Some callbacks in {@link ActivityMetricsLaunchObserver} have a {@code byte[]}
+ * parameter; this array is reused by all the registered observers, so it must not be written to
+ * (i.e. all observers must treat any array parameters as immutable).
+ *
+ * <br /><br />
+ * Multi-cast invocations occurs sequentially in-order of registered observers.
+ */
+public interface ActivityMetricsLaunchObserverRegistry {
+ /**
+ * Register an extra launch observer to receive the multi-cast.
+ *
+ * <br /><br />
+ * Multi-cast invocation happens in the same order the observers were registered. For example,
+ * <pre>
+ * registerLaunchObserver(A)
+ * registerLaunchObserver(B)
+ *
+ * obs.onIntentFailed() ->
+ * A.onIntentFailed()
+ * B.onIntentFailed()
+ * </pre>
+ */
+ void registerLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver);
+
+ /**
+ * Unregister an existing launch observer. It will not receive the multi-cast in the future.
+ *
+ * <br /><br />
+ * This does nothing if this observer was not already registered.
+ */
+ void unregisterLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver);
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 1c08d03..12690a9 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -99,10 +99,12 @@
import android.util.SparseIntArray;
import android.util.StatsLog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
/**
@@ -168,7 +170,8 @@
* Due to the global single concurrent launch sequence, all calls to this observer must be made
* in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
*/
- private final ActivityMetricsLaunchObserver mLaunchObserver = null;
+ private final LaunchObserverRegistryImpl mLaunchObserver;
+ @VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
private final class H extends Handler {
@@ -251,7 +254,7 @@
type = getTransitionType(info);
processRecord = findProcessForActivity(launchedActivity);
processName = launchedActivity.processName;
- userId = launchedActivity.userId;
+ userId = launchedActivity.mUserId;
launchedActivityShortComponentName = launchedActivity.shortComponentName;
activityRecordIdHashCode = System.identityHashCode(launchedActivity);
this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
@@ -263,6 +266,7 @@
mSupervisor = supervisor;
mContext = context;
mHandler = new H(looper);
+ mLaunchObserver = new LaunchObserverRegistryImpl(looper);
}
void logWindowState() {
@@ -277,7 +281,8 @@
mLastLogTimeSecs = now;
mWindowState = WINDOW_STATE_INVALID;
- ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+ ActivityStack stack =
+ mSupervisor.mRootActivityContainer.getTopDisplayFocusedStack();
if (stack == null) {
return;
}
@@ -289,7 +294,7 @@
@WindowingMode int windowingMode = stack.getWindowingMode();
if (windowingMode == WINDOWING_MODE_PINNED) {
- stack = mSupervisor.findStackBehind(stack);
+ stack = mSupervisor.mRootActivityContainer.findStackBehind(stack);
windowingMode = stack.getWindowingMode();
}
switch (windowingMode) {
@@ -515,7 +520,7 @@
if (info.launchedActivity != activityRecord) {
return;
}
- final TaskRecord t = activityRecord.getTask();
+ final TaskRecord t = activityRecord.getTaskRecord();
final SomeArgs args = SomeArgs.obtain();
args.arg1 = t;
args.arg2 = activityRecord;
@@ -850,9 +855,10 @@
builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,
targetUidHasAnyVisibleWindow ? 1 : 0);
builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag);
- builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
- builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+ if (intent != null) {
+ builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+ }
if (callerApp != null) {
builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
@@ -881,29 +887,34 @@
(nowUptime - callerApp.getWhenUnimportant()));
}
}
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString());
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
- if (r.lastVisibleTime != 0) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
- (nowUptime - r.lastVisibleTime));
- }
- if (r.resultTo != null) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
- r.resultTo.shortComponentName);
- }
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
- r.visibleIgnoringKeyguard ? 1 : 0);
- if (r.lastLaunchTime != 0) {
- builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
- (nowUptime - r.lastLaunchTime));
+ if (r != null) {
+ builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY,
+ r.mActivityComponent.toShortString());
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
+ if (r.lastVisibleTime != 0) {
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
+ (nowUptime - r.lastVisibleTime));
+ }
+ if (r.resultTo != null) {
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME,
+ r.resultTo.packageName);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
+ r.resultTo.shortComponentName);
+ }
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
+ r.visibleIgnoringKeyguard ? 1 : 0);
+ if (r.lastLaunchTime != 0) {
+ builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
+ (nowUptime - r.lastLaunchTime));
+ }
}
mMetricsLogger.write(builder);
}
@@ -993,12 +1004,19 @@
}
}
+ public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
+ return mLaunchObserver;
+ }
+
/** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
private void launchObserverNotifyIntentStarted(Intent intent) {
- if (mLaunchObserver != null) {
- // Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onIntentStarted(intent);
- }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyIntentStarted");
+
+ // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+ mLaunchObserver.onIntentStarted(intent);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1007,9 +1025,12 @@
* intent being delivered to the top running activity.
*/
private void launchObserverNotifyIntentFailed() {
- if (mLaunchObserver != null) {
- mLaunchObserver.onIntentFailed();
- }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyIntentFailed");
+
+ mLaunchObserver.onIntentFailed();
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1017,14 +1038,17 @@
* has started.
*/
private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunched");
+
@ActivityMetricsLaunchObserver.Temperature int temperature =
convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
- if (mLaunchObserver != null) {
- // Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onActivityLaunched(info.launchedActivity,
- temperature);
- }
+ // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+ mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.launchedActivity),
+ temperature);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1032,11 +1056,15 @@
* cancelled.
*/
private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
- final ActivityRecord launchedActivity = info != null ? info.launchedActivity : null;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
- if (mLaunchObserver != null) {
- mLaunchObserver.onActivityLaunchCancelled(launchedActivity);
- }
+ final @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] activityRecordProto =
+ info != null ? convertActivityRecordToProto(info.launchedActivity) : null;
+
+ mLaunchObserver.onActivityLaunchCancelled(activityRecordProto);
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
@@ -1044,11 +1072,34 @@
* has fully finished (successfully).
*/
private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
- final ActivityRecord launchedActivity = info.launchedActivity;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
- if (mLaunchObserver != null) {
- mLaunchObserver.onActivityLaunchFinished(launchedActivity);
- }
+ mLaunchObserver.onActivityLaunchFinished(
+ convertActivityRecordToProto(info.launchedActivity));
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @VisibleForTesting
+ static @ActivityMetricsLaunchObserver.ActivityRecordProto byte[]
+ convertActivityRecordToProto(ActivityRecord record) {
+ // May take non-negligible amount of time to convert ActivityRecord into a proto,
+ // so track the time.
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:convertActivityRecordToProto");
+
+ // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream,
+ // so create a new one every time.
+ final ProtoOutputStream protoOutputStream =
+ new ProtoOutputStream(LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+ // Write this data out as the top-most ActivityRecordProto (i.e. it is not a sub-object).
+ record.writeToProto(protoOutputStream);
+ final byte[] bytes = protoOutputStream.getBytes();
+
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ return bytes;
}
private static @ActivityMetricsLaunchObserver.Temperature int
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5e92b9e..255a003 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -18,7 +18,17 @@
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -63,6 +73,7 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
import static android.content.res.Configuration.EMPTY;
@@ -111,12 +122,18 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService
+ .RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -147,6 +164,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Build;
@@ -167,11 +185,14 @@
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.util.XmlUtils;
@@ -200,7 +221,7 @@
/**
* An entry in the history stack, representing an activity.
*/
-final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
+final class ActivityRecord extends ConfigurationContainer {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
@@ -223,18 +244,20 @@
private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
- final ActivityTaskManagerService service; // owner
+ final ActivityTaskManagerService mAtmService; // owner
final IApplicationToken.Stub appToken; // window manager token
- AppWindowContainerController mWindowContainerController;
+ // TODO: Remove after unification
+ AppWindowToken mAppWindowToken;
+
final ActivityInfo info; // all about me
// TODO: This is duplicated state already contained in info.applicationInfo - remove
ApplicationInfo appInfo; // information about activity's app
final int launchedFromPid; // always the pid who started the activity.
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
- final int userId; // Which user is this running for?
+ final int mUserId; // Which user is this running for?
final Intent intent; // the original intent that generated us
- final ComponentName realActivity; // the intent component, or target of an alias.
+ final ComponentName mActivityComponent; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
final String resolvedType; // as per original caller;
final String packageName; // the package implementing intent's component
@@ -322,6 +345,7 @@
private boolean inHistory; // are we in the history stack?
final ActivityStackSupervisor mStackSupervisor;
+ final RootActivityContainer mRootActivityContainer;
static final int STARTING_WINDOW_NOT_SHOWN = 0;
static final int STARTING_WINDOW_SHOWN = 1;
@@ -383,14 +407,14 @@
pw.print(" processName="); pw.println(processName);
pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
- pw.print(" userId="); pw.println(userId);
+ pw.print(" userId="); pw.println(mUserId);
pw.print(prefix); pw.print("app="); pw.println(app);
pw.print(prefix); pw.println(intent.toInsecureStringWithClip());
pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
pw.print(" task="); pw.println(task);
pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
- pw.print(prefix); pw.print("realActivity=");
- pw.println(realActivity.flattenToShortString());
+ pw.print(prefix); pw.print("mActivityComponent=");
+ pw.println(mActivityComponent.flattenToShortString());
if (appInfo != null) {
pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
@@ -620,7 +644,7 @@
"Reporting activity moved to display" + ", activityRecord=" + this
+ ", displayId=" + displayId + ", config=" + config);
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
MoveToDisplayItem.obtain(displayId, config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -638,7 +662,7 @@
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
+ config);
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
ActivityConfigurationChangeItem.obtain(config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -665,7 +689,7 @@
private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
try {
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
} catch (Exception e) {
// If process died, I don't care.
@@ -696,7 +720,7 @@
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
overrideConfig));
} catch (Exception e) {
@@ -717,10 +741,10 @@
@Override
protected ConfigurationContainer getParent() {
- return getTask();
+ return getTaskRecord();
}
- TaskRecord getTask() {
+ TaskRecord getTaskRecord() {
return task;
}
@@ -741,12 +765,12 @@
* @param reparenting Whether we're in the middle of reparenting.
*/
void setTask(TaskRecord task, boolean reparenting) {
- // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}.
- if (task != null && task == getTask()) {
+ // Do nothing if the {@link TaskRecord} is the same as the current {@link getTaskRecord}.
+ if (task != null && task == getTaskRecord()) {
return;
}
- final ActivityStack oldStack = getStack();
+ final ActivityStack oldStack = getActivityStack();
final ActivityStack newStack = task != null ? task.getStack() : null;
// Inform old stack (if present) of activity removal and new stack (if set) of activity
@@ -769,10 +793,16 @@
}
/**
- * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)}
+ * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
+ * This information helps AWT know that the app is in the process of pausing before it gets the
+ * signal on the WM side.
*/
void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
- getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip);
+ if (mAppWindowToken == null) {
+ return;
+ }
+
+ mAppWindowToken.setWillCloseOrEnterPip(willCloseOrEnterPip);
}
static class Token extends IApplicationToken.Stub {
@@ -789,7 +819,7 @@
return null;
}
ActivityRecord r = token.weakActivity.get();
- if (r == null || r.getStack() == null) {
+ if (r == null || r.getActivityStack() == null) {
return null;
}
return r;
@@ -822,7 +852,7 @@
}
boolean isResolverActivity() {
- return ResolverActivity.class.getName().equals(realActivity.getClassName());
+ return ResolverActivity.class.getName().equals(mActivityComponent.getClassName());
}
boolean isResolverOrChildActivity() {
@@ -831,7 +861,7 @@
}
try {
return ResolverActivity.class.isAssignableFrom(
- Object.class.getClassLoader().loadClass(realActivity.getClassName()));
+ Object.class.getClassLoader().loadClass(mActivityComponent.getClassName()));
} catch (ClassNotFoundException e) {
return false;
}
@@ -843,13 +873,14 @@
ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
ActivityOptions options, ActivityRecord sourceRecord) {
- service = _service;
+ mAtmService = _service;
+ mRootActivityContainer = _service.mRootActivityContainer;
appToken = new Token(this, _intent);
info = aInfo;
launchedFromPid = _launchedFromPid;
launchedFromUid = _launchedFromUid;
launchedFromPackage = _launchedFromPackage;
- userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
+ mUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
@@ -886,9 +917,9 @@
|| (aInfo.targetActivity.equals(_intent.getComponent().getClassName())
&& (aInfo.launchMode == LAUNCH_MULTIPLE
|| aInfo.launchMode == LAUNCH_SINGLE_TOP))) {
- realActivity = _intent.getComponent();
+ mActivityComponent = _intent.getComponent();
} else {
- realActivity = new ComponentName(aInfo.packageName, aInfo.targetActivity);
+ mActivityComponent = new ComponentName(aInfo.packageName, aInfo.targetActivity);
}
taskAffinity = aInfo.taskAffinity;
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
@@ -927,7 +958,7 @@
launchMode = aInfo.launchMode;
Entry ent = AttributeCache.instance().get(packageName,
- realTheme, com.android.internal.R.styleable.Window, userId);
+ realTheme, com.android.internal.R.styleable.Window, mUserId);
if (ent != null) {
fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
@@ -991,13 +1022,9 @@
return hasProcess() && app.hasThread();
}
- AppWindowContainerController getWindowContainerController() {
- return mWindowContainerController;
- }
-
- void createWindowContainer() {
- if (mWindowContainerController != null) {
- throw new IllegalArgumentException("Window container=" + mWindowContainerController
+ void createAppWindowToken() {
+ if (mAppWindowToken != null) {
+ throw new IllegalArgumentException("App Window Token=" + mAppWindowToken
+ " already created for r=" + this);
}
@@ -1010,12 +1037,31 @@
// Make sure override configuration is up-to-date before using to create window controller.
updateOverrideConfiguration();
- mWindowContainerController = new AppWindowContainerController(taskController, appToken,
- realActivity, this, Integer.MAX_VALUE /* add on top */, info.screenOrientation,
- fullscreen, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
- task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
- appInfo.targetSdkVersion, mRotationAnimationHint,
- ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+ // TODO: remove after unification
+ mAppWindowToken = mAtmService.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder());
+ if (mAppWindowToken != null) {
+ // TODO: Should this throw an exception instead?
+ Slog.w(TAG, "Attempted to add existing app token: " + appToken);
+ } else {
+ final Task container = taskController.mContainer;
+ if (container == null) {
+ throw new IllegalArgumentException("AppWindowContainerController: invalid "
+ + " controller=" + taskController);
+ }
+ mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,
+ task.voiceSession != null, container.getDisplayContent(),
+ ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this)
+ * 1000000L, fullscreen,
+ (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion,
+ info.screenOrientation, mRotationAnimationHint, info.configChanges,
+ mLaunchTaskBehind, isAlwaysFocusable());
+ if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "addAppToken: "
+ + mAppWindowToken + " controller=" + taskController + " at "
+ + Integer.MAX_VALUE);
+ }
+ container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */);
+ }
task.addActivityToTop(this);
@@ -1026,17 +1072,51 @@
mLastReportedPictureInPictureMode = inPinnedWindowingMode();
}
+ boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+ CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+ IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "setAppStartingWindow: token=" + appToken
+ + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
+ + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
+ + " allowTaskSnapshot=" + allowTaskSnapshot);
+ }
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken);
+ return false;
+ }
+ return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
+ labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
+ processRunning, allowTaskSnapshot, activityCreated, fromRecents);
+ }
+
+ // TODO: Remove after unification
+ @VisibleForTesting
+ AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
+ boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
+ boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
+ int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
+ boolean alwaysFocusable) {
+ return new AppWindowToken(service, token, mActivityComponent, voiceInteraction, dc,
+ inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
+ rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
+ this);
+ }
+
void removeWindowContainer() {
- // Do not try to remove a window container if we have already removed it.
- if (mWindowContainerController == null) {
+ if (mAtmService.mWindowManager.mRoot == null) return;
+
+ final DisplayContent dc = mAtmService.mWindowManager.mRoot.getDisplayContent(
+ getDisplayId());
+ if (dc == null) {
+ Slog.w(TAG, "removeWindowContainer: Attempted to remove token: "
+ + appToken + " from non-existing displayId=" + getDisplayId());
return;
}
-
// Resume key dispatching if it is currently paused before we remove the container.
resumeKeyDispatchingLocked();
-
- mWindowContainerController.removeContainer(getDisplayId());
- mWindowContainerController = null;
+ dc.removeAppToken(appToken.asBinder());
}
/**
@@ -1044,6 +1124,10 @@
* should ensure that the {@param newTask} is not already the parent of this activity.
*/
void reparent(TaskRecord newTask, int position, String reason) {
+ if (mAppWindowToken == null) {
+ Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
+ return;
+ }
final TaskRecord prevTask = task;
if (prevTask == newTask) {
throw new IllegalArgumentException(reason + ": task=" + newTask
@@ -1059,8 +1143,7 @@
+ " r=" + this + " (" + prevTask.getStackId() + ")");
}
- // Must reparent first in window manager
- mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
+ mAppWindowToken.reparent(newTask.getWindowContainerController(), position);
// Reparenting prevents informing the parent stack of activity removal in the case that
// the new stack has the same parent. we must manually signal here if this is not the case.
@@ -1110,7 +1193,7 @@
*/
private boolean canLaunchAssistActivity(String packageName) {
final ComponentName assistComponent =
- service.mActiveVoiceInteractionServiceComponent;
+ mAtmService.mActiveVoiceInteractionServiceComponent;
if (assistComponent != null) {
return assistComponent.getPackageName().equals(packageName);
}
@@ -1130,8 +1213,8 @@
// We only allow home activities to be resizeable if they explicitly requested it.
info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
- } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
- || service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+ } else if (mActivityComponent.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
+ || mAtmService.getRecentTasks().isRecentsComponent(mActivityComponent, appInfo.uid)) {
activityType = ACTIVITY_TYPE_RECENTS;
} else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
&& canLaunchAssistActivity(launchedFromPackage)) {
@@ -1149,16 +1232,16 @@
/**
* @return Stack value from current task, null if there is no task.
*/
- <T extends ActivityStack> T getStack() {
+ <T extends ActivityStack> T getActivityStack() {
return task != null ? (T) task.getStack() : null;
}
int getStackId() {
- return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
+ return getActivityStack() != null ? getActivityStack().mStackId : INVALID_STACK_ID;
}
ActivityDisplay getDisplay() {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
return stack != null ? stack.getDisplay() : null;
}
@@ -1189,7 +1272,7 @@
}
boolean isInStackLocked() {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
return stack != null && stack.isInStackLocked(this) != null;
}
@@ -1200,7 +1283,7 @@
}
boolean isFocusable() {
- return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
+ return mRootActivityContainer.isFocusable(this, isAlwaysFocusable());
}
boolean isResizeable() {
@@ -1219,7 +1302,7 @@
* @return whether this activity supports PiP multi-window and can be put in the pinned stack.
*/
boolean supportsPictureInPicture() {
- return service.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
+ return mAtmService.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
&& info.supportsPictureInPicture();
}
@@ -1232,7 +1315,7 @@
// An activity can not be docked even if it is considered resizeable because it only
// supports picture-in-picture mode but has a non-resizeable resizeMode
return super.supportsSplitScreenWindowingMode()
- && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+ && mAtmService.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
}
/**
@@ -1240,16 +1323,16 @@
* stack.
*/
boolean supportsFreeform() {
- return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
+ return mAtmService.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
}
/**
* @return whether this activity supports non-PiP multi-window.
*/
private boolean supportsResizeableMultiWindow() {
- return service.mSupportsMultiWindow && !isActivityTypeHome()
+ return mAtmService.mSupportsMultiWindow && !isActivityTypeHome()
&& (ActivityInfo.isResizeableMode(info.resizeMode)
- || service.mForceResizableActivities);
+ || mAtmService.mForceResizableActivities);
}
/**
@@ -1260,7 +1343,7 @@
* secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
- return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+ return mAtmService.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
launchedFromUid, info);
}
@@ -1281,13 +1364,13 @@
}
// Check to see if we are in VR mode, and disallow PiP if so
- if (service.shouldDisableNonVrUiLocked()) {
+ if (mAtmService.shouldDisableNonVrUiLocked()) {
return false;
}
- boolean isKeyguardLocked = service.isKeyguardLocked();
+ boolean isKeyguardLocked = mAtmService.isKeyguardLocked();
boolean isCurrentAppLocked =
- service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ mAtmService.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
final ActivityDisplay display = getDisplay();
boolean hasPinnedStack = display != null && display.hasPinnedStack();
// Don't return early if !isNotLocked, since we want to throw an exception if the activity
@@ -1328,7 +1411,7 @@
* @return Whether AppOps allows this package to enter picture-in-picture.
*/
private boolean checkEnterPictureInPictureAppOpsState() {
- return service.getAppOpsService().checkOperation(
+ return mAtmService.getAppOpsService().checkOperation(
OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
}
@@ -1345,15 +1428,15 @@
return false;
}
- final TaskRecord task = getTask();
- final ActivityStack stack = getStack();
+ final TaskRecord task = getTaskRecord();
+ final ActivityStack stack = getActivityStack();
if (stack == null) {
Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: activity="
+ this + " task=" + task);
return false;
}
- if (mStackSupervisor.getTopResumedActivity() == this) {
+ if (mRootActivityContainer.getTopResumedActivity() == this) {
if (DEBUG_FOCUS) {
Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
}
@@ -1366,9 +1449,9 @@
stack.moveToFront(reason, task);
// Report top activity change to tracking services and WM
- if (mStackSupervisor.getTopResumedActivity() == this) {
+ if (mRootActivityContainer.getTopResumedActivity() == this) {
// TODO(b/111361570): Support multiple focused apps in WM
- service.setResumedActivityUncheckLocked(this, reason);
+ mAtmService.setResumedActivityUncheckLocked(this, reason);
}
return true;
}
@@ -1378,7 +1461,7 @@
* {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
*/
boolean hasDismissKeyguardWindows() {
- return service.mWindowManager.containsDismissKeyguardWindow(appToken);
+ return mAtmService.mWindowManager.containsDismissKeyguardWindow(appToken);
}
void makeFinishingLocked() {
@@ -1390,14 +1473,14 @@
clearOptionsLocked();
}
- if (service != null) {
- service.getTaskChangeNotificationController().notifyTaskStackChanged();
+ if (mAtmService != null) {
+ mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
}
UriPermissionOwner getUriPermissionsLocked() {
if (uriPermissions == null) {
- uriPermissions = new UriPermissionOwner(service.mUgmInternal, this);
+ uriPermissions = new UriPermissionOwner(mAtmService.mUgmInternal, this);
}
return uriPermissions;
}
@@ -1439,8 +1522,8 @@
}
final boolean isSleeping() {
- final ActivityStack stack = getStack();
- return stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked();
+ final ActivityStack stack = getActivityStack();
+ return stack != null ? stack.shouldSleepActivities() : mAtmService.isSleepingLocked();
}
/**
@@ -1449,8 +1532,8 @@
*/
final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
// The activity now gets access to the data associated with this Intent.
- service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
- intent, getUriPermissionsLocked(), userId);
+ mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
+ intent, getUriPermissionsLocked(), mUserId);
final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
boolean unsent = true;
final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
@@ -1464,7 +1547,7 @@
try {
ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
ar.add(rintent);
- service.getLifecycleManager().scheduleTransaction(
+ mAtmService.getLifecycleManager().scheduleTransaction(
app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED));
unsent = false;
} catch (RemoteException e) {
@@ -1490,7 +1573,7 @@
void applyOptionsLocked() {
if (pendingOptions != null
&& pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
- mWindowContainerController.applyOptionsLocked(pendingOptions, intent);
+ applyOptionsLocked(pendingOptions, intent);
if (task == null) {
clearOptionsLocked(false /* withAbort */);
} else {
@@ -1500,6 +1583,104 @@
}
}
+ /**
+ * Apply override app transition base on options & animation type.
+ */
+ void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
+ final int animationType = pendingOptions.getAnimationType();
+ final DisplayContent displayContent = mAppWindowToken.getDisplayContent();
+ switch (animationType) {
+ case ANIM_CUSTOM:
+ displayContent.mAppTransition.overridePendingAppTransition(
+ pendingOptions.getPackageName(),
+ pendingOptions.getCustomEnterResId(),
+ pendingOptions.getCustomExitResId(),
+ pendingOptions.getOnAnimationStartListener());
+ break;
+ case ANIM_CLIP_REVEAL:
+ displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
+ }
+ break;
+ case ANIM_SCALE_UP:
+ displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
+ }
+ break;
+ case ANIM_THUMBNAIL_SCALE_UP:
+ case ANIM_THUMBNAIL_SCALE_DOWN:
+ final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
+ final GraphicBuffer buffer = pendingOptions.getThumbnail();
+ displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getOnAnimationStartListener(),
+ scaleUp);
+ if (intent.getSourceBounds() == null && buffer != null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + buffer.getWidth(),
+ pendingOptions.getStartY() + buffer.getHeight()));
+ }
+ break;
+ case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
+ case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
+ final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
+ final IAppTransitionAnimationSpecsFuture specsFuture =
+ pendingOptions.getSpecsFuture();
+ if (specsFuture != null) {
+ // TODO(multidisplay): Shouldn't be really used anymore from next CL.
+ displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
+ specsFuture, pendingOptions.getOnAnimationStartListener(),
+ animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
+ } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
+ && specs != null) {
+ displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
+ specs, pendingOptions.getOnAnimationStartListener(),
+ pendingOptions.getAnimationFinishedListener(), false);
+ } else {
+ displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
+ pendingOptions.getThumbnail(),
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight(),
+ pendingOptions.getOnAnimationStartListener(),
+ (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
+ }
+ }
+ break;
+ case ANIM_OPEN_CROSS_PROFILE_APPS:
+ displayContent.mAppTransition
+ .overridePendingAppTransitionStartCrossProfileApps();
+ break;
+ case ANIM_REMOTE_ANIMATION:
+ // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
+ displayContent.mAppTransition.overridePendingAppTransitionRemote(
+ pendingOptions.getRemoteAnimationAdapter());
+ break;
+ case ANIM_NONE:
+ break;
+ default:
+ Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
+ break;
+ }
+ }
+
ActivityOptions getOptionsForTargetActivityLocked() {
return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
}
@@ -1532,8 +1713,11 @@
if (!keysPaused) {
keysPaused = true;
- if (mWindowContainerController != null) {
- mWindowContainerController.pauseKeyDispatching();
+ // TODO: remove the check after unification with AppWindowToken. The DC check is not
+ // needed after no mock mAppWindowToken in tests.
+ if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+ mAppWindowToken.getDisplayContent().getInputMonitor().pauseDispatchingLw(
+ mAppWindowToken);
}
}
}
@@ -1542,8 +1726,11 @@
if (keysPaused) {
keysPaused = false;
- if (mWindowContainerController != null) {
- mWindowContainerController.resumeKeyDispatching();
+ // TODO: remove the check after unification with AppWindowToken. The DC check is not
+ // needed after no mock mAppWindowToken in tests.
+ if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+ mAppWindowToken.getDisplayContent().getInputMonitor().resumeDispatchingLw(
+ mAppWindowToken);
}
}
}
@@ -1565,11 +1752,16 @@
}
void setVisibility(boolean visible) {
- mWindowContainerController.setVisibility(visible, mDeferHidingClient);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
+ + appToken);
+ return;
+ }
+ mAppWindowToken.setVisibility(visible, mDeferHidingClient);
mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
}
- // TODO: Look into merging with #setVisibility()
+ // TODO: Look into merging with #commitVisibility()
void setVisible(boolean newVisible) {
visible = newVisible;
mDeferHidingClient = !visible && mDeferHidingClient;
@@ -1589,7 +1781,7 @@
mState = state;
- final TaskRecord parent = getTask();
+ final TaskRecord parent = getTaskRecord();
if (parent != null) {
parent.onActivityStateChanged(this, state, reason);
@@ -1599,7 +1791,12 @@
// an indication that the Surface will eventually be destroyed.
// This however isn't necessarily true if we are going to sleep.
if (state == STOPPING && !isSleeping()) {
- mWindowContainerController.notifyAppStopping();
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
+ + appToken);
+ return;
+ }
+ mAppWindowToken.detachChildren();
}
}
@@ -1637,7 +1834,12 @@
}
void notifyAppResumed(boolean wasStopped) {
- mWindowContainerController.notifyAppResumed(wasStopped);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: "
+ + appToken);
+ return;
+ }
+ mAppWindowToken.notifyAppResumed(wasStopped);
}
void notifyUnknownVisibilityLaunched() {
@@ -1645,7 +1847,10 @@
// No display activities never add a window, so there is no point in waiting them for
// relayout.
if (!noDisplay) {
- mWindowContainerController.notifyUnknownVisibilityLaunched();
+ if (mAppWindowToken != null) {
+ mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
+ .notifyLaunched(mAppWindowToken);
+ }
}
}
@@ -1674,7 +1879,7 @@
// If this activity is paused, tell it to now show its window.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Making visible and scheduling visibility: " + this);
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
try {
if (stack.mTranslucentActivityWaiting != null) {
updateOptionsLocked(returningOptions);
@@ -1702,13 +1907,13 @@
void makeClientVisible() {
mClientVisibilityDeferred = false;
try {
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
WindowVisibilityItem.obtain(true /* showWindow */));
if (shouldPauseWhenBecomingVisible()) {
// An activity must be in the {@link PAUSING} state for the system to validate
// the move to {@link PAUSED}.
setState(PAUSING, "makeVisibleIfNeeded");
- service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PauseActivityItem.obtain(finishing, false /* userLeaving */,
configChangeFlags, false /* dontReport */));
}
@@ -1726,7 +1931,7 @@
// paused state. We also avoid doing this for the activity the stack supervisor
// considers the resumed activity, as normal means will bring the activity from STOPPED
// to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
- if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
+ if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null
|| isResumedActivityOnDisplay()) {
return false;
}
@@ -1789,8 +1994,8 @@
if (isActivityTypeHome()) {
WindowProcessController app = task.mActivities.get(0).app;
- if (hasProcess() && app != service.mHomeProcess) {
- service.mHomeProcess = app;
+ if (hasProcess() && app != mAtmService.mHomeProcess) {
+ mAtmService.mHomeProcess = app;
}
}
@@ -1805,7 +2010,7 @@
mStackSupervisor.reportResumedActivityLocked(this);
resumeKeyDispatchingLocked();
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
mStackSupervisor.mNoAnimActivities.clear();
// Mark the point when the activity is resuming
@@ -1831,7 +2036,7 @@
final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
CharSequence description) {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (mState != STOPPING) {
Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
@@ -1839,7 +2044,7 @@
}
if (newPersistentState != null) {
persistentState = newPersistentState;
- service.notifyTaskPersisterLocked(task, false);
+ mAtmService.notifyTaskPersisterLocked(task, false);
}
if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + icicle);
@@ -1857,16 +2062,18 @@
stopped = true;
setState(STOPPED, "activityStoppedLocked");
- mWindowContainerController.notifyAppStopped();
+ if (mAppWindowToken != null) {
+ mAppWindowToken.notifyAppStopped();
+ }
if (finishing) {
clearOptionsLocked();
} else {
if (deferRelaunchUntilPaused) {
stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
} else {
- mStackSupervisor.updatePreviousProcessLocked(this);
+ mRootActivityContainer.updatePreviousProcess(this);
}
}
}
@@ -1887,7 +2094,7 @@
return false;
}
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack == null) {
return false;
}
@@ -1900,7 +2107,7 @@
void finishLaunchTickingLocked() {
launchTickTime = 0;
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack != null) {
stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
}
@@ -1918,14 +2125,33 @@
public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
if (mayFreezeScreenLocked(app)) {
- mWindowContainerController.startFreezingScreen(configChanges);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM,
+ "Attempted to freeze screen with non-existing app token: " + appToken);
+ return;
+ }
+
+ if (configChanges == 0 && mAppWindowToken.okToDisplay()) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken);
+ return;
+ }
+
+ mAppWindowToken.startFreezingScreen();
}
}
public void stopFreezingScreenLocked(boolean force) {
if (force || frozenBeforeDestroy) {
frozenBeforeDestroy = false;
- mWindowContainerController.stopFreezingScreen(force);
+ if (mAppWindowToken == null) {
+ return;
+ }
+ if (DEBUG_ORIENTATION) {
+ Slog.v(TAG_WM, "Clear freezing of " + appToken + ": hidden="
+ + mAppWindowToken.isHidden() + " freezing="
+ + mAppWindowToken.isFreezingScreen());
+ }
+ mAppWindowToken.stopFreezingScreen(true, force);
}
}
@@ -1937,17 +2163,20 @@
info.windowsFullyDrawnDelayMs);
}
}
- @Override
+
+ /**
+ * Called when the starting window for this container is drawn.
+ */
public void onStartingWindowDrawn(long timestamp) {
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
getWindowingMode(), timestamp);
}
}
- @Override
+ /** Called when the windows associated app window container are drawn. */
public void onWindowsDrawn(boolean drawn, long timestamp) {
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
mDrawn = drawn;
if (!drawn) {
return;
@@ -1965,9 +2194,9 @@
}
}
- @Override
+ /** Called when the windows associated app window container are visible. */
public void onWindowsVisible() {
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
mStackSupervisor.reportActivityVisibleLocked(this);
if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
if (!nowVisible) {
@@ -1994,25 +2223,32 @@
mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
false /* remove */, true /* processPausingActivities */);
}
- service.scheduleAppGcsLocked();
+ mAtmService.scheduleAppGcsLocked();
}
}
}
- @Override
+ /** Called when the windows associated app window container are no longer visible. */
public void onWindowsGone() {
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
nowVisible = false;
}
}
- @Override
+ /**
+ * Called when the key dispatching to a window associated with the app window container
+ * timed-out.
+ *
+ * @param reason The reason for the key dispatching time out.
+ * @param windowPid The pid of the window key dispatching timed out on.
+ * @return True if input dispatching should be aborted.
+ */
public boolean keyDispatchingTimedOut(String reason, int windowPid) {
ActivityRecord anrActivity;
WindowProcessController anrApp;
boolean windowFromSameProcessAsActivity;
- synchronized (service.mGlobalLock) {
+ synchronized (mAtmService.mGlobalLock) {
anrActivity = getWaitingHistoryRecordLocked();
anrApp = app;
windowFromSameProcessAsActivity =
@@ -2020,13 +2256,13 @@
}
if (windowFromSameProcessAsActivity) {
- return service.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
+ return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
anrActivity.shortComponentName, anrActivity.appInfo, shortComponentName,
app, false, reason);
} else {
// In this case another process added windows using this activity token. So, we call the
// generic service input dispatch timed out method so that the right process is blamed.
- return service.mAmInternal.inputDispatchingTimedOut(
+ return mAtmService.mAmInternal.inputDispatchingTimedOut(
windowPid, false /* aboveSystem */, reason) < 0;
}
}
@@ -2036,7 +2272,7 @@
// another activity to start or has stopped, then the key dispatching
// timeout should not be caused by this.
if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
- final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
// Try to use the one which is closest to top.
ActivityRecord r = stack.getResumedActivity();
if (r == null) {
@@ -2053,14 +2289,14 @@
public boolean okToShowLocked() {
// We cannot show activities when the device is locked and the application is not
// encryption aware.
- if (!StorageManager.isUserKeyUnlocked(userId)
+ if (!StorageManager.isUserKeyUnlocked(mUserId)
&& !info.applicationInfo.isEncryptionAware()) {
return false;
}
return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
- || (mStackSupervisor.isCurrentProfileLocked(userId)
- && service.mAmInternal.isUserRunning(userId, 0 /* flags */));
+ || (mStackSupervisor.isCurrentProfileLocked(mUserId)
+ && mAtmService.mAmInternal.isUserRunning(mUserId, 0 /* flags */));
}
/**
@@ -2107,13 +2343,13 @@
static ActivityRecord isInStackLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- return (r != null) ? r.getStack().isInStackLocked(r) : null;
+ return (r != null) ? r.getActivityStack().isInStackLocked(r) : null;
}
static ActivityStack getStackLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- return r.getStack();
+ return r.getActivityStack();
}
return null;
}
@@ -2123,7 +2359,7 @@
* {@link android.view.Display#INVALID_DISPLAY} if not attached.
*/
int getDisplayId() {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack == null) {
return INVALID_DISPLAY;
}
@@ -2135,7 +2371,7 @@
// This would be redundant.
return false;
}
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack == null || this == stack.getResumedActivity() || this == stack.mPausingActivity
|| !haveState || !stopped) {
// We're not ready for this kind of thing.
@@ -2161,7 +2397,7 @@
final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId),
iconFilename);
final String iconFilePath = iconFile.getAbsolutePath();
- service.getRecentTasks().saveImage(icon, iconFilePath);
+ mAtmService.getRecentTasks().saveImage(icon, iconFilePath);
_taskDescription.setIconFilename(iconFilePath);
}
taskDescription = _taskDescription;
@@ -2183,7 +2419,7 @@
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
boolean fromRecents) {
- if (mWindowContainerController == null) {
+ if (mAppWindowToken == null) {
return;
}
if (mTaskOverlay) {
@@ -2197,8 +2433,8 @@
}
final CompatibilityInfo compatInfo =
- service.compatibilityInfoForPackageLocked(info.applicationInfo);
- final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
+ mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);
+ final boolean shown = addStartingWindow(packageName, theme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
allowTaskSnapshot(),
@@ -2213,34 +2449,62 @@
if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
mStartingWindowState = STARTING_WINDOW_REMOVED;
- mWindowContainerController.removeStartingWindow();
+ mAppWindowToken.removeStartingWindow();
}
}
int getRequestedOrientation() {
- return mWindowContainerController.getOrientation();
+ return getOrientation();
}
void setRequestedOrientation(int requestedOrientation) {
final int displayId = getDisplayId();
final Configuration displayConfig =
- mStackSupervisor.getDisplayOverrideConfiguration(displayId);
+ mRootActivityContainer.getDisplayOverrideConfiguration(displayId);
- final Configuration config = mWindowContainerController.setOrientation(requestedOrientation,
+ final Configuration config = setOrientation(requestedOrientation,
displayId, displayConfig, mayFreezeScreenLocked(app));
if (config != null) {
frozenBeforeDestroy = true;
- if (!service.updateDisplayOverrideConfigurationLocked(config, this,
+ if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this,
false /* deferResume */, displayId)) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
- service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
+ mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
task.taskId, requestedOrientation);
}
+ Configuration setOrientation(int requestedOrientation, int displayId,
+ Configuration displayConfig, boolean freezeScreenIfNeeded) {
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM,
+ "Attempted to set orientation of non-existing app token: " + appToken);
+ return null;
+ }
+
+ mAppWindowToken.setOrientation(requestedOrientation);
+
+ final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
+ return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
+ displayId);
+ }
+
+ int getOrientation() {
+ if (mAppWindowToken == null) {
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ return mAppWindowToken.getOrientationIgnoreVisibility();
+ }
+
void setDisablePreviewScreenshots(boolean disable) {
- mWindowContainerController.setDisablePreviewScreenshots(disable);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
+ + " token: " + appToken);
+ return;
+ }
+ mAppWindowToken.setDisablePreviewScreenshots(disable);
}
/**
@@ -2279,7 +2543,7 @@
// Bounds changed...update configuration to match.
if (!matchParentBounds()) {
- task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */,
+ task.computeOverrideConfiguration(mTmpConfig, updatedBounds,
false /* overrideWidth */, false /* overrideHeight */);
}
@@ -2288,8 +2552,8 @@
/** Returns true if the configuration is compatible with this activity. */
boolean isConfigurationCompatible(Configuration config) {
- final int orientation = mWindowContainerController != null
- ? mWindowContainerController.getOrientation() : info.screenOrientation;
+ final int orientation = mAppWindowToken != null
+ ? getOrientation() : info.screenOrientation;
if (isFixedOrientationPortrait(orientation)
&& config.orientation != ORIENTATION_PORTRAIT) {
return false;
@@ -2308,7 +2572,7 @@
private void computeBounds(Rect outBounds) {
outBounds.setEmpty();
final float maxAspectRatio = info.maxAspectRatio;
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0
|| isInVrUiMode(getConfiguration())) {
// We don't set override configuration if that activity task isn't fullscreen. I.e. the
@@ -2354,7 +2618,7 @@
// bounds would end up too small.
outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
- if (service.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
+ if (mAtmService.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
// Position the activity frame on the opposite side of the nav bar.
outBounds.left = appBounds.right - maxActivityWidth;
outBounds.right = appBounds.right;
@@ -2390,7 +2654,7 @@
*/
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreStopState) {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack.mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check (will change): " + this);
@@ -2453,7 +2717,7 @@
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
+ setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig);
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
@@ -2507,7 +2771,7 @@
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
if (hasResizeChange) {
final boolean isDragResizing =
- getTask().getWindowContainerController().isDragResizing();
+ getTaskRecord().getWindowContainerController().isDragResizing();
mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
: RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
} else {
@@ -2640,7 +2904,7 @@
}
void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
- if (service.mSuppressResizeConfigChanges && preserveWindow) {
+ if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) {
configChangeFlags = 0;
return;
}
@@ -2656,7 +2920,7 @@
+ " newIntents=" + pendingNewIntents + " andResume=" + andResume
+ " preserveWindow=" + preserveWindow);
EventLog.writeEvent(andResume ? AM_RELAUNCH_RESUME_ACTIVITY
- : AM_RELAUNCH_ACTIVITY, userId, System.identityHashCode(this),
+ : AM_RELAUNCH_ACTIVITY, mUserId, System.identityHashCode(this),
task.taskId, shortComponentName);
startFreezingScreenLocked(app, 0);
@@ -2669,7 +2933,7 @@
mStackSupervisor.activityRelaunchingLocked(this);
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
- new MergedConfiguration(service.getGlobalConfiguration(),
+ new MergedConfiguration(mAtmService.getGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
@@ -2682,7 +2946,7 @@
final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
- service.getLifecycleManager().scheduleTransaction(transaction);
+ mAtmService.getLifecycleManager().scheduleTransaction(transaction);
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
// request resume if this activity is currently resumed, which implies we aren't
// sleeping.
@@ -2696,9 +2960,9 @@
}
results = null;
newIntents = null;
- service.getAppWarningsLocked().onResumeActivity(this);
+ mAtmService.getAppWarningsLocked().onResumeActivity(this);
} else {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
if (stack != null) {
stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
}
@@ -2713,7 +2977,7 @@
private boolean isProcessRunning() {
WindowProcessController proc = app;
if (proc == null) {
- proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
+ proc = mAtmService.mProcessNames.get(processName, info.applicationInfo.uid);
}
return proc != null && proc.hasThread();
}
@@ -2757,7 +3021,7 @@
out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
}
out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
- out.attribute(null, ATTR_USERID, String.valueOf(userId));
+ out.attribute(null, ATTR_USERID, String.valueOf(mUserId));
if (taskDescription != null) {
taskDescription.saveToXml(out);
@@ -2867,7 +3131,7 @@
void setShowWhenLocked(boolean showWhenLocked) {
mShowWhenLocked = showWhenLocked;
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */,
+ mRootActivityContainer.ensureActivitiesVisible(null, 0 /* configChanges */,
false /* preserveWindows */);
}
@@ -2880,7 +3144,7 @@
*/
boolean canShowWhenLocked() {
return !inPinnedWindowingMode() && (mShowWhenLocked
- || service.mWindowManager.containsShowWhenLockedWindow(appToken));
+ || mAtmService.mWindowManager.containsShowWhenLockedWindow(appToken));
}
void setTurnScreenOn(boolean turnScreenOn) {
@@ -2895,7 +3159,7 @@
* @return true if the screen can be turned on, false otherwise.
*/
boolean canTurnScreenOn() {
- final ActivityStack stack = getStack();
+ final ActivityStack stack = getActivityStack();
return mTurnScreenOn && stack != null &&
stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
}
@@ -2905,7 +3169,7 @@
}
boolean isTopRunningActivity() {
- return mStackSupervisor.topRunningActivityLocked() == this;
+ return mRootActivityContainer.topRunningActivity() == this;
}
/**
@@ -2918,7 +3182,12 @@
}
void registerRemoteAnimations(RemoteAnimationDefinition definition) {
- mWindowContainerController.registerRemoteAnimations(definition);
+ if (mAppWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
+ + " token: " + appToken);
+ return;
+ }
+ mAppWindowToken.registerRemoteAnimations(definition);
}
@Override
@@ -2931,7 +3200,7 @@
sb.append("ActivityRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" u");
- sb.append(userId);
+ sb.append(mUserId);
sb.append(' ');
sb.append(intent.getComponent().flattenToShortString());
stringName = sb.toString();
@@ -2941,13 +3210,16 @@
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
- proto.write(USER_ID, userId);
+ proto.write(USER_ID, mUserId);
proto.write(TITLE, intent.getComponent().flattenToShortString());
proto.end(token);
}
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
+ /**
+ * Write all fields to an {@code ActivityRecordProto}. This assumes the
+ * {@code ActivityRecordProto} is the outer-most proto data.
+ */
+ void writeToProto(ProtoOutputStream proto) {
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
writeIdentifierToProto(proto, IDENTIFIER);
proto.write(STATE, mState.toString());
@@ -2957,6 +3229,11 @@
proto.write(PROC_ID, app.getPid());
}
proto.write(TRANSLUCENT, !fullscreen);
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ writeToProto(proto);
proto.end(token);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bd3e43c..3ccede0 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -63,7 +63,6 @@
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -103,6 +102,7 @@
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.RootActivityContainer.FindTaskResult;
import static java.lang.Integer.MAX_VALUE;
@@ -267,7 +267,7 @@
mStackSupervisor.resizeDockedStackLocked(
getOverrideBounds(), mTmpRect2, mTmpRect2, null, null, PRESERVE_WINDOWS);
}
- mStackSupervisor.updateUIDsPresentOnDisplay();
+ mRootActivityContainer.updateUIDsPresentOnDisplay();
}
enum ActivityState {
@@ -390,6 +390,7 @@
/** Run all ActivityStacks through this */
protected final ActivityStackSupervisor mStackSupervisor;
+ protected final RootActivityContainer mRootActivityContainer;
private boolean mTopActivityOccludesKeyguard;
private ActivityRecord mTopDismissingKeyguardActivity;
@@ -489,6 +490,7 @@
int windowingMode, int activityType, boolean onTop) {
mStackSupervisor = supervisor;
mService = supervisor.mService;
+ mRootActivityContainer = mService.mRootActivityContainer;
mHandler = new ActivityStackHandler(supervisor.mLooper);
mWindowManager = mService.mWindowManager;
mStackId = stackId;
@@ -508,7 +510,7 @@
T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
- mStackSupervisor.mWindowManager);
+ mRootActivityContainer.mWindowManager);
}
T getWindowContainerController() {
@@ -532,11 +534,11 @@
if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
+ reason);
setResumedActivity(record, reason + " - onActivityStateChanged");
- if (record == mStackSupervisor.getTopResumedActivity()) {
+ if (record == mRootActivityContainer.getTopResumedActivity()) {
// TODO(b/111361570): Support multiple focused apps in WM
mService.setResumedActivityUncheckLocked(record, reason);
}
- mStackSupervisor.mRecentTasks.add(record.getTask());
+ mStackSupervisor.mRecentTasks.add(record.getTaskRecord());
}
}
@@ -622,7 +624,7 @@
display.onStackWindowingModeChanged(this);
}
if (hasNewOverrideBounds) {
- mStackSupervisor.resizeStackLocked(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
+ mRootActivityContainer.resizeStack(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, true /* deferResume */);
}
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -819,8 +821,8 @@
}
if (!deferEnsuringVisibility) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
@@ -854,10 +856,10 @@
/** Resume next focusable stack after reparenting to another display. */
void postReparent() {
adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
// Update visibility of activities before notifying WM. This way it won't try to resize
// windows that are no longer visible.
- mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+ mRootActivityContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
!PRESERVE_WINDOWS);
}
@@ -882,7 +884,7 @@
}
ActivityDisplay getDisplay() {
- return mStackSupervisor.getActivityDisplay(mDisplayId);
+ return mRootActivityContainer.getActivityDisplay(mDisplayId);
}
/**
@@ -1034,7 +1036,7 @@
}
/**
- * This is a simplified version of topRunningActivityLocked that provides a number of
+ * This is a simplified version of topRunningActivity that provides a number of
* optional skip-over modes. It is intended for use with the ActivityController hook only.
*
* @param token If non-null, any history records matching this token will be skipped.
@@ -1104,8 +1106,8 @@
if (r == null) {
return null;
}
- final TaskRecord task = r.getTask();
- final ActivityStack stack = r.getStack();
+ final TaskRecord task = r.getTaskRecord();
+ final ActivityStack stack = r.getActivityStack();
if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
if (stack != this) Slog.w(TAG,
"Illegal state! task does not point to stack it is in.");
@@ -1236,7 +1238,7 @@
boolean isFocusable() {
final ActivityRecord r = topRunningActivityLocked();
- return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
+ return mRootActivityContainer.isFocusable(this, r != null && r.isFocusable());
}
boolean isFocusableAndVisible() {
@@ -1280,7 +1282,7 @@
// Overlays should not be considered as the task's logical top activity.
final ActivityRecord r = task.getTopActivity(false /* includeOverlays */);
- if (r == null || r.finishing || r.userId != userId ||
+ if (r == null || r.finishing || r.mUserId != userId ||
r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
continue;
@@ -1307,7 +1309,7 @@
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
+ taskIntent.getComponent().flattenToShortString()
- + "/aff=" + r.getTask().rootAffinity + " to new cls="
+ + "/aff=" + r.getTaskRecord().rootAffinity + " to new cls="
+ intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
// TODO Refactor to remove duplications. Check if logic can be simplified.
if (taskIntent != null && taskIntent.getComponent() != null &&
@@ -1366,7 +1368,7 @@
if (!r.okToShowLocked()) {
continue;
}
- if (!r.finishing && r.userId == userId) {
+ if (!r.finishing && r.mUserId == userId) {
if (compareIntentFilters) {
if (r.intent.filterEquals(intent)) {
return r;
@@ -1398,7 +1400,7 @@
final TaskRecord task = mTaskHistory.get(i);
if (task.okToShowLocked()) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUser: stack=" + getStackId() +
" moving " + task + " to top");
mTaskHistory.remove(i);
mTaskHistory.add(task);
@@ -1450,7 +1452,7 @@
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord ar = activities.get(activityNdx);
- if ((userId == ar.userId) && packageName.equals(ar.packageName)) {
+ if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) {
ar.updateApplicationInfo(aInfo);
}
}
@@ -1536,7 +1538,7 @@
private boolean containsActivityFromStack(List<ActivityRecord> rs) {
for (ActivityRecord r : rs) {
- if (r.getStack() == this) {
+ if (r.getActivityStack() == this) {
return true;
}
}
@@ -1587,7 +1589,7 @@
if (prev == null) {
if (resuming == null) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return false;
}
@@ -1604,7 +1606,7 @@
mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
prev.setState(PAUSING, "startPausingLocked");
- prev.getTask().touchActiveTime();
+ prev.getTaskRecord().touchActiveTime();
clearLaunchTime(prev);
mService.updateCpuStats();
@@ -1612,7 +1614,7 @@
if (prev.attachedToProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
- EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
+ EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
mService.updateUsageStats(prev, false);
@@ -1665,7 +1667,7 @@
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
if (resuming == null) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return false;
}
@@ -1690,7 +1692,7 @@
return;
} else {
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
- r.userId, System.identityHashCode(r), r.shortComponentName,
+ r.mUserId, System.identityHashCode(r), r.shortComponentName,
mPausingActivity != null
? mPausingActivity.shortComponentName : "(none)");
if (r.isState(PAUSING)) {
@@ -1704,7 +1706,7 @@
}
}
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
@@ -1757,9 +1759,9 @@
}
if (resumeNext) {
- final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
if (!topStack.shouldSleepOrShutDownActivities()) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null);
+ mRootActivityContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
} else {
checkReadyForSleep();
ActivityRecord top = topStack.topRunningActivityLocked();
@@ -1768,7 +1770,7 @@
// something. Also if the top activity on the stack is not the just paused
// activity, we need to go ahead and resume it to ensure we complete an
// in-flight app switch.
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
}
@@ -1799,7 +1801,7 @@
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
}
- mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
}
private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
@@ -2011,7 +2013,7 @@
/**
* Ensure visibility with an option to also update the configuration of visible activities.
* @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
- * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+ * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
*/
// TODO: Should be re-worked based on the fact that each task as a stack in most cases.
final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
@@ -2032,7 +2034,7 @@
boolean aboveTop = top != null;
final boolean stackShouldBeVisible = shouldBeVisible(starting);
boolean behindFullscreenActivity = !stackShouldBeVisible;
- boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
+ boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this)
&& (isInStackLocked(starting) == null);
final boolean isTopNotPinnedStack =
isAttached() && getDisplay().isTopNotPinnedStack(this);
@@ -2443,7 +2445,7 @@
*
* NOTE: It is not safe to call this method directly as it can cause an activity in a
* non-focused stack to be resumed.
- * Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the
+ * Use {@link RootActivityContainer#resumeFocusedStacksTopActivities} to resume the
* right activity for the current system state.
*/
@GuardedBy("mService")
@@ -2513,7 +2515,7 @@
return false;
}
- mStackSupervisor.cancelInitializingActivities();
+ mRootActivityContainer.cancelInitializingActivities();
// Remember how we'll process this pause/resume situation, and ensure
// that the state is reset however we wind up proceeding.
@@ -2536,7 +2538,6 @@
executeAppTransition(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Top activity resumed " + next);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -2544,7 +2545,7 @@
// activity is paused, well that is the state we want.
if (shouldSleepOrShutDownActivities()
&& mLastPausedActivity == next
- && mStackSupervisor.allPausedActivitiesComplete()) {
+ && mRootActivityContainer.allPausedActivitiesComplete()) {
// If the current top activity may be able to occlude keyguard but the occluded state
// has not been set, update visibility and check again if we should continue to resume.
boolean nothingToResume = true;
@@ -2565,7 +2566,6 @@
executeAppTransition(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Going to sleep and all paused");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
}
@@ -2573,10 +2573,9 @@
// Make sure that the user who owns this activity is started. If not,
// we will just leave it as is because someone should be bringing
// another user's activities to the top of the stack.
- if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
+ if (!mService.mAmInternal.hasStartedUserState(next.mUserId)) {
Slog.w(TAG, "Skipping resume of top activity " + next
- + ": user " + next.userId + " is stopped");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ + ": user " + next.mUserId + " is stopped");
return false;
}
@@ -2590,10 +2589,9 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
// If we are currently pausing an activity, then don't do anything until that is done.
- if (!mStackSupervisor.allPausedActivitiesComplete()) {
+ if (!mRootActivityContainer.allPausedActivitiesComplete()) {
if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
"resumeTopActivityLocked: Skip resume: some activity pausing.");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -2640,7 +2638,6 @@
next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
}
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
if (lastResumed != null) {
lastResumed.setWillCloseOrEnterPip(true);
}
@@ -2655,7 +2652,6 @@
executeAppTransition(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2673,7 +2669,7 @@
if (prev != null && prev != next) {
if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
- && next != null && !next.nowVisible) {
+ && !next.nowVisible) {
mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Resuming top, waiting visible to hide: " + prev);
@@ -2706,7 +2702,7 @@
// considered stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
- next.packageName, false, next.userId); /* TODO: Verify if correct userid */
+ next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
} catch (RemoteException e1) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
@@ -2727,7 +2723,7 @@
dwc.prepareAppTransition(TRANSIT_NONE, false);
} else {
dwc.prepareAppTransition(
- prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE
+ prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_CLOSE
: TRANSIT_TASK_CLOSE, false);
}
prev.setVisibility(false);
@@ -2739,7 +2735,7 @@
dwc.prepareAppTransition(TRANSIT_NONE, false);
} else {
dwc.prepareAppTransition(
- prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN
+ prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_OPEN
: next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
: TRANSIT_TASK_OPEN, false);
}
@@ -2814,7 +2810,7 @@
// result of invisible window resize.
// TODO: Remove this once visibilities are set correctly immediately when
// starting an activity.
- notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+ notUpdated = !mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
true /* markFrozenIfConfigChanged */, false /* deferResume */);
}
@@ -2836,7 +2832,6 @@
next.setVisibility(true);
}
next.completeResumeLocked();
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2863,8 +2858,8 @@
// Clear app token stopped state in window manager if needed.
next.notifyAppResumed(next.stopped);
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
- System.identityHashCode(next), next.getTask().taskId,
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.mUserId,
+ System.identityHashCode(next), next.getTaskRecord().taskId,
next.shortComponentName);
next.sleeping = false;
@@ -2899,7 +2894,6 @@
false /* taskSwitch */);
}
mStackSupervisor.startSpecificActivityLocked(next, true, false);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2913,7 +2907,6 @@
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
"resume-exception", true);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
} else {
@@ -2931,7 +2924,6 @@
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -2942,7 +2934,7 @@
// Try to move focus to the next visible stack with a running activity if this
// stack is not covering the entire screen or is on a secondary display (with no home
// stack).
- return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev,
+ return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev,
null /* targetOptions */);
}
@@ -2950,8 +2942,7 @@
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityInNextFocusableStack: " + reason + ", go home");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
+ return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
}
/** Returns the position the input task should be placed in this stack. */
@@ -3017,7 +3008,7 @@
void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
boolean newTask, boolean keepCurTransition, ActivityOptions options) {
- TaskRecord rTask = r.getTask();
+ TaskRecord rTask = r.getTaskRecord();
final int taskId = rTask.taskId;
// mLaunchTaskBehind tasks get placed at the back of the task stack.
if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
@@ -3043,7 +3034,7 @@
if (!startIt) {
if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
+ task, new RuntimeException("here").fillInStackTrace());
- r.createWindowContainer();
+ r.createAppWindowToken();
ActivityOptions.abort(options);
return;
}
@@ -3058,7 +3049,7 @@
// If we are not placing the new activity frontmost, we do not want to deliver the
// onUserLeaving callback to the actual frontmost activity
- final TaskRecord activityTask = r.getTask();
+ final TaskRecord activityTask = r.getTaskRecord();
if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
mStackSupervisor.mUserLeaving = false;
if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
@@ -3073,9 +3064,10 @@
// TODO: Need to investigate if it is okay for the controller to already be created by the
// time we get to this point. I think it is, but need to double check.
// Use test in b/34179495 to trace the call path.
- if (r.getWindowContainerController() == null) {
- r.createWindowContainer();
+ if (r.mAppWindowToken == null) {
+ r.createAppWindowToken();
}
+
task.setFrontOfTask();
if (!isHomeOrRecentsStack() || numActivities() > 0) {
@@ -3130,12 +3122,12 @@
// "has the same starting icon" as the next one. This allows the
// window manager to keep the previous window it had previously
// created, if it still had one.
- TaskRecord prevTask = r.getTask();
+ TaskRecord prevTask = r.getTaskRecord();
ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();
if (prev != null) {
// We don't want to reuse the previous starting preview if:
// (1) The current activity is in a different task.
- if (prev.getTask() != prevTask) {
+ if (prev.getTaskRecord() != prevTask) {
prev = null;
}
// (2) The current activity is already displayed.
@@ -3168,7 +3160,7 @@
return false;
}
final ActivityStack targetStack = toFrontTask != null
- ? toFrontTask.getStack() : toFrontActivity.getStack();
+ ? toFrontTask.getStack() : toFrontActivity.getActivityStack();
if (targetStack != null && targetStack.isActivityTypeAssistant()) {
// Ensure the task/activity being brought forward is not the assistant
return false;
@@ -3178,7 +3170,7 @@
private boolean isTaskSwitch(ActivityRecord r,
ActivityRecord topFocusedActivity) {
- return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
+ return topFocusedActivity != null && r.getTaskRecord() != topFocusedActivity.getTaskRecord();
}
/**
@@ -3245,16 +3237,16 @@
!mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
mTaskHistory.get(0).mActivities.get(0) : null;
if (bottom != null && target.taskAffinity != null
- && target.taskAffinity.equals(bottom.getTask().affinity)) {
+ && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) {
// If the activity currently at the bottom has the
// same task affinity as the one we are moving,
// then merge it into the same task.
- targetTask = bottom.getTask();
+ targetTask = bottom.getTaskRecord();
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
+ " out to bottom task " + targetTask);
} else {
targetTask = createTaskRecord(
- mStackSupervisor.getNextTaskIdForUserLocked(target.userId),
+ mStackSupervisor.getNextTaskIdForUserLocked(target.mUserId),
target.info, null, null, null, false);
targetTask.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
@@ -3448,7 +3440,7 @@
ActivityRecord newActivity) {
final boolean forceReset =
(newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- final TaskRecord task = taskTop.getTask();
+ final TaskRecord task = taskTop.getTaskRecord();
/** False until we evaluate the TaskRecord associated with taskTop. Switches to true
* for remaining tasks. Used for later tasks to reparent to task. */
@@ -3497,7 +3489,7 @@
if (callingUid > 0) {
mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
- data, r.getUriPermissionsLocked(), r.userId);
+ data, r.getUriPermissionsLocked(), r.mUserId);
}
if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
@@ -3536,7 +3528,7 @@
}
private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
- if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
+ if (!mRootActivityContainer.isTopDisplayFocusedStack(this) ||
((mResumedActivity != r) && (mResumedActivity != null))) {
return;
}
@@ -3545,7 +3537,7 @@
final String myReason = reason + " adjustFocus";
if (next == r) {
- final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
+ final ActivityRecord top = mRootActivityContainer.topRunningActivity();
if (top != null) {
top.moveFocusableActivityToTop(myReason);
}
@@ -3559,7 +3551,7 @@
// Task is not guaranteed to be non-null. For example, destroying the
// {@link ActivityRecord} will disassociate the task from the activity.
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (task == null) {
throw new IllegalStateException("activity no longer associated with task:" + r);
@@ -3569,7 +3561,7 @@
final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason);
if (nextFocusableStack != null) {
final ActivityRecord top = nextFocusableStack.topRunningActivityLocked();
- if (top != null && top == mStackSupervisor.getTopResumedActivity()) {
+ if (top != null && top == mRootActivityContainer.getTopResumedActivity()) {
// TODO(b/111361570): Remove this and update focused app per-display in
// WindowManager every time an activity becomes resumed in
// ActivityTaskManagerService#setResumedActivityUncheckLocked().
@@ -3597,7 +3589,7 @@
*/
private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
final ActivityStack stack =
- mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
+ mRootActivityContainer.getNextFocusableStack(this, !allowFocusSelf);
final String myReason = reason + " adjustFocusToNextFocusableStack";
if (stack == null) {
return null;
@@ -3651,7 +3643,7 @@
r.setVisible(false);
}
EventLogTags.writeAmStopActivity(
- r.userId, System.identityHashCode(r), r.shortComponentName);
+ r.mUserId, System.identityHashCode(r), r.shortComponentName);
mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
StopActivityItem.obtain(r.visible, r.configChangeFlags));
if (shouldSleepOrShutDownActivities()) {
@@ -3727,7 +3719,7 @@
}
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishedTask = r.getTask();
+ finishedTask = r.getTaskRecord();
int taskNdx = mTaskHistory.indexOf(finishedTask);
final TaskRecord task = finishedTask;
int activityNdx = task.mActivities.indexOf(r);
@@ -3801,7 +3793,7 @@
}
final boolean finishActivityAffinityLocked(ActivityRecord r) {
- ArrayList<ActivityRecord> activities = r.getTask().mActivities;
+ ArrayList<ActivityRecord> activities = r.getTaskRecord().mActivities;
for (int index = activities.indexOf(r); index >= 0; --index) {
ActivityRecord cur = activities.get(index);
if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) {
@@ -3819,15 +3811,15 @@
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Adding result to " + resultTo
+ " who=" + r.resultWho + " req=" + r.requestCode
+ " res=" + resultCode + " data=" + resultData);
- if (resultTo.userId != r.userId) {
+ if (resultTo.mUserId != r.mUserId) {
if (resultData != null) {
- resultData.prepareToLeaveUser(r.userId);
+ resultData.prepareToLeaveUser(r.mUserId);
}
}
if (r.info.applicationInfo.uid > 0) {
mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
resultTo.packageName, resultData,
- resultTo.getUriPermissionsLocked(), resultTo.userId);
+ resultTo.getUriPermissionsLocked(), resultTo.mUserId);
}
resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
r.resultTo = null;
@@ -3865,9 +3857,9 @@
mWindowManager.deferSurfaceLayout();
try {
r.makeFinishingLocked();
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
- r.userId, System.identityHashCode(r),
+ r.mUserId, System.identityHashCode(r),
task.taskId, r.shortComponentName, reason);
final ArrayList<ActivityRecord> activities = task.mActivities;
final int index = activities.indexOf(r);
@@ -4000,7 +3992,7 @@
r.setState(FINISHING, "finishCurrentActivityLocked");
final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
- && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
+ && prevState == PAUSED && (r.getActivityStack() != display.getFocusedStack()
|| (next == null && display.topRunningActivity() == null));
if (mode == FINISH_IMMEDIATELY
@@ -4018,11 +4010,11 @@
// stack, need to make something visible in its place. Also if the display does not
// have running activity, the configuration may need to be updated for restoring
// original orientation of the display.
- mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+ mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
if (activityRemoved) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
"destroyActivityLocked: finishCurrentActivityLocked r=" + r +
@@ -4035,7 +4027,7 @@
if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
mStackSupervisor.mFinishingActivities.add(r);
r.resumeKeyDispatchingLocked();
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
return r;
}
@@ -4076,15 +4068,15 @@
boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
// Basic case: for simple app-centric recents, we need to recreate
// the task if the affinity has changed.
- if (srec == null || srec.getTask().affinity == null ||
- !srec.getTask().affinity.equals(destAffinity)) {
+ if (srec == null || srec.getTaskRecord().affinity == null ||
+ !srec.getTaskRecord().affinity.equals(destAffinity)) {
return true;
}
// Document-centric case: an app may be split in to multiple documents;
// they need to re-create their task if this current activity is the root
// of a document, unless simply finishing it will return them to the the
// correct app behind.
- final TaskRecord task = srec.getTask();
+ final TaskRecord task = srec.getTaskRecord();
if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) {
// Okay, this activity is at the root of its task. What to do, what to do...
if (!inFrontOfStandardStack()) {
@@ -4108,7 +4100,7 @@
final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
Intent resultData) {
- final TaskRecord task = srec.getTask();
+ final TaskRecord task = srec.getTaskRecord();
final ArrayList<ActivityRecord> activities = task.mActivities;
final int start = activities.indexOf(srec);
if (!mTaskHistory.contains(task) || (start < 0)) {
@@ -4173,7 +4165,7 @@
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
- srec.userId);
+ srec.mUserId);
// TODO(b/64750076): Check if calling pid should really be -1.
final int res = mService.getActivityStartController()
.obtainStarter(destIntent, "navigateUpTo")
@@ -4290,7 +4282,7 @@
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r);
r.app = null;
r.removeWindowContainer();
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
final boolean lastActivity = task != null ? task.removeActivity(r) : false;
// If we are removing the last activity in the task, not including task overlay activities,
// then fall through into the block below to remove the entire task itself
@@ -4377,7 +4369,7 @@
}
}
if (activityRemoved) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
@@ -4454,8 +4446,8 @@
}
EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
- r.userId, System.identityHashCode(r),
- r.getTask().taskId, r.shortComponentName, reason);
+ r.mUserId, System.identityHashCode(r),
+ r.getTaskRecord().taskId, r.shortComponentName, reason);
boolean removedFromHistory = false;
@@ -4568,7 +4560,7 @@
}
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
@@ -4654,8 +4646,8 @@
if (!r.finishing) {
Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
- r.userId, System.identityHashCode(r),
- r.getTask().taskId, r.shortComponentName,
+ r.mUserId, System.identityHashCode(r),
+ r.getTaskRecord().taskId, r.shortComponentName,
"proc died without state saved");
if (r.getState() == RESUMED) {
mService.updateUsageStats(r, false);
@@ -4712,7 +4704,7 @@
task.mLastTimeMoved *= -1;
}
}
- mStackSupervisor.invalidateTaskLayers();
+ mRootActivityContainer.invalidateTaskLayers();
}
final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
@@ -4754,7 +4746,7 @@
final ActivityRecord top = tr.getTopActivity();
if (top == null || !top.okToShowLocked()) {
if (top != null) {
- mStackSupervisor.mRecentTasks.add(top.getTask());
+ mStackSupervisor.mRecentTasks.add(top.getTaskRecord());
}
ActivityOptions.abort(options);
return;
@@ -4788,7 +4780,7 @@
topActivity.supportsEnterPipOnTaskSwitch = true;
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
@@ -4860,7 +4852,7 @@
return true;
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
return true;
}
@@ -4869,7 +4861,7 @@
final String strData = data != null ? data.toSafeString() : null;
EventLog.writeEvent(tag,
- r.userId, System.identityHashCode(r), task.taskId,
+ r.mUserId, System.identityHashCode(r), task.taskId,
r.shortComponentName, r.intent.getAction(),
r.intent.getType(), strData, r.intent.getFlags());
}
@@ -4882,15 +4874,15 @@
return;
}
- final TaskRecord startTask = start.getTask();
+ final TaskRecord startTask = start.getTaskRecord();
boolean behindFullscreen = false;
boolean updatedConfig = false;
for (int taskIndex = mTaskHistory.indexOf(startTask); taskIndex >= 0; --taskIndex) {
final TaskRecord task = mTaskHistory.get(taskIndex);
final ArrayList<ActivityRecord> activities = task.mActivities;
- int activityIndex =
- (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1;
+ int activityIndex = (start.getTaskRecord() == task)
+ ? activities.indexOf(start) : activities.size() - 1;
for (; activityIndex >= 0; --activityIndex) {
final ActivityRecord r = activities.get(activityIndex);
updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
@@ -4907,7 +4899,7 @@
if (updatedConfig) {
// Ensure the resumed state of the focus activity if we updated the configuration of
// any activity.
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
@@ -4952,9 +4944,12 @@
}
}
- mTmpBounds.put(task.taskId, task.getOverrideBounds());
- if (tempTaskInsetBounds != null) {
- mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
+ if (task.hasDisplayedBounds()) {
+ mTmpBounds.put(task.taskId, task.getDisplayedBounds());
+ mTmpInsetBounds.put(task.taskId, task.getOverrideBounds());
+ } else {
+ mTmpBounds.put(task.taskId, task.getOverrideBounds());
+ mTmpInsetBounds.put(task.taskId, null);
}
}
@@ -5054,10 +5049,10 @@
ActivityRecord r = mTmpActivities.remove(0);
final boolean sameComponent =
(r.packageName.equals(packageName) && (filterByClasses == null
- || filterByClasses.contains(r.realActivity.getClassName())))
- || (packageName == null && r.userId == userId);
- if ((userId == UserHandle.USER_ALL || r.userId == userId)
- && (sameComponent || r.getTask() == lastTask)
+ || filterByClasses.contains(r.mActivityComponent.getClassName())))
+ || (packageName == null && r.mUserId == userId);
+ if ((userId == UserHandle.USER_ALL || r.mUserId == userId)
+ && (sameComponent || r.getTaskRecord() == lastTask)
&& (r.app == null || evenPersistent || !r.app.isPersistent())) {
if (!doit) {
if (r.finishing) {
@@ -5068,11 +5063,11 @@
return true;
}
if (r.isActivityTypeHome()) {
- if (homeActivity != null && homeActivity.equals(r.realActivity)) {
+ if (homeActivity != null && homeActivity.equals(r.mActivityComponent)) {
Slog.i(TAG, "Skip force-stop again " + r);
continue;
} else {
- homeActivity = r.realActivity;
+ homeActivity = r.mActivityComponent;
}
}
didSomething = true;
@@ -5083,7 +5078,7 @@
}
r.app = null;
}
- lastTask = r.getTask();
+ lastTask = r.getTaskRecord();
finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop",
true);
}
@@ -5099,7 +5094,7 @@
*/
void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
- boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
+ boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this;
boolean topTask = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -5164,7 +5159,7 @@
return removeHistoryRecordsForAppLocked(app);
}
- void handleAppCrashLocked(WindowProcessController app) {
+ void handleAppCrash(WindowProcessController app) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -5311,7 +5306,7 @@
// We only need to adjust focused stack if this stack is in focus and we are not in the
// process of moving the task to the top of the stack that will be focused.
if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
- && mStackSupervisor.isTopDisplayFocusedStack(this)) {
+ && mRootActivityContainer.isTopDisplayFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
getDisplay().moveHomeStackToFront(myReason);
@@ -5417,7 +5412,7 @@
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) {
@@ -5484,7 +5479,7 @@
moveToFront(reason);
// If the original state is resumed, there is no state change to update focused app.
// So here makes sure the activity focus is set if it is the top.
- if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+ if (origState == RESUMED && r == mRootActivityContainer.getTopResumedActivity()) {
// TODO(b/111361570): Support multiple focused apps in WM
mService.setResumedActivityUncheckLocked(r, reason);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index c517bd7..f3c5630 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -25,28 +25,18 @@
import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WaitResult.INVALID_DELAY;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -59,26 +49,13 @@
import static android.view.Display.TYPE_VIRTUAL;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
-import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
-import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
-import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
-import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
-import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -86,9 +63,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -96,6 +71,10 @@
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.android.server.wm.RootActivityContainer.TAG_STATES;
import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
@@ -103,27 +82,15 @@
import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-import static java.lang.Integer.MAX_VALUE;
-
import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
-import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.WaitResult;
-import android.app.WindowConfiguration;
-import android.app.WindowConfiguration.ActivityType;
-import android.app.WindowConfiguration.WindowingMode;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.LaunchActivityItem;
@@ -139,17 +106,10 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
-import android.os.FactoryTest;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -163,20 +123,13 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.MediaStore;
-import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.DisplayMetrics;
import android.util.EventLog;
-import android.util.IntArray;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-import android.view.DisplayInfo;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -185,34 +138,28 @@
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
-import com.android.server.am.AppTimeTracker;
import com.android.server.am.EventLogTags;
import com.android.server.am.UserState;
-import com.android.server.wm.ActivityStack.ActivityState;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
-import java.util.Set;
-public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
- RecentTasks.Callbacks, RootWindowContainerListener {
+// TODO: This class has become a dumping ground. Let's
+// - Move things relating to the hierarchy to RootWindowContainer
+// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler
+// - Move interface things to ActivityTaskManagerService.
+// - All other little things to other files.
+public class ActivityStackSupervisor implements RecentTasks.Callbacks {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
- private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
- static final String TAG_STATES = TAG + POSTFIX_STATES;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
/** How long we wait until giving up on the last activity telling us it is idle. */
@@ -233,12 +180,6 @@
static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
- private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
-
- // Used to indicate if an object (e.g. stack) that we are trying to get
- // should be created if it doesn't exist already.
- static final boolean CREATE_IF_NEEDED = true;
-
// Used to indicate that windows of activities should be preserved during the resize.
static final boolean PRESERVE_WINDOWS = true;
@@ -270,25 +211,6 @@
private Rect mPendingTempOtherTaskBounds;
private Rect mPendingTempOtherTaskInsetBounds;
- /**
- * The modes which affect which tasks are returned when calling
- * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- MATCH_TASK_IN_STACKS_ONLY,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
- })
- public @interface AnyTaskForIdMatchTaskMode {}
- // Match only tasks in the current stacks
- static final int MATCH_TASK_IN_STACKS_ONLY = 0;
- // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
- static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
- // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
- // provided stack id
- static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
-
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -316,19 +238,19 @@
private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
final ActivityTaskManagerService mService;
+ RootActivityContainer mRootActivityContainer;
/** The historial list of recent tasks including inactive tasks */
RecentTasks mRecentTasks;
/** Helper class to abstract out logic for fetching the set of currently running tasks */
- private RunningTasks mRunningTasks;
+ RunningTasks mRunningTasks;
final ActivityStackSupervisorHandler mHandler;
final Looper mLooper;
/** Short cut */
WindowManagerService mWindowManager;
- DisplayManager mDisplayManager;
/** Common synchronization logic used to save things to disks. */
PersisterQueue mPersisterQueue;
@@ -341,9 +263,6 @@
*/
private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
- /** The current user */
- int mCurrentUser;
-
/** List of activities that are waiting for a new activity to become visible before completing
* whatever operation they are supposed to do. */
// TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from
@@ -392,9 +311,6 @@
* is being brought in front of us. */
boolean mUserLeaving = false;
- /** Set when a power hint has started, but not ended. */
- private boolean mPowerHintSent;
-
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
@@ -410,29 +326,6 @@
*/
PowerManager.WakeLock mGoingToSleep;
- /**
- * A list of tokens that cause the top activity to be put to sleep.
- * They are used by components that may hide and block interaction with underlying
- * activities.
- */
- final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
-
- /** Stack id of the front stack when user switched, indexed by userId. */
- SparseIntArray mUserStackInFront = new SparseIntArray(2);
-
- /** Reference to default display so we can quickly look it up. */
- private ActivityDisplay mDefaultDisplay;
-
- /**
- * List of displays which contain activities, sorted by z-order.
- * The last entry in the list is the topmost.
- */
- private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
-
- private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
-
- private DisplayManagerInternal mDisplayManagerInternal;
-
/** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
boolean inResumeTopActivity;
@@ -443,50 +336,8 @@
private final Rect tempRect = new Rect();
private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
- // The default minimal size that will be used if the activity doesn't specify its minimal size.
- // It will be calculated when the default display gets added.
- int mDefaultMinSizeOfResizeableTaskDp = -1;
-
- // Whether tasks have moved and we need to rank the tasks before next OOM scoring
- private boolean mTaskLayersChanged = true;
-
private ActivityMetricsLogger mActivityMetricsLogger;
- private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
-
- @Override
- protected int getChildCount() {
- return mActivityDisplays.size();
- }
-
- @Override
- protected ActivityDisplay getChildAt(int index) {
- return mActivityDisplays.get(index);
- }
-
- @Override
- protected ConfigurationContainer getParent() {
- return null;
- }
-
- Configuration getDisplayOverrideConfiguration(int displayId) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- throw new IllegalArgumentException("No display found with id: " + displayId);
- }
-
- return activityDisplay.getOverrideConfiguration();
- }
-
- void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- throw new IllegalArgumentException("No display found with id: " + displayId);
- }
-
- activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
- }
-
/** Check if placing task or activity on specified display is allowed. */
boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
ActivityInfo activityInfo) {
@@ -508,44 +359,6 @@
}
/**
- * Check if configuration of specified display matches current global config.
- * Used to check if we can put a non-resizeable activity on a secondary display and it will get
- * the same config as on the default display.
- * @param displayId Id of the display to check.
- * @return {@code true} if configuration matches.
- */
- private boolean displayConfigMatchesGlobal(int displayId) {
- if (displayId == DEFAULT_DISPLAY) {
- return true;
- }
- if (displayId == INVALID_DISPLAY) {
- return false;
- }
- final ActivityDisplay targetDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (targetDisplay == null) {
- throw new IllegalArgumentException("No display found with id: " + displayId);
- }
- return getConfiguration().equals(targetDisplay.getConfiguration());
- }
-
- static class FindTaskResult {
- ActivityRecord mRecord;
- boolean mIdealMatch;
-
- void clear() {
- mRecord = null;
- mIdealMatch = false;
- }
-
- void setTo(FindTaskResult result) {
- mRecord = result.mRecord;
- mIdealMatch = result.mIdealMatch;
- }
- }
-
- private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
-
- /**
* Used to keep track whether app visibilities got changed since the last pause. Useful to
* determine whether to invoke the task stack change listener after pausing.
*/
@@ -565,11 +378,6 @@
*/
private boolean mAllowDockedStackResize = true;
- /**
- * Is dock currently minimized.
- */
- boolean mIsDockMinimized;
-
private KeyguardController mKeyguardController;
private PowerManager mPowerManager;
@@ -577,8 +385,6 @@
private boolean mInitialized;
- private RootWindowContainerController mWindowContainerController;
-
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -617,11 +423,6 @@
mHandler = new ActivityStackSupervisorHandler(looper);
}
- @VisibleForTesting
- void setWindowContainerController(RootWindowContainerController controller) {
- mWindowContainerController = controller;
- }
-
public void initialize() {
if (mInitialized) {
return;
@@ -629,7 +430,9 @@
mInitialized = true;
mRunningTasks = createRunningTasks();
- mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
+
+ mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
+ mHandler.getLooper());
mKeyguardController = new KeyguardController(mService, this);
mPersisterQueue = new PersisterQueue();
@@ -676,321 +479,16 @@
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
getKeyguardController().setWindowManager(wm);
- setWindowContainerController(new RootWindowContainerController(this));
-
- mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
- mDisplayManager.registerDisplayListener(this, mHandler);
- mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-
- final Display[] displays = mDisplayManager.getDisplays();
- for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
- final Display display = displays[displayNdx];
- final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
- if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
- mDefaultDisplay = activityDisplay;
- }
- addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
- }
- calculateDefaultMinimalSizeOfResizeableTasks();
-
- final ActivityDisplay defaultDisplay = getDefaultDisplay();
-
- defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
- }
-
- /** Change the z-order of the given display. */
- private void positionChildAt(ActivityDisplay display, int position) {
- if (position >= mActivityDisplays.size()) {
- position = mActivityDisplays.size() - 1;
- } else if (position < 0) {
- position = 0;
- }
-
- if (mActivityDisplays.isEmpty()) {
- mActivityDisplays.add(display);
- } else if (mActivityDisplays.get(position) != display) {
- mActivityDisplays.remove(display);
- mActivityDisplays.add(position, display);
- }
- }
-
- @Override
- public void onChildPositionChanged(DisplayWindowController childController, int position) {
- // Assume AM lock is held from positionChildAt of controller in each hierarchy.
- final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
- if (display != null) {
- positionChildAt(display, position);
- }
- }
-
- ActivityStack getTopDisplayFocusedStack() {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
- if (focusedStack != null) {
- return focusedStack;
- }
- }
- return null;
- }
-
- ActivityRecord getTopResumedActivity() {
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- if (focusedStack == null) {
- return null;
- }
- final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
- if (resumedActivity != null && resumedActivity.app != null) {
- return resumedActivity;
- }
- // The top focused stack might not have a resumed activity yet - look on all displays in
- // focus order.
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
- if (resumedActivityOnDisplay != null) {
- return resumedActivityOnDisplay;
- }
- }
- return null;
- }
-
- boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
- if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
- return false;
- }
-
- return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
- }
-
- boolean isTopDisplayFocusedStack(ActivityStack stack) {
- return stack != null && stack == getTopDisplayFocusedStack();
}
void moveRecentsStackToFront(String reason) {
- final ActivityStack recentsStack = getDefaultDisplay().getStack(
+ final ActivityStack recentsStack = mRootActivityContainer.getDefaultDisplay().getStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
if (recentsStack != null) {
recentsStack.moveToFront(reason);
}
}
- boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
- if (!mService.isBooting() && !mService.isBooted()) {
- // Not ready yet!
- return false;
- }
-
- if (displayId == INVALID_DISPLAY) {
- displayId = DEFAULT_DISPLAY;
- }
-
- final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
- final String myReason = reason + " resumeHomeActivity";
-
- // Only resume home activity if isn't finishing.
- if (r != null && !r.finishing) {
- r.moveFocusableActivityToTop(myReason);
- return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
- }
- return startHomeOnDisplay(mCurrentUser, myReason, displayId);
- }
-
- /**
- * Check if home activity start should be allowed on a display.
- * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
- * @param displayId The id of the target display.
- * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
- * @return {@code true} if allow to launch, {@code false} otherwise.
- */
- boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
- boolean allowInstrumenting) {
- if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
- && mService.mTopAction == null) {
- // We are running in factory test mode, but unable to find the factory test app, so
- // just sit around displaying the error message and don't try to start anything.
- return false;
- }
-
- final WindowProcessController app =
- mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
- if (!allowInstrumenting && app != null && app.isInstrumenting()) {
- // Don't do this if the home app is currently being instrumented.
- return false;
- }
-
- if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
- && displayId == mService.mVr2dDisplayId)) {
- // No restrictions to default display or vr 2d display.
- return true;
- }
-
- final ActivityDisplay display = getActivityDisplay(displayId);
- if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
- // Can't launch home on display that doesn't support system decorations.
- return false;
- }
-
- final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
- && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
- && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
- if (!supportMultipleInstance) {
- // Can't launch home on other displays if it requested to be single instance. Also we
- // don't allow home applications that target before Q to have multiple home activity
- // instances because they may not be expected to have multiple home scenario and
- // haven't explicitly request for single instance.
- return false;
- }
-
- return true;
- }
-
- TaskRecord anyTaskForIdLocked(int id) {
- return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
- }
-
- TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
- return anyTaskForIdLocked(id, matchMode, null, !ON_TOP);
- }
-
- /**
- * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
- * @param id Id of the task we would like returned.
- * @param matchMode The mode to match the given task id in.
- * @param aOptions The activity options to use for restoration. Can be null.
- * @param onTop If the stack for the task should be the topmost on the display.
- */
- TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode,
- @Nullable ActivityOptions aOptions, boolean onTop) {
- // If options are set, ensure that we are attempting to actually restore a task
- if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
- throw new IllegalArgumentException("Should not specify activity options for non-restore"
- + " lookup");
- }
-
- int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final TaskRecord task = stack.taskForIdLocked(id);
- if (task == null) {
- continue;
- }
- if (aOptions != null) {
- // Resolve the stack the task should be placed in now based on options
- // and reparent if needed.
- final ActivityStack launchStack = getLaunchStack(null, aOptions, task, onTop);
- if (launchStack != null && stack != launchStack) {
- final int reparentMode = onTop
- ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
- task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
- "anyTaskForIdLocked");
- }
- }
- return task;
- }
- }
-
- // If we are matching stack tasks only, return now
- if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
- return null;
- }
-
- // Otherwise, check the recent tasks and return if we find it there and we are not restoring
- // the task from recents
- if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
- final TaskRecord task = mRecentTasks.getTask(id);
-
- if (task == null) {
- if (DEBUG_RECENTS) {
- Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
- }
-
- return null;
- }
-
- if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
- return task;
- }
-
- // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
- if (!restoreRecentTaskLocked(task, aOptions, onTop)) {
- if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
- "Couldn't restore task id=" + id + " found in recents");
- return null;
- }
- if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
- return task;
- }
-
- ActivityRecord isInAnyStackLocked(IBinder token) {
- int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.isInStackLocked(token);
- if (r != null) {
- return r;
- }
- }
- }
- return null;
- }
-
- /**
- * Detects whether we should show a lock screen in front of this task for a locked user.
- * <p>
- * We'll do this if either of the following holds:
- * <ul>
- * <li>The top activity explicitly belongs to {@param userId}.</li>
- * <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
- * </ul>
- *
- * @return {@code true} if the top activity looks like it belongs to {@param userId}.
- */
- private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
- // To handle the case that work app is in the task but just is not the top one.
- final ActivityRecord activityRecord = task.getTopActivity();
- final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
-
- return (activityRecord != null && activityRecord.userId == userId)
- || (resultTo != null && resultTo.userId == userId);
- }
-
- /**
- * Find all visible task stacks containing {@param userId} and intercept them with an activity
- * to block out the contents and possibly start a credential-confirming intent.
- *
- * @param userId user handle for the locked managed profile.
- */
- void lockAllProfileTasks(@UserIdInt int userId) {
- mWindowManager.deferSurfaceLayout();
- try {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final List<TaskRecord> tasks = stack.getAllTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
- final TaskRecord task = tasks.get(taskNdx);
-
- // Check the task for a top activity belonging to userId, or returning a
- // result to an activity belonging to userId. Example case: a document
- // picker for personal files, opened by a work app, should still get locked.
- if (taskTopActivityIsUser(task, userId)) {
- mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
- task.taskId, userId);
- }
- }
- }
- }
- } finally {
- mWindowManager.continueSurfaceLayout();
- }
- }
-
void setNextTaskIdForUserLocked(int taskId, int userId) {
final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
if (taskId > currentTaskId) {
@@ -1014,7 +512,7 @@
// was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
while (mRecentTasks.containsTaskId(candidateTaskId, userId)
- || anyTaskForIdLocked(
+ || mRootActivityContainer.anyTaskForId(
candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
if (candidateTaskId == currentTaskId) {
@@ -1029,142 +527,6 @@
return candidateTaskId;
}
- boolean attachApplicationLocked(WindowProcessController app) throws RemoteException {
- final String processName = app.mName;
- boolean didSomething = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final ActivityStack stack = display.getFocusedStack();
- if (stack != null) {
- stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
- final ActivityRecord top = stack.topRunningActivityLocked();
- final int size = mTmpActivityList.size();
- for (int i = 0; i < size; i++) {
- final ActivityRecord activity = mTmpActivityList.get(i);
- if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
- && processName.equals(activity.processName)) {
- try {
- if (realStartActivityLocked(activity, app,
- top == activity /* andResume */, true /* checkConfig */)) {
- didSomething = true;
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception in new application when starting activity "
- + top.intent.getComponent().flattenToShortString(), e);
- throw e;
- }
- }
- }
- }
- }
- if (!didSomething) {
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- }
- return didSomething;
- }
-
- boolean allResumedActivitiesIdle() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- // TODO(b/117135575): Check resumed activities on all visible stacks.
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- if (display.isSleeping()) {
- // No resumed activities while display is sleeping.
- continue;
- }
-
- // If the focused stack is not null or not empty, there should have some activities
- // resuming or resumed. Make sure these activities are idle.
- final ActivityStack stack = display.getFocusedStack();
- if (stack == null || stack.numActivities() == 0) {
- continue;
- }
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity == null || !resumedActivity.idle) {
- if (DEBUG_STATES) {
- Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
- + stack.mStackId + " " + resumedActivity + " not idle");
- }
- return false;
- }
- }
- // Send launch end powerhint when idle
- sendPowerHintForLaunchEndIfNeeded();
- return true;
- }
-
- private boolean allResumedActivitiesVisible() {
- boolean foundResumed = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.getResumedActivity();
- if (r != null) {
- if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) {
- return false;
- }
- foundResumed = true;
- }
- }
- }
- return foundResumed;
- }
-
- private void executeAppTransitionForAllDisplay() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- display.getWindowContainerController().executeAppTransition();
- }
- }
-
- /**
- * Pause all activities in either all of the stacks or just the back stacks.
- * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
- * @param resuming The resuming activity.
- * @param dontWait The resuming activity isn't going to wait for all activities to be paused
- * before resuming.
- * @return true if any activity was paused as a result of this call.
- */
- boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
- boolean someActivityPaused = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- someActivityPaused |= mActivityDisplays.get(displayNdx)
- .pauseBackStacks(userLeaving, resuming, dontWait);
- }
- return someActivityPaused;
- }
-
- boolean allPausedActivitiesComplete() {
- boolean pausing = true;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.mPausingActivity;
- if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
- if (DEBUG_STATES) {
- Slog.d(TAG_STATES,
- "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
- pausing = false;
- } else {
- return false;
- }
- }
- }
- }
- return pausing;
- }
-
- void cancelInitializingActivities() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.cancelInitializingActivities();
- }
- }
- }
-
void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) {
final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs);
mWaitingForActivityVisible.add(waitInfo);
@@ -1178,7 +540,7 @@
mActivitiesWaitingForVisibleActivity.remove(r);
for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
- if (mWaitingForActivityVisible.get(i).matches(r.realActivity)) {
+ if (mWaitingForActivityVisible.get(i).matches(r.mActivityComponent)) {
mWaitingForActivityVisible.remove(i);
}
}
@@ -1192,7 +554,7 @@
boolean changed = false;
for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
final WaitInfo w = mWaitingForActivityVisible.get(i);
- if (w.matches(r.realActivity)) {
+ if (w.matches(r.mActivityComponent)) {
final WaitResult result = w.getResult();
changed = true;
result.timeout = false;
@@ -1226,7 +588,7 @@
// Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there
// will be no followup launch signals. Assign the result and launched component.
if (result == START_DELIVERED_TO_TOP) {
- w.who = r.realActivity;
+ w.who = r.mActivityComponent;
}
}
}
@@ -1255,24 +617,6 @@
}
}
- ActivityRecord topRunningActivityLocked() {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
- if (topActivity != null) {
- return topActivity;
- }
- }
- return null;
- }
-
- @VisibleForTesting
- void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
- @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
- int callingUid, boolean allowed) {
- mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode,
- mActivityDisplays, callingUid, allowed);
- }
-
ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
ProfilerInfo profilerInfo) {
final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
@@ -1318,31 +662,29 @@
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
int filterCallingUid) {
- synchronized (mService.mGlobalLock) {
- try {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
- int modifiedFlags = flags
- | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
- if (intent.isWebIntent()
- || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
- modifiedFlags |= PackageManager.MATCH_INSTANT;
- }
-
- // In order to allow cross-profile lookup, we clear the calling identity here.
- // Note the binder identity won't affect the result, but filterCallingUid will.
-
- // Cross-user/profile call check are done at the entry points
- // (e.g. AMS.startActivityAsUser).
- final long token = Binder.clearCallingIdentity();
- try {
- return mService.getPackageManagerInternalLocked().resolveIntent(
- intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ try {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+ int modifiedFlags = flags
+ | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
+ if (intent.isWebIntent()
+ || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
+ modifiedFlags |= PackageManager.MATCH_INSTANT;
}
+
+ // In order to allow cross-profile lookup, we clear the calling identity here.
+ // Note the binder identity won't affect the result, but filterCallingUid will.
+
+ // Cross-user/profile call check are done at the entry points
+ // (e.g. AMS.startActivityAsUser).
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mService.getPackageManagerInternalLocked().resolveIntent(
+ intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -1352,10 +694,10 @@
return resolveActivity(intent, rInfo, startFlags, profilerInfo);
}
- private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
+ boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
- if (!allPausedActivitiesComplete()) {
+ if (!mRootActivityContainer.allPausedActivitiesComplete()) {
// While there are activities pausing we skipping starting any new activities until
// pauses are complete. NOTE: that we also do this for activities that are starting in
// the paused state because they will first be resumed then paused on the client side.
@@ -1365,7 +707,7 @@
return false;
}
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
final ActivityStack stack = task.getStack();
beginDeferResume();
@@ -1390,11 +732,11 @@
// Deferring resume here because we're going to launch new activity shortly.
// We don't want to perform a redundant launch of the same record while ensuring
// configurations and trying to resume top activity of focused stack.
- ensureVisibilityAndConfig(r, r.getDisplayId(),
+ mRootActivityContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
- if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
+ if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
true /* isTop */)) {
// We only set the visibility to true if the activity is allowed to be visible
// based on
@@ -1406,7 +748,7 @@
final int applicationInfoUid =
(r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
- if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
+ if ((r.mUserId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
Slog.wtf(TAG,
"User ID for activity changing for " + r
+ " appInfo.uid=" + r.appInfo.uid
@@ -1447,7 +789,7 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Launching: " + r + " icicle=" + r.icicle + " with results=" + results
+ " newIntents=" + newIntents + " andResume=" + andResume);
- EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
+ EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.mUserId,
System.identityHashCode(r), task.taskId, r.shortComponentName);
if (r.isActivityTypeHome()) {
// Home process is the root process of the task.
@@ -1560,7 +902,7 @@
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
- if (isTopDisplayFocusedStack(stack)) {
+ if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
mService.getActivityStartController().startSetupActivity();
}
@@ -1573,47 +915,6 @@
return true;
}
- /**
- * Ensure all activities visibility, update orientation and configuration.
- *
- * @param starting The currently starting activity or {@code null} if there is none.
- * @param displayId The id of the display where operation is executed.
- * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
- * {@code true} if config changed.
- * @param deferResume Whether to defer resume while updating config.
- * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
- * because of configuration update.
- */
- boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
- boolean markFrozenIfConfigChanged, boolean deferResume) {
- // First ensure visibility without updating the config just yet. We need this to know what
- // activities are affecting configuration now.
- // Passing null here for 'starting' param value, so that visibility of actual starting
- // activity will be properly updated.
- ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */, false /* notifyClients */);
-
- if (displayId == INVALID_DISPLAY) {
- // The caller didn't provide a valid display id, skip updating config.
- return true;
- }
-
- // Force-update the orientation from the WindowManager, since we need the true configuration
- // to send to the client now.
- final Configuration config = mWindowManager.updateOrientationFromAppTokens(
- getDisplayOverrideConfiguration(displayId),
- starting != null && starting.mayFreezeScreenLocked(starting.app)
- ? starting.appToken : null,
- displayId, true /* forceUpdate */);
- if (starting != null && markFrozenIfConfigChanged && config != null) {
- starting.frozenBeforeDestroy = true;
- }
-
- // Update the configuration of the activities on the display.
- return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
- displayId);
- }
-
private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
int extrasSize = 0;
if (intent != null) {
@@ -1669,47 +970,6 @@
mService.mH.sendMessage(msg);
}
- void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
- boolean sendHint = forceSend;
-
- if (!sendHint) {
- // Send power hint if we don't know what we're launching yet
- sendHint = targetActivity == null || targetActivity.app == null;
- }
-
- if (!sendHint) { // targetActivity != null
- // Send power hint when the activity's process is different than the current resumed
- // activity on all displays, or if there are no resumed activities in the system.
- boolean noResumedActivities = true;
- boolean allFocusedProcessesDiffer = true;
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
- final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
- final WindowProcessController resumedActivityProcess =
- resumedActivity == null ? null : resumedActivity.app;
-
- noResumedActivities &= resumedActivityProcess == null;
- if (resumedActivityProcess != null) {
- allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
- }
- }
- sendHint = noResumedActivities || allFocusedProcessesDiffer;
- }
-
- if (sendHint && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
- mPowerHintSent = true;
- }
- }
-
- void sendPowerHintForLaunchEndIfNeeded() {
- // Trigger launch power hint if activity is launched
- if (mPowerHintSent && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
- mPowerHintSent = false;
- }
- }
-
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
int requestCode, int callingPid, int callingUid, String callingPackage,
boolean ignoreTargetSecurity, boolean launchingInTask,
@@ -1788,7 +1048,8 @@
return true;
}
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId);
+ final ActivityDisplay activityDisplay =
+ mRootActivityContainer.getActivityDisplayOrCreate(launchDisplayId);
if (activityDisplay == null || activityDisplay.isRemoved()) {
Slog.w(TAG, "Launch on display check: display not found");
return false;
@@ -1850,21 +1111,6 @@
return false;
}
- /** Update lists of UIDs that are present on displays and have access to them. */
- void updateUIDsPresentOnDisplay() {
- mDisplayAccessUIDs.clear();
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
- // Only bother calculating the whitelist for private displays
- if (activityDisplay.isPrivate()) {
- mDisplayAccessUIDs.append(
- activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
- }
- }
- // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
- mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
- }
-
UserInfo getUserInfo(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -2016,7 +1262,8 @@
// Check if able to finish booting when device is booting and all resumed activities
// are idle.
- if ((mService.isBooting() && allResumedActivitiesIdle()) || fromTimeout) {
+ if ((mService.isBooting() && mRootActivityContainer.allResumedActivitiesIdle())
+ || fromTimeout) {
booting = checkFinishBootingLocked();
}
@@ -2025,7 +1272,7 @@
r.mRelaunchReason = RELAUNCH_REASON_NONE;
}
- if (allResumedActivitiesIdle()) {
+ if (mRootActivityContainer.allResumedActivitiesIdle()) {
if (r != null) {
mService.scheduleAppGcsLocked();
}
@@ -2038,7 +1285,7 @@
}
mLaunchingActivity.release();
}
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
// Atomically retrieve all of the other things to do.
@@ -2059,7 +1306,7 @@
// waiting for the next one to start.
for (int i = 0; i < NS; i++) {
r = stops.get(i);
- final ActivityStack stack = r.getStack();
+ final ActivityStack stack = r.getActivityStack();
if (stack != null) {
if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
@@ -2074,7 +1321,7 @@
// waiting for the next one to start.
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
- final ActivityStack stack = r.getStack();
+ final ActivityStack stack = r.getActivityStack();
if (stack != null) {
activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
}
@@ -2094,186 +1341,13 @@
//mWindowManager.dump();
if (activityRemoved) {
- resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return r;
}
- boolean handleAppDiedLocked(WindowProcessController app) {
- boolean hasVisibleActivities = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- hasVisibleActivities |= stack.handleAppDiedLocked(app);
- }
- }
- return hasVisibleActivities;
- }
-
- void closeSystemDialogsLocked() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.closeSystemDialogsLocked();
- }
- }
- }
-
- void removeUserLocked(int userId) {
- mUserStackInFront.delete(userId);
- }
-
- /**
- * Update the last used stack id for non-current user (current user's last
- * used stack is the focused stack)
- */
- void updateUserStackLocked(int userId, ActivityStack stack) {
- if (userId != mCurrentUser) {
- mUserStackInFront.put(userId, stack != null ? stack.getStackId()
- : getDefaultDisplay().getHomeStack().mStackId);
- }
- }
-
- /**
- * @return true if some activity was finished (or would have finished if doit were true).
- */
- boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
- boolean didSomething = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (stack.finishDisabledPackageActivitiesLocked(
- packageName, filterByClasses, doit, evenPersistent, userId)) {
- didSomething = true;
- }
- }
- }
- return didSomething;
- }
-
- void updatePreviousProcessLocked(ActivityRecord r) {
- // Now that this process has stopped, we may want to consider
- // it to be the previous app to try to keep around in case
- // the user wants to return to it.
-
- // First, found out what is currently the foreground app, so that
- // we don't blow away the previous app if this activity is being
- // hosted by the process that is actually still the foreground.
- WindowProcessController fgApp = null;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (isTopDisplayFocusedStack(stack)) {
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity != null) {
- fgApp = resumedActivity.app;
- } else if (stack.mPausingActivity != null) {
- fgApp = stack.mPausingActivity.app;
- }
- break;
- }
- }
- }
-
- // Now set this one as the previous process, only if that really
- // makes sense to.
- if (r.hasProcess() && fgApp != null && r.app != fgApp
- && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
- && r.app != mService.mHomeProcess) {
- mService.mPreviousProcess = r.app;
- mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
- }
- }
-
- boolean resumeFocusedStacksTopActivitiesLocked() {
- return resumeFocusedStacksTopActivitiesLocked(null, null, null);
- }
-
- boolean resumeFocusedStacksTopActivitiesLocked(
- ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
-
- if (!readyToResume()) {
- return false;
- }
-
- if (targetStack != null && (targetStack.isTopStackOnDisplay()
- || getTopDisplayFocusedStack() == targetStack)) {
- return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
- }
-
- // Resume all top activities in focused stacks on all displays.
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final ActivityStack focusedStack = display.getFocusedStack();
- if (focusedStack == null) {
- continue;
- }
- final ActivityRecord r = focusedStack.topRunningActivityLocked();
- if (r == null || !r.isState(RESUMED)) {
- focusedStack.resumeTopActivityUncheckedLocked(null, null);
- } else if (r.isState(RESUMED)) {
- // Kick off any lingering app transitions form the MoveTaskToFront operation.
- focusedStack.executeAppTransition(targetOptions);
- }
- }
-
- return false;
- }
-
- void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.updateActivityApplicationInfoLocked(aInfo);
- }
- }
- }
-
- /**
- * Finish the topmost activities in all stacks that belong to the crashed app.
- * @param app The app that crashed.
- * @param reason Reason to perform this action.
- * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
- */
- int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
- TaskRecord finishedTask = null;
- ActivityStack focusedStack = getTopDisplayFocusedStack();
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- // It is possible that request to finish activity might also remove its task and stack,
- // so we need to be careful with indexes in the loop and check child count every time.
- for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
- if (stack == focusedStack || finishedTask == null) {
- finishedTask = t;
- }
- }
- }
- return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
- }
-
- void finishVoiceTask(IVoiceInteractionSession session) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final int numStacks = display.getChildCount();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.finishVoiceTask(session);
- }
- }
- }
-
- /**
- * This doesn't just find a task, it also moves the task to front.
- */
+ /** This doesn't just find a task, it also moves the task to front. */
void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
ActivityStack currentStack = task.getStack();
@@ -2293,7 +1367,8 @@
final Rect bounds = options.getLaunchBounds();
task.updateOverrideConfiguration(bounds);
- ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
+ ActivityStack stack =
+ mRootActivityContainer.getLaunchStack(null, options, task, ON_TOP);
if (stack != currentStack) {
moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
@@ -2305,7 +1380,7 @@
// still need moveTaskToFrontLocked() below for any transition settings.
}
if (stack.resizeStackWithLaunchBounds()) {
- resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
+ mRootActivityContainer.resizeStack(stack, bounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
} else {
@@ -2358,388 +1433,22 @@
return mLaunchParamsController;
}
- protected <T extends ActivityStack> T getStack(int stackId) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final T stack = mActivityDisplays.get(i).getStack(stackId);
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ private void deferUpdateRecentsHomeStackBounds() {
+ mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME);
}
- /** @see ActivityDisplay#getStack(int, int) */
- private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
- if (stack != null) {
- return stack;
- }
- }
- return null;
- }
-
- int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord task) {
- // Preference is given to the activity type for the activity then the task since the type
- // once set shouldn't change.
- int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
- if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
- activityType = task.getActivityType();
- }
- if (activityType != ACTIVITY_TYPE_UNDEFINED) {
- return activityType;
- }
- if (options != null) {
- activityType = options.getLaunchActivityType();
- }
- return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
- }
-
- <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
- @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
- return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
- }
-
- /**
- * Returns the right stack to use for launching factoring in all the input parameters.
- *
- * @param r The activity we are trying to launch. Can be null.
- * @param options The activity options used to the launch. Can be null.
- * @param candidateTask The possible task the activity might be launched in. Can be null.
- * @params launchParams The resolved launch params to use.
- *
- * @return The stack to use for the launch or INVALID_STACK_ID.
- */
- <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
- @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
- int taskId = INVALID_TASK_ID;
- int displayId = INVALID_DISPLAY;
- //Rect bounds = null;
-
- // We give preference to the launch preference in activity options.
- if (options != null) {
- taskId = options.getLaunchTaskId();
- displayId = options.getLaunchDisplayId();
- }
-
- // First preference for stack goes to the task Id set in the activity options. Use the stack
- // associated with that if possible.
- if (taskId != INVALID_TASK_ID) {
- // Temporarily set the task id to invalid in case in re-entry.
- options.setLaunchTaskId(INVALID_TASK_ID);
- final TaskRecord task = anyTaskForIdLocked(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
- options.setLaunchTaskId(taskId);
- if (task != null) {
- return task.getStack();
- }
- }
-
- final int activityType = resolveActivityType(r, options, candidateTask);
- T stack;
-
- // Next preference for stack goes to the display Id set the candidate display.
- if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
- displayId = launchParams.mPreferredDisplayId;
- }
- if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
- if (r != null) {
- stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
- launchParams);
- if (stack != null) {
- return stack;
- }
- }
- final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
- if (display != null) {
- stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
- if (stack != null) {
- return stack;
- }
- }
- }
-
- // Give preference to the stack and display of the input task and activity if they match the
- // mode we want to launch into.
- stack = null;
- ActivityDisplay display = null;
- if (candidateTask != null) {
- stack = candidateTask.getStack();
- }
- if (stack == null && r != null) {
- stack = r.getStack();
- }
- if (stack != null) {
- display = stack.getDisplay();
- if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
- int windowingMode = launchParams != null ? launchParams.mWindowingMode
- : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- windowingMode = display.resolveWindowingMode(r, options, candidateTask,
- activityType);
- }
- if (stack.isCompatible(windowingMode, activityType)) {
- return stack;
- }
- if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
- && display.getSplitScreenPrimaryStack() == stack
- && candidateTask == stack.topTask()) {
- // This is a special case when we try to launch an activity that is currently on
- // top of split-screen primary stack, but is targeting split-screen secondary.
- // In this case we don't want to move it to another stack.
- // TODO(b/78788972): Remove after differentiating between preferred and required
- // launch options.
- return stack;
- }
- }
- }
-
- if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
- display = getDefaultDisplay();
- }
-
- return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
- }
-
- /** @return true if activity record is null or can be launched on provided display. */
- private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
- if (r == null) {
- return true;
- }
- return r.canBeLaunchedOnDisplay(displayId);
- }
-
- /**
- * Get a topmost stack on the display, that is a valid launch stack for specified activity.
- * If there is no such stack, new dynamic stack can be created.
- * @param displayId Target display.
- * @param r Activity that should be launched there.
- * @param candidateTask The possible task the activity might be put in.
- * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
- */
- private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
- @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- throw new IllegalArgumentException(
- "Display with displayId=" + displayId + " not found.");
- }
-
- if (!r.canBeLaunchedOnDisplay(displayId)) {
- return null;
- }
-
- // If {@code r} is already in target display and its task is the same as the candidate task,
- // the intention should be getting a launch stack for the reusable activity, so we can use
- // the existing stack.
- if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
- return candidateTask.getStack();
- }
-
- // Return the topmost valid stack on the display.
- for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = activityDisplay.getChildAt(i);
- if (isValidLaunchStack(stack, displayId, r)) {
- return stack;
- }
- }
-
- // If there is no valid stack on the external display - check if new dynamic stack will do.
- if (displayId != DEFAULT_DISPLAY) {
- final int windowingMode;
- if (launchParams != null) {
- // When launch params is not null, we always defer to its windowing mode. Sometimes
- // it could be unspecified, which indicates it should inherit windowing mode from
- // display.
- windowingMode = launchParams.mWindowingMode;
- } else {
- windowingMode = options != null ? options.getLaunchWindowingMode()
- : r.getWindowingMode();
- }
- final int activityType =
- options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
- ? options.getLaunchActivityType() : r.getActivityType();
- return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
- }
-
- Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
- return null;
- }
-
- ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
- @Nullable ActivityOptions options,
- @Nullable LaunchParamsController.LaunchParams launchParams) {
- return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
- launchParams);
- }
-
- // TODO: Can probably be consolidated into getLaunchStack()...
- private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
- switch (stack.getActivityType()) {
- case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
- case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
- case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
- }
- // There is a 1-to-1 relationship between stack and task when not in
- // primary split-windowing mode.
- if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return false;
- } else {
- return r.supportsSplitScreenWindowingMode();
- }
- }
-
- /**
- * Get next focusable stack in the system. This will search through the stack on the same
- * display as the current focused stack, looking for a focusable and visible stack, different
- * from the target stack. If no valid candidates will be found, it will then go through all
- * displays and stacks in last-focused order.
- *
- * @param currentFocus The stack that previously had focus.
- * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
- * candidate.
- * @return Next focusable {@link ActivityStack}, {@code null} if not found.
- */
- ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus,
- boolean ignoreCurrent) {
- // First look for next focusable stack on the same display
- final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
- final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
- currentFocus, ignoreCurrent);
- if (preferredFocusableStack != null) {
- return preferredFocusableStack;
- }
- if (preferredDisplay.supportsSystemDecorations()) {
- // Stop looking for focusable stack on other displays because the preferred display
- // supports system decorations. Home activity would be launched on the same display if
- // no focusable stack found.
- return null;
- }
-
- // Now look through all displays
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- if (display == preferredDisplay) {
- // We've already checked this one
- continue;
- }
- final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
- ignoreCurrent);
- if (nextFocusableStack != null) {
- return nextFocusableStack;
- }
- }
-
- return null;
- }
-
- /**
- * Get next valid stack for launching provided activity in the system. This will search across
- * displays and stacks in last-focused order for a focusable and visible stack, except those
- * that are on a currently focused display.
- *
- * @param r The activity that is being launched.
- * @param currentFocus The display that previously had focus and thus needs to be ignored when
- * searching for the next candidate.
- * @return Next valid {@link ActivityStack}, null if not found.
- */
- ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- if (display.mDisplayId == currentFocus) {
- continue;
- }
- final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
- null /* options */, null /* launchParams */);
- if (stack != null) {
- return stack;
- }
- }
- return null;
- }
-
- ActivityRecord getDefaultDisplayHomeActivity() {
- return getDefaultDisplayHomeActivityForUser(mCurrentUser);
- }
-
- ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
- return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
- }
-
- void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
- Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
- boolean deferResume) {
-
- if (stack.inSplitScreenPrimaryWindowingMode()) {
- resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
- preserveWindows, deferResume);
- return;
- }
-
- final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
- if (!allowResizeInDockedMode
- && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
- // If the docked stack exists, don't resize non-floating stacks independently of the
- // size computed from the docked stack size (otherwise they will be out of sync)
- return;
- }
-
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
- mWindowManager.deferSurfaceLayout();
- try {
- if (stack.affectedBySplitScreenResize()) {
- if (bounds == null && stack.inSplitScreenWindowingMode()) {
- // null bounds = fullscreen windowing mode...at least for now.
- stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- } else if (splitScreenActive) {
- // If we are in split-screen mode and this stack support split-screen, then
- // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
- stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- }
- }
- stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
- if (!deferResume) {
- stack.ensureVisibleActivitiesConfigurationLocked(
- stack.topRunningActivityLocked(), preserveWindows);
- }
- } finally {
- mWindowManager.continueSurfaceLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
- }
- }
-
- void deferUpdateRecentsHomeStackBounds() {
- deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
- deferUpdateBounds(ACTIVITY_TYPE_HOME);
- }
-
- void deferUpdateBounds(int activityType) {
- final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- stack.deferUpdateBounds();
- }
- }
-
- void continueUpdateRecentsHomeStackBounds() {
- continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
- continueUpdateBounds(ACTIVITY_TYPE_HOME);
- }
-
- void continueUpdateBounds(int activityType) {
- final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- stack.continueUpdateBounds();
- }
+ private void continueUpdateRecentsHomeStackBounds() {
+ mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+ mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME);
}
void notifyAppTransitionDone() {
continueUpdateRecentsHomeStackBounds();
for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
final int taskId = mResizingTasksDuringAnimation.valueAt(i);
- final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
+ final TaskRecord task =
+ mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY);
if (task != null) {
task.setTaskDockedResizing(false);
}
@@ -2758,7 +1467,8 @@
try {
final int windowingMode = fromStack.getWindowingMode();
final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
- final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
+ final ActivityDisplay toDisplay =
+ mRootActivityContainer.getActivityDisplay(toDisplayId);
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// Tell the display we are exiting split-screen mode.
@@ -2815,8 +1525,8 @@
}
}
- ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
@@ -2862,7 +1572,7 @@
false /* deferResume */);
}
- private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+ void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
boolean preserveWindows, boolean deferResume) {
@@ -2871,7 +1581,8 @@
return;
}
- final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack stack =
+ mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
if (stack == null) {
Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
return;
@@ -2910,7 +1621,7 @@
// static stacks need to be adjusted so they don't overlap with the docked stack.
// We get the bounds to use from window manager which has been adjusted for any
// screen controls and is also the same for all stacks.
- final ActivityDisplay display = getDefaultDisplay();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
final Rect otherTaskRect = new Rect();
for (int i = display.getChildCount() - 1; i >= 0; --i) {
final ActivityStack current = display.getChildAt(i);
@@ -2930,7 +1641,8 @@
tempRect /* outStackBounds */,
otherTaskRect /* outTempTaskBounds */);
- resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
+ mRootActivityContainer.resizeStack(current,
+ !tempRect.isEmpty() ? tempRect : null,
!otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
true /* allowResizeInDockedMode */, deferResume);
@@ -2948,7 +1660,8 @@
void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
// TODO(multi-display): Pinned stack display should be passed in.
- final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack();
+ final PinnedActivityStack stack =
+ mRootActivityContainer.getDefaultDisplay().getPinnedStack();
if (stack == null) {
Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
return;
@@ -3029,22 +1742,6 @@
}
/**
- * Removes stacks in the input windowing modes from the system if they are of activity type
- * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
- */
- void removeStacksInWindowingModes(int... windowingModes) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
- }
- }
-
- void removeStacksWithActivityTypes(int... activityTypes) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
- }
- }
-
- /**
* See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
*/
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
@@ -3065,7 +1762,8 @@
*/
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
boolean pauseImmediately, String reason) {
- final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ final TaskRecord tr =
+ mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
tr.removeTaskActivitiesLocked(pauseImmediately, reason);
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
@@ -3154,7 +1852,8 @@
* @return true if the task has been restored successfully.
*/
boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) {
- final ActivityStack stack = getLaunchStack(null, aOptions, task, onTop);
+ final ActivityStack stack =
+ mRootActivityContainer.getLaunchStack(null, aOptions, task, onTop);
final ActivityStack currentStack = task.getStack();
if (currentStack != null) {
// Task has already been restored once. See if we need to do anything more
@@ -3174,7 +1873,7 @@
"Added restored task=" + task + " to stack=" + stack);
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- activities.get(activityNdx).createWindowContainer();
+ activities.get(activityNdx).createAppWindowToken();
}
return true;
}
@@ -3196,39 +1895,6 @@
}
/**
- * Move stack with all its existing content to specified display.
- * @param stackId Id of stack to move.
- * @param displayId Id of display to move stack to.
- * @param onTop Indicates whether container should be place on top or on bottom.
- */
- void moveStackToDisplayLocked(int stackId, int displayId, boolean onTop) {
- final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
- if (activityDisplay == null) {
- throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
- + displayId);
- }
- final ActivityStack stack = getStack(stackId);
- if (stack == null) {
- throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
- + stackId);
- }
-
- final ActivityDisplay currentDisplay = stack.getDisplay();
- if (currentDisplay == null) {
- throw new IllegalStateException("moveStackToDisplayLocked: Stack with stack=" + stack
- + " is not attached to any display.");
- }
-
- if (currentDisplay.mDisplayId == displayId) {
- throw new IllegalArgumentException("Trying to move stack=" + stack
- + " to its current displayId=" + displayId);
- }
-
- stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
- // TODO(multi-display): resize stacks properly if moved from split-screen.
- }
-
- /**
* Returns the reparent target stack, creating the stack if necessary. This call also enforces
* the various checks on tasks that are going to be reparented from one stack to another.
*/
@@ -3280,159 +1946,6 @@
return stack;
}
- boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
- final ActivityStack stack = getStack(stackId);
- if (stack == null) {
- throw new IllegalArgumentException(
- "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
- }
-
- final ActivityRecord r = stack.topRunningActivityLocked();
- if (r == null) {
- Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
- + " in stack=" + stack);
- return false;
- }
-
- if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
- Slog.w(TAG,
- "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
- + " r=" + r);
- return false;
- }
-
- moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
- "moveTopActivityToPinnedStack");
- return true;
- }
-
- void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
- String reason) {
-
- mWindowManager.deferSurfaceLayout();
-
- final ActivityDisplay display = r.getStack().getDisplay();
- PinnedActivityStack stack = display.getPinnedStack();
-
- // This will clear the pinned stack by moving an existing task to the full screen stack,
- // ensuring only one task is present.
- if (stack != null) {
- moveTasksToFullscreenStackLocked(stack, !ON_TOP);
- }
-
- // Need to make sure the pinned stack exist so we can resize it below...
- stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
-
- // Calculate the target bounds here before the task is reparented back into pinned windowing
- // mode (which will reset the saved bounds)
- final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
- try {
- final TaskRecord task = r.getTask();
- // Resize the pinned stack to match the current size of the task the activity we are
- // going to be moving is currently contained in. We do this to have the right starting
- // animation bounds for the pinned stack to the desired bounds the caller wants.
- resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
- null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
- true /* allowResizeInDockedMode */, !DEFER_RESUME);
-
- if (task.mActivities.size() == 1) {
- // Defer resume until below, and do not schedule PiP changes until we animate below
- task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
- false /* schedulePictureInPictureModeChange */, reason);
- } else {
- // There are multiple activities in the task and moving the top activity should
- // reveal/leave the other activities in their original task.
-
- // Currently, we don't support reparenting activities across tasks in two different
- // stacks, so instead, just create a new task in the same stack, reparent the
- // activity into that task, and then reparent the whole task to the new stack. This
- // ensures that all the necessary work to migrate states in the old and new stacks
- // is also done.
- final TaskRecord newTask = task.getStack().createTaskRecord(
- getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true);
- r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
-
- // Defer resume until below, and do not schedule PiP changes until we animate below
- newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
- DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
- }
-
- // Reset the state that indicates it can enter PiP while pausing after we've moved it
- // to the pinned stack
- r.supportsEnterPipOnTaskSwitch = false;
- } finally {
- mWindowManager.continueSurfaceLayout();
- }
-
- stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
- true /* fromFullscreen */);
-
- // Update the visibility of all activities after the they have been reparented to the new
- // stack. This MUST run after the animation above is scheduled to ensure that the windows
- // drawn signal is scheduled after the bounds animation start call on the bounds animator
- // thread.
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- resumeFocusedStacksTopActivitiesLocked();
-
- mService.getTaskChangeNotificationController().notifyActivityPinned(r);
- }
-
- ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
- mTmpFindTaskResult.clear();
-
- // Looking up task on preferred display first
- final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
- if (preferredDisplay != null) {
- preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
- if (mTmpFindTaskResult.mIdealMatch) {
- return mTmpFindTaskResult.mRecord;
- }
- }
-
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- if (display.mDisplayId == preferredDisplayId) {
- continue;
- }
-
- display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
- if (mTmpFindTaskResult.mIdealMatch) {
- return mTmpFindTaskResult.mRecord;
- }
- }
-
- if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
- return mTmpFindTaskResult.mRecord;
- }
-
- ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
- boolean compareIntentFilters) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord ar = stack.findActivityLocked(
- intent, info, compareIntentFilters);
- if (ar != null) {
- return ar;
- }
- }
- }
- return null;
- }
-
- boolean hasAwakeDisplay() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- if (!display.shouldSleep()) {
- return true;
- }
- }
- return false;
- }
-
void goingToSleepLocked() {
scheduleSleepTimeout();
if (!mGoingToSleep.isHeld()) {
@@ -3446,24 +1959,19 @@
}
}
- applySleepTokensLocked(false /* applyToStacks */);
+ mRootActivityContainer.applySleepTokens(false /* applyToStacks */);
checkReadyForSleepLocked(true /* allowDelay */);
}
- void prepareForShutdownLocked() {
- for (int i = 0; i < mActivityDisplays.size(); i++) {
- createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId);
- }
- }
-
boolean shutdownLocked(int timeout) {
goingToSleepLocked();
boolean timedout = false;
final long endTime = System.currentTimeMillis() + timeout;
while (true) {
- if (!putStacksToSleepLocked(true /* allowDelay */, true /* shuttingDown */)) {
+ if (!mRootActivityContainer.putStacksToSleep(
+ true /* allowDelay */, true /* shuttingDown */)) {
long timeRemaining = endTime - System.currentTimeMillis();
if (timeRemaining > 0) {
try {
@@ -3493,54 +2001,9 @@
}
}
- void applySleepTokensLocked(boolean applyToStacks) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- // Set the sleeping state of the display.
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final boolean displayShouldSleep = display.shouldSleep();
- if (displayShouldSleep == display.isSleeping()) {
- continue;
- }
- display.setIsSleeping(displayShouldSleep);
-
- if (!applyToStacks) {
- continue;
- }
-
- // Set the sleeping state of the stacks on the display.
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (displayShouldSleep) {
- stack.goToSleepIfPossible(false /* shuttingDown */);
- } else {
- stack.awakeFromSleepingLocked();
- if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
- .isKeyguardOrAodShowing(display.mDisplayId)) {
- // If the keyguard is unlocked - resume immediately.
- // It is possible that the display will not be awake at the time we
- // process the keyguard going away, which can happen before the sleep token
- // is released. As a result, it is important we resume the activity here.
- resumeFocusedStacksTopActivitiesLocked();
- }
- }
- }
-
- if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) {
- continue;
- }
- // The display is awake now, so clean up the going to sleep list.
- for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) {
- final ActivityRecord r = it.next();
- if (r.getDisplayId() == display.mDisplayId) {
- it.remove();
- }
- }
- }
- }
-
void activitySleptLocked(ActivityRecord r) {
mGoingToSleepActivities.remove(r);
- final ActivityStack s = r.getStack();
+ final ActivityStack s = r.getActivityStack();
if (s != null) {
s.checkReadyForSleep();
} else {
@@ -3554,12 +2017,13 @@
return;
}
- if (!putStacksToSleepLocked(allowDelay, false /* shuttingDown */)) {
+ if (!mRootActivityContainer.putStacksToSleep(
+ allowDelay, false /* shuttingDown */)) {
return;
}
// Send launch end powerhint before going sleep
- sendPowerHintForLaunchEndIfNeeded();
+ mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
removeSleepTimeouts();
@@ -3571,55 +2035,27 @@
}
}
- // Tries to put all activity stacks to sleep. Returns true if all stacks were
- // successfully put to sleep.
- private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) {
- boolean allSleep = true;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (allowDelay) {
- allSleep &= stack.goToSleepIfPossible(shuttingDown);
- } else {
- stack.goToSleep();
- }
- }
- }
- return allSleep;
- }
-
boolean reportResumedActivityLocked(ActivityRecord r) {
// A resumed activity cannot be stopping. remove from list
mStoppingActivities.remove(r);
- final ActivityStack stack = r.getStack();
- if (isTopDisplayFocusedStack(stack)) {
+ final ActivityStack stack = r.getActivityStack();
+ if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
mService.updateUsageStats(r, true);
}
if (stack.getDisplay().allResumedActivitiesComplete()) {
- ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// Make sure activity & window visibility should be identical
// for all displays in this stage.
- executeAppTransitionForAllDisplay();
+ mRootActivityContainer.executeAppTransitionForAllDisplay();
return true;
}
return false;
}
- void handleAppCrashLocked(WindowProcessController app) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.handleAppCrashLocked(app);
- }
- }
- }
-
// Called when WindowManager has finished animating the launchingBehind activity to the back.
private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
final ActivityStack stack = task.getStack();
r.mLaunchTaskBehind = false;
@@ -3631,7 +2067,7 @@
// task has been shown briefly
final ActivityRecord top = stack.getTopActivity();
if (top != null) {
- top.getTask().touchActiveTime();
+ top.getTaskRecord().touchActiveTime();
}
}
@@ -3639,157 +2075,9 @@
mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
}
- /**
- * Make sure that all activities that need to be visible in the system actually are and update
- * their configuration.
- */
- void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
- boolean preserveWindows) {
- ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
- true /* notifyClients */);
- }
-
- /**
- * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
- */
- void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
- boolean preserveWindows, boolean notifyClients) {
- getKeyguardController().beginActivityVisibilityUpdate();
- try {
- // First the front stacks. In case any are not fullscreen and are in front of home.
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
- notifyClients);
- }
- }
- } finally {
- getKeyguardController().endActivityVisibilityUpdate();
- }
- }
-
- void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.addStartingWindowsForVisibleActivities(taskSwitch);
- }
- }
- }
-
- void invalidateTaskLayers() {
- mTaskLayersChanged = true;
- }
-
- void rankTaskLayersIfNeeded() {
- if (!mTaskLayersChanged) {
- return;
- }
- mTaskLayersChanged = false;
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- int baseLayer = 0;
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- baseLayer += stack.rankTaskLayers(baseLayer);
- }
- }
- }
-
- void clearOtherAppTimeTrackers(AppTimeTracker except) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.clearOtherAppTimeTrackers(except);
- }
- }
- }
-
- void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.scheduleDestroyActivities(app, reason);
- }
- }
- }
-
- void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
- // Tasks is non-null only if two or more tasks are found.
- ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
- if (tasks == null) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
- return;
- }
- // If we have activities in multiple tasks that are in a position to be destroyed,
- // let's iterate through the tasks and release the oldest one.
- final int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final int stackCount = display.getChildCount();
- // Step through all stacks starting from behind, to hit the oldest things first.
- for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- // Try to release activities in this stack; if we manage to, we are done.
- if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
- return;
- }
- }
- }
- }
-
- boolean switchUserLocked(int userId, UserState uss) {
- final int focusStackId = getTopDisplayFocusedStack().getStackId();
- // We dismiss the docked stack whenever we switch users.
- final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
- if (dockedStack != null) {
- moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
- }
- // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
- // also cause all tasks to be moved to the fullscreen stack at a position that is
- // appropriate.
- removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
- mUserStackInFront.put(mCurrentUser, focusStackId);
- final int restoreStackId =
- mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
- mCurrentUser = userId;
-
- mStartingUsers.add(uss);
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.switchUserLocked(userId);
- TaskRecord task = stack.topTask();
- if (task != null) {
- stack.positionChildWindowContainerAtTop(task);
- }
- }
- }
-
- ActivityStack stack = getStack(restoreStackId);
- if (stack == null) {
- stack = getDefaultDisplay().getHomeStack();
- }
- final boolean homeInFront = stack.isActivityTypeHome();
- if (stack.isOnHomeDisplay()) {
- stack.moveToFront("switchUserOnHomeDisplay");
- } else {
- // Stack was moved to another display while user was swapped out.
- resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
- }
- return homeInFront;
- }
-
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
- if (userId == mCurrentUser) return true;
+ if (userId == mRootActivityContainer.mCurrentUser) return true;
return mService.mAmInternal.isCurrentProfile(userId);
}
@@ -3814,7 +2102,7 @@
boolean remove, boolean processPausingActivities) {
ArrayList<ActivityRecord> stops = null;
- final boolean nowVisible = allResumedActivitiesVisible();
+ final boolean nowVisible = mRootActivityContainer.allResumedActivitiesVisible();
for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord s = mStoppingActivities.get(activityNdx);
boolean waitingVisible = mActivitiesWaitingForVisibleActivity.contains(s);
@@ -3834,7 +2122,7 @@
}
}
if (remove) {
- final ActivityStack stack = s.getStack();
+ final ActivityStack stack = s.getActivityStack();
final boolean shouldSleepOrShutDown = stack != null
? stack.shouldSleepOrShutDownActivities()
: mService.isSleepingOrShuttingDownLocked();
@@ -3864,134 +2152,26 @@
return stops;
}
- void validateTopActivitiesLocked() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord r = stack.topRunningActivityLocked();
- final ActivityState state = r == null ? DESTROYED : r.getState();
- if (isTopDisplayFocusedStack(stack)) {
- if (r == null) Slog.e(TAG,
- "validateTop...: null top activity, stack=" + stack);
- else {
- final ActivityRecord pausing = stack.mPausingActivity;
- if (pausing != null && pausing == r) Slog.e(TAG,
- "validateTop...: top stack has pausing activity r=" + r
- + " state=" + state);
- if (state != INITIALIZING && state != RESUMED) Slog.e(TAG,
- "validateTop...: activity in front not resumed r=" + r
- + " state=" + state);
- }
- } else {
- final ActivityRecord resumed = stack.getResumedActivity();
- if (resumed != null && resumed == r) Slog.e(TAG,
- "validateTop...: back stack has resumed activity r=" + r
- + " state=" + state);
- if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG,
- "validateTop...: activity in back resumed r=" + r + " state=" + state);
- }
- }
- }
- }
-
- public void dumpDisplays(PrintWriter pw) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- pw.print("[id:" + display.mDisplayId + " stacks:");
- display.dumpStacks(pw);
- pw.print("]");
- }
- }
-
public void dump(PrintWriter pw, String prefix) {
pw.println();
pw.println("ActivityStackSupervisor state:");
- pw.print(prefix);
- pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+ mRootActivityContainer.dump(pw, prefix);
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
- pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- display.dump(pw, prefix);
- }
+ pw.println(prefix + "mUserStackInFront=" + mRootActivityContainer.mUserStackInFront);
if (!mWaitingForActivityVisible.isEmpty()) {
- pw.print(prefix); pw.println("mWaitingForActivityVisible=");
+ pw.println(prefix + "mWaitingForActivityVisible=");
for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
- pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
+ pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
}
}
pw.print(prefix); pw.print("isHomeRecentsComponent=");
- pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+ pw.print(mRecentTasks.isRecentsComponentHomeActivity(mRootActivityContainer.mCurrentUser));
getKeyguardController().dump(pw, prefix);
mService.getLockTaskController().dump(pw, prefix);
}
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
- activityDisplay.writeToProto(proto, DISPLAYS);
- }
- getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
- // TODO(b/111541062): Update tests to look for resumed activities on all displays
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- if (focusedStack != null) {
- proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
- final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
- if (focusedActivity != null) {
- focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
- }
- } else {
- proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
- }
- proto.write(IS_HOME_RECENTS_COMPONENT,
- mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
- mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
- proto.end(token);
- }
-
- /**
- * Dump all connected displays' configurations.
- * @param prefix Prefix to apply to each line of the dump.
- */
- void dumpDisplayConfigs(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.println("Display override configurations:");
- final int displayCount = mActivityDisplays.size();
- for (int i = 0; i < displayCount; i++) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
- pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
- pw.println(activityDisplay.getOverrideConfiguration());
- }
- }
-
- /**
- * Dumps the activities matching the given {@param name} in the either the focused stack
- * or all visible stacks if {@param dumpVisibleStacks} is true.
- */
- ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
- boolean dumpFocusedStackOnly) {
- if (dumpFocusedStackOnly) {
- return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
- } else {
- ArrayList<ActivityRecord> activities = new ArrayList<>();
- int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
- activities.addAll(stack.getDumpActivitiesLocked(name));
- }
- }
- }
- return activities;
- }
- }
-
static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
boolean needSep, String prefix) {
if (activity != null) {
@@ -4007,73 +2187,6 @@
return false;
}
- boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
- boolean dumpClient, String dumpPackage) {
- boolean printed = false;
- boolean needSep = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
- pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
- pw.println(" (activities from top to bottom):");
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- pw.println();
- pw.println(" Stack #" + stack.mStackId
- + ": type=" + activityTypeToString(stack.getActivityType())
- + " mode=" + windowingModeToString(stack.getWindowingMode()));
- pw.println(" isSleeping=" + stack.shouldSleepActivities());
- pw.println(" mBounds=" + stack.getOverrideBounds());
-
- printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
- needSep);
-
- printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false,
- !dumpAll, false, dumpPackage, true,
- " Running activities (most recent first):", null);
-
- needSep = printed;
- boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
- " mPausingActivity: ");
- if (pr) {
- printed = true;
- needSep = false;
- }
- pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
- " mResumedActivity: ");
- if (pr) {
- printed = true;
- needSep = false;
- }
- if (dumpAll) {
- pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
- " mLastPausedActivity: ");
- if (pr) {
- printed = true;
- needSep = true;
- }
- printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
- needSep, " mLastNoHistoryActivity: ");
- }
- needSep = printed;
- }
- printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
- " ResumedActivity:");
- }
-
- printed |= dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll,
- false, dumpPackage, true, " Activities waiting to finish:", null);
- printed |= dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll,
- false, dumpPackage, true, " Activities waiting to stop:", null);
- printed |= dumpHistoryList(fd, pw, mActivitiesWaitingForVisibleActivity, " ", "Wait",
- false, !dumpAll, false, dumpPackage, true,
- " Activities waiting for another to become visible:", null);
- printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll,
- false, dumpPackage, true, " Activities waiting to sleep:", null);
-
- return printed;
- }
-
static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
String prefix, String label, boolean complete, boolean brief, boolean client,
String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
@@ -4099,8 +2212,8 @@
pw.println(header);
header = null;
}
- if (lastTask != r.getTask()) {
- lastTask = r.getTask();
+ if (lastTask != r.getTaskRecord()) {
+ lastTask = r.getTaskRecord();
pw.print(prefix);
pw.print(full ? "* " : " ");
pw.println(lastTask);
@@ -4183,294 +2296,6 @@
mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
}
- @Override
- public void onDisplayAdded(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
- synchronized (mService.mGlobalLock) {
- getActivityDisplayOrCreateLocked(displayId);
- // Do not start home before booting, or it may accidentally finish booting before it
- // starts. Instead, we expect home activities to be launched when the system is ready
- // (ActivityManagerService#systemReady).
- if (mService.isBooted() || mService.isBooting()) {
- startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
- }
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
- if (displayId == DEFAULT_DISPLAY) {
- throw new IllegalArgumentException("Can't remove the primary display.");
- }
-
- synchronized (mService.mGlobalLock) {
- final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
- if (activityDisplay == null) {
- return;
- }
-
- activityDisplay.remove();
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
- synchronized (mService.mGlobalLock) {
- final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
- if (activityDisplay != null) {
- activityDisplay.onDisplayChanged();
- }
- }
- }
-
- /** Check if display with specified id is added to the list. */
- boolean isDisplayAdded(int displayId) {
- return getActivityDisplayOrCreateLocked(displayId) != null;
- }
-
- // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
- ActivityDisplay getActivityDisplay(int displayId) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
- if (activityDisplay.mDisplayId == displayId) {
- return activityDisplay;
- }
- }
- return null;
- }
-
- // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
- ActivityDisplay getDefaultDisplay() {
- return mDefaultDisplay;
- }
-
- /**
- * Get an existing instance of {@link ActivityDisplay} or create new if there is a
- * corresponding record in display manager.
- */
- // TODO: Look into consolidating with getActivityDisplay()
- ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
- ActivityDisplay activityDisplay = getActivityDisplay(displayId);
- if (activityDisplay != null) {
- return activityDisplay;
- }
- if (mDisplayManager == null) {
- // The system isn't fully initialized yet.
- return null;
- }
- final Display display = mDisplayManager.getDisplay(displayId);
- if (display == null) {
- // The display is not registered in DisplayManager.
- return null;
- }
- // The display hasn't been added to ActivityManager yet, create a new record now.
- activityDisplay = new ActivityDisplay(this, display);
- addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
- return activityDisplay;
- }
-
- /**
- * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is
- * defined in {@link DisplayInfo#uniqueId}.
- *
- * @param uniqueId the unique ID of the display
- * @return the {@link ActivityDisplay} or {@code null} if nothing is found.
- */
- ActivityDisplay getActivityDisplay(String uniqueId) {
- for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- final boolean isValid = display.mDisplay.isValid();
- if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
- return display;
- }
- }
-
- return null;
- }
-
- boolean startHomeOnAllDisplays(int userId, String reason) {
- boolean homeStarted = false;
- for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
- final int displayId = mActivityDisplays.get(i).mDisplayId;
- homeStarted |= startHomeOnDisplay(userId, reason, displayId);
- }
- return homeStarted;
- }
-
- /**
- * This starts home activity on displays that can have system decorations and only if the
- * home activity can have multiple instances.
- */
- boolean startHomeOnDisplay(int userId, String reason, int displayId) {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
- if (aInfo == null) {
- return false;
- }
-
- if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
- return false;
- }
-
- // Update the reason for ANR debugging to verify if the user activity is the one that
- // actually launched.
- final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
- aInfo.applicationInfo.uid);
- mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
- displayId);
- return true;
- }
-
- /**
- * This resolves the home activity info and updates the home component of the given intent.
- * @return the home activity info if any.
- */
- private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
- final int flags = ActivityManagerService.STOCK_PM_FLAGS;
- final ComponentName comp = homeIntent.getComponent();
- ActivityInfo aInfo = null;
- try {
- if (comp != null) {
- // Factory test.
- aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
- } else {
- final String resolvedType =
- homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
- final ResolveInfo info = AppGlobals.getPackageManager()
- .resolveIntent(homeIntent, resolvedType, flags, userId);
- if (info != null) {
- aInfo = info.activityInfo;
- }
- }
- } catch (RemoteException e) {
- // ignore
- }
-
- if (aInfo == null) {
- Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
- return null;
- }
-
- homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
- aInfo = new ActivityInfo(aInfo);
- aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
- homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
- return aInfo;
- }
-
- @VisibleForTesting
- void addChild(ActivityDisplay activityDisplay, int position) {
- positionChildAt(activityDisplay, position);
- mWindowContainerController.positionChildAt(
- activityDisplay.getWindowContainerController(), position);
- }
-
- void removeChild(ActivityDisplay activityDisplay) {
- // The caller must tell the controller of {@link ActivityDisplay} to release its container
- // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
- mActivityDisplays.remove(activityDisplay);
- }
-
- private void calculateDefaultMinimalSizeOfResizeableTasks() {
- final Resources res = mService.mContext.getResources();
- final float minimalSize = res.getDimension(
- com.android.internal.R.dimen.default_minimal_size_resizable_task);
- final DisplayMetrics dm = res.getDisplayMetrics();
-
- mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
- }
-
- SleepToken createSleepTokenLocked(String tag, int displayId) {
- final ActivityDisplay display = getActivityDisplay(displayId);
- if (display == null) {
- throw new IllegalArgumentException("Invalid display: " + displayId);
- }
-
- final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
- mSleepTokens.add(token);
- display.mAllSleepTokens.add(token);
- return token;
- }
-
- private void removeSleepTokenLocked(SleepTokenImpl token) {
- mSleepTokens.remove(token);
-
- final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
- if (display != null) {
- display.mAllSleepTokens.remove(token);
- if (display.mAllSleepTokens.isEmpty()) {
- mService.updateSleepIfNeededLocked();
- }
- }
- }
-
- private StackInfo getStackInfo(ActivityStack stack) {
- final int displayId = stack.mDisplayId;
- final ActivityDisplay display = getActivityDisplay(displayId);
- StackInfo info = new StackInfo();
- stack.getWindowContainerBounds(info.bounds);
- info.displayId = displayId;
- info.stackId = stack.mStackId;
- info.userId = stack.mCurrentUser;
- info.visible = stack.shouldBeVisible(null);
- // A stack might be not attached to a display.
- info.position = display != null ? display.getIndexOf(stack) : 0;
- info.configuration.setTo(stack.getConfiguration());
-
- ArrayList<TaskRecord> tasks = stack.getAllTasks();
- final int numTasks = tasks.size();
- int[] taskIds = new int[numTasks];
- String[] taskNames = new String[numTasks];
- Rect[] taskBounds = new Rect[numTasks];
- int[] taskUserIds = new int[numTasks];
- for (int i = 0; i < numTasks; ++i) {
- final TaskRecord task = tasks.get(i);
- taskIds[i] = task.taskId;
- taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
- : task.realActivity != null ? task.realActivity.flattenToString()
- : task.getTopActivity() != null ? task.getTopActivity().packageName
- : "unknown";
- taskBounds[i] = new Rect();
- task.getWindowContainerBounds(taskBounds[i]);
- taskUserIds[i] = task.userId;
- }
- info.taskIds = taskIds;
- info.taskNames = taskNames;
- info.taskBounds = taskBounds;
- info.taskUserIds = taskUserIds;
-
- final ActivityRecord top = stack.topRunningActivityLocked();
- info.topActivity = top != null ? top.intent.getComponent() : null;
- return info;
- }
-
- StackInfo getStackInfo(int stackId) {
- ActivityStack stack = getStack(stackId);
- if (stack != null) {
- return getStackInfo(stack);
- }
- return null;
- }
-
- StackInfo getStackInfo(int windowingMode, int activityType) {
- final ActivityStack stack = getStack(windowingMode, activityType);
- return (stack != null) ? getStackInfo(stack) : null;
- }
-
- ArrayList<StackInfo> getAllStackInfosLocked() {
- ArrayList<StackInfo> list = new ArrayList<>();
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- list.add(getStackInfo(stack));
- }
- }
- return list;
- }
-
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
int preferredDisplayId, ActivityStack actualStack) {
handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
@@ -4545,7 +2370,7 @@
mWindowManager.notifyAppRelaunchingFinished(token);
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- if (r.getStack().shouldSleepOrShutDownActivities()) {
+ if (r.getActivityStack().shouldSleepOrShutDownActivities()) {
r.setSleeping(true, true);
}
}
@@ -4616,21 +2441,6 @@
}
}
- void setDockedStackMinimized(boolean minimized) {
- // Get currently focused stack before setting mIsDockMinimized. We do this because if
- // split-screen is active, primary stack will not be focusable (see #isFocusable) while
- // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
- final ActivityStack current = getTopDisplayFocusedStack();
- mIsDockMinimized = minimized;
- if (mIsDockMinimized) {
- if (current.inSplitScreenPrimaryWindowingMode()) {
- // The primary split-screen stack can't be focused while it is minimize, so move
- // focus to something else.
- current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
- }
- }
- }
-
void wakeUp(String reason) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason);
}
@@ -4649,10 +2459,8 @@
mDeferResumeCount--;
}
- /**
- * @return True if resume can be called.
- */
- private boolean readyToResume() {
+ /** @return True if resume can be called. */
+ boolean readyToResume() {
return mDeferResumeCount == 0;
}
@@ -4704,7 +2512,7 @@
} break;
case RESUME_TOP_ACTIVITY_MSG: {
synchronized (mService.mGlobalLock) {
- resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
} break;
case SLEEP_TIMEOUT_MSG: {
@@ -4740,19 +2548,6 @@
}
}
- ActivityStack findStackBehind(ActivityStack stack) {
- final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
- if (display != null) {
- for (int i = display.getChildCount() - 1; i >= 0; i--) {
- if (display.getChildAt(i) == stack && i > 0) {
- return display.getChildAt(i - 1);
- }
- }
- }
- throw new IllegalStateException("Failed to find a stack behind stack=" + stack
- + " in=" + display);
- }
-
/**
* Puts a task into resizing mode during the next app transition.
*
@@ -4797,8 +2592,8 @@
mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
}
- task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
- activityOptions, ON_TOP);
+ task = mRootActivityContainer.anyTaskForId(taskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
if (task == null) {
continueUpdateRecentsHomeStackBounds();
mWindowManager.executeAppTransition();
@@ -4811,7 +2606,8 @@
// from whatever is started from the recents activity, so move the home stack
// forward.
// TODO (b/115289124): Multi-display supports for recents.
- getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
+ mRootActivityContainer.getDefaultDisplay().moveHomeStackToFront(
+ "startActivityFromRecents");
}
// If the user must confirm credentials (e.g. when first launching a work app and the
@@ -4820,7 +2616,8 @@
&& task.getRootActivity() != null) {
final ActivityRecord targetActivity = task.getTopActivity();
- sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
+ mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+ true /* forceSend */, targetActivity);
mActivityMetricsLogger.notifyActivityLaunching(task.intent);
try {
mService.moveTaskToFrontLocked(task.taskId, 0, options,
@@ -4873,35 +2670,6 @@
}
/**
- * @return a list of activities which are the top ones in each visible stack. The first
- * entry will be the focused activity.
- */
- List<IBinder> getTopVisibleActivities() {
- final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
- final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
- // Traverse all displays.
- for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
- final ActivityDisplay display = mActivityDisplays.get(i);
- // Traverse all stacks on a display.
- for (int j = display.getChildCount() - 1; j >= 0; --j) {
- final ActivityStack stack = display.getChildAt(j);
- // Get top activity from a visible stack and add it to the list.
- if (stack.shouldBeVisible(null /* starting */)) {
- final ActivityRecord top = stack.getTopActivity();
- if (top != null) {
- if (stack == topFocusedStack) {
- topActivityTokens.add(0, top.appToken);
- } else {
- topActivityTokens.add(top.appToken);
- }
- }
- }
- }
- }
- return topActivityTokens;
- }
-
- /**
* Internal container to store a match qualifier alongside a WaitResult.
*/
static class WaitInfo {
@@ -4939,30 +2707,4 @@
mResult.dump(pw, prefix);
}
}
-
- private final class SleepTokenImpl extends SleepToken {
- private final String mTag;
- private final long mAcquireTime;
- private final int mDisplayId;
-
- public SleepTokenImpl(String tag, int displayId) {
- mTag = tag;
- mDisplayId = displayId;
- mAcquireTime = SystemClock.uptimeMillis();
- }
-
- @Override
- public void release() {
- synchronized (mService.mGlobalLock) {
- removeSleepTokenLocked(this);
- }
- }
-
- @Override
- public String toString() {
- return "{\"" + mTag + "\", display " + mDisplayId
- + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
- }
- }
-
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index ee5a43c..b8442a8 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -70,6 +70,7 @@
private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mSupervisor;
+ private final RootActivityContainer mRootActivityContainer;
private final Context mServiceContext;
// UserManager cannot be final as it's not ready when this class is instantiated during boot
@@ -102,14 +103,15 @@
ActivityStartInterceptor(
ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
- this(service, supervisor, service.mContext);
+ this(service, supervisor, service.mRootActivityContainer, service.mContext);
}
@VisibleForTesting
ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
- Context context) {
+ RootActivityContainer root, Context context) {
mService = service;
mSupervisor = supervisor;
+ mRootActivityContainer = root;
mServiceContext = context;
}
@@ -279,11 +281,11 @@
mActivityOptions = ActivityOptions.makeBasic();
}
- ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
- if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
- // Showing credential confirmation activity in home task to avoid stopping multi-windowed
- // mode after showing the full-screen credential confirmation activity.
- mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId);
+ ActivityRecord homeActivityRecord = mRootActivityContainer.getDefaultDisplayHomeActivity();
+ if (homeActivityRecord != null && homeActivityRecord.getTaskRecord() != null) {
+ // Showing credential confirmation activity in home task to avoid stopping
+ // multi-windowed mode after showing the full-screen credential confirmation activity.
+ mActivityOptions.setLaunchTaskId(homeActivityRecord.getTaskRecord().taskId);
}
final UserInfo parent = mUserManager.getProfileParent(mUserId);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 90f3ff8..bc2136e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -98,6 +98,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -108,6 +109,7 @@
import android.util.EventLog;
import android.util.Pools.SynchronizedPool;
import android.util.Slog;
+import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HeavyWeightSwitcherActivity;
@@ -137,6 +139,7 @@
private static final int INVALID_LAUNCH_MODE = -1;
private final ActivityTaskManagerService mService;
+ private final RootActivityContainer mRootActivityContainer;
private final ActivityStackSupervisor mSupervisor;
private final ActivityStartInterceptor mInterceptor;
private final ActivityStartController mController;
@@ -421,6 +424,7 @@
ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
mController = controller;
mService = service;
+ mRootActivityContainer = service.mRootActivityContainer;
mSupervisor = supervisor;
mInterceptor = interceptor;
reset(true);
@@ -617,7 +621,7 @@
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
if (resultTo != null) {
- sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
+ sourceRecord = mRootActivityContainer.isInAnyStack(resultTo);
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Will send result to " + resultTo + " " + sourceRecord);
if (sourceRecord != null) {
@@ -674,7 +678,7 @@
}
if (err == ActivityManager.START_SUCCESS && sourceRecord != null
- && sourceRecord.getTask().voiceSession != null) {
+ && sourceRecord.getTaskRecord().voiceSession != null) {
// If this activity is being launched as part of a voice session, we need
// to ensure that it is safe to do so. If the upcoming activity will also
// be part of the voice session, we can only launch it if it has explicitly
@@ -714,7 +718,8 @@
}
}
- final ActivityStack resultStack = resultRecord == null ? null : resultRecord.getStack();
+ final ActivityStack resultStack = resultRecord == null
+ ? null : resultRecord.getActivityStack();
if (err != START_SUCCESS) {
if (resultRecord != null) {
@@ -731,6 +736,13 @@
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
+ // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking
+ // on START_ABORTED
+ if (!abort) {
+ abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid,
+ callerApp);
+ }
+
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
@@ -774,6 +786,8 @@
// We pretend to the caller that it was really started, but
// they will just get a cancel result.
ActivityOptions.abort(checkedOptions);
+ maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp,
+ null /*r*/, originatingPendingIntent, true /*abortedStart*/);
return START_ABORTED;
}
@@ -811,7 +825,8 @@
null /*profilerInfo*/);
if (DEBUG_PERMISSIONS_REVIEW) {
- final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack focusedStack =
+ mRootActivityContainer.getTopDisplayFocusedStack();
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
true, false) + "} from uid " + callingUid + " on display "
+ (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
@@ -847,7 +862,7 @@
r.appTimeTracker = sourceRecord.appTimeTracker;
}
- final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
// If we are starting an activity that is not from the same uid as the currently resumed
// one, check whether app switches are allowed.
@@ -866,19 +881,60 @@
mController.doPendingActivityLaunches(false);
maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
- originatingPendingIntent);
+ originatingPendingIntent, false /*abortedStart*/);
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true /* doResume */, checkedOptions, inTask, outActivity);
}
+ private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
+ int realCallingUid, WindowProcessController callerApp) {
+ if (mService.isBackgroundActivityStartsEnabled()) {
+ return false;
+ }
+ // don't abort for the most important UIDs
+ if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
+ return false;
+ }
+ // don't abort if the callerApp has any visible activity
+ if (callerApp != null && callerApp.hasForegroundActivities()) {
+ return false;
+ }
+ // don't abort if the callingUid is in the foreground
+ if (isUidForeground(callingUid)) {
+ return false;
+ }
+ // don't abort if the realCallingUid is in the foreground and callingUid isn't
+ if ((realCallingUid != callingUid) && isUidForeground(realCallingUid)) {
+ return false;
+ }
+ // don't abort if the caller has the same uid as the recents component
+ if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
+ return false;
+ }
+ // anything that has fallen through will currently be aborted
+ // TODO: remove this toast after feature development is done
+ mService.mUiHandler.post(() -> {
+ Toast.makeText(mService.mContext,
+ "Blocking background activity start for " + callingPackage,
+ Toast.LENGTH_SHORT).show();
+ });
+ return true;
+ }
+
+ /** Returns true if uid has a visible window or its process is in top or persistent state. */
+ private boolean isUidForeground(int uid) {
+ return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_TOP)
+ || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
+ }
+
private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
Intent intent, WindowProcessController callerApp, ActivityRecord r,
- PendingIntentRecord originatingPendingIntent) {
+ PendingIntentRecord originatingPendingIntent, boolean abortedStart) {
boolean callerAppHasForegroundActivity =
callerApp != null && callerApp.hasForegroundActivities();
if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
- || r == null) {
+ || (!abortedStart && r == null)) {
// skip logging in this case
return;
}
@@ -894,8 +950,8 @@
final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
? callingUidHasAnyVisibleWindow
: mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
- final String targetPackage = r.packageName;
- final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1;
+ final String targetPackage = (r != null) ? r.packageName : null;
+ final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
final int targetUidProcState = mService.getUidStateLocked(targetUid);
final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
@@ -1063,7 +1119,7 @@
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
synchronized (mService.mGlobalLock) {
- final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
stack.mConfigWillChange = globalConfig != null
&& mService.getGlobalConfiguration().diff(globalConfig) != 0;
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1179,7 +1235,7 @@
}
case START_DELIVERED_TO_TOP: {
outResult.timeout = false;
- outResult.who = r.realActivity;
+ outResult.who = r.mActivityComponent;
outResult.totalTime = 0;
break;
}
@@ -1188,11 +1244,12 @@
// in the resumed state.
if (r.nowVisible && r.isState(RESUMED)) {
outResult.timeout = false;
- outResult.who = r.realActivity;
+ outResult.who = r.mActivityComponent;
outResult.totalTime = 0;
} else {
final long startTimeMs = SystemClock.uptimeMillis();
- mSupervisor.waitActivityVisible(r.realActivity, outResult, startTimeMs);
+ mSupervisor.waitActivityVisible(
+ r.mActivityComponent, outResult, startTimeMs);
// Note: the timeout variable is not currently not ever set.
do {
try {
@@ -1238,7 +1295,7 @@
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity);
} finally {
- final ActivityStack currentStack = r.getStack();
+ final ActivityStack currentStack = r.getActivityStack();
startedActivityStack = currentStack != null ? currentStack : mTargetStack;
if (ActivityManager.isStartResultSuccessful(result)) {
@@ -1249,7 +1306,8 @@
final ActivityRecord currentTop =
startedActivityStack.topRunningActivityLocked();
if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
- mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+ mRootActivityContainer.ensureVisibilityAndConfig(
+ currentTop, currentTop.getDisplayId(),
true /* markFrozenIfConfigChanged */, false /* deferResume */);
}
}
@@ -1257,7 +1315,7 @@
// If we are not able to proceed, disassociate the activity from the task.
// Leaving an activity in an incomplete state can lead to issues, such as
// performing operations without a window container.
- final ActivityStack stack = mStartActivity.getStack();
+ final ActivityStack stack = mStartActivity.getActivityStack();
if (stack != null) {
stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
null /* intentResultData */, "startActivity", true /* oomAdj */);
@@ -1284,7 +1342,7 @@
// Do not start home activity if it cannot be launched on preferred display. We are not
// doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
// fallback to launch on other displays.
- if (r.isActivityTypeHome() && !mSupervisor.canStartHomeOnDisplay(r.info,
+ if (r.isActivityTypeHome() && !mRootActivityContainer.canStartHomeOnDisplay(r.info,
mPreferredDisplayId, true /* allowInstrumenting */)) {
Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
return START_CANCELED;
@@ -1302,7 +1360,8 @@
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
// the device would otherwise leave the locked task.
- if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
+ if (mService.getLockTaskController().isLockTaskModeViolation(
+ reusedActivity.getTaskRecord(),
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
@@ -1319,14 +1378,14 @@
// If mStartActivity does not have a task associated with it, associate it with the
// reused activity's task. Do not do so if we're clearing top and resetting for a
// standard launchMode activity.
- if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
- mStartActivity.setTask(reusedActivity.getTask());
+ if (mStartActivity.getTaskRecord() == null && !clearTopAndResetStandardLaunchMode) {
+ mStartActivity.setTask(reusedActivity.getTaskRecord());
}
- if (reusedActivity.getTask().intent == null) {
+ if (reusedActivity.getTaskRecord().intent == null) {
// This task was started because of movement of the activity based on affinity...
// Now that we are actually launching it, we can assign the base intent.
- reusedActivity.getTask().setIntent(mStartActivity);
+ reusedActivity.getTaskRecord().setIntent(mStartActivity);
}
// This code path leads to delivering a new intent, we want to make sure we schedule it
@@ -1335,7 +1394,7 @@
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
- final TaskRecord task = reusedActivity.getTask();
+ final TaskRecord task = reusedActivity.getTaskRecord();
// In this situation we want to remove all activities from the task up to the one
// being started. In most cases this means we are resetting the task to its initial
@@ -1347,7 +1406,7 @@
// the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
// task reference is needed in the call below to
// {@link setTargetStackAndMoveToFrontIfNeeded}.
- if (reusedActivity.getTask() == null) {
+ if (reusedActivity.getTaskRecord() == null) {
reusedActivity.setTask(task);
}
@@ -1355,13 +1414,14 @@
if (top.frontOfTask) {
// Activity aliases may mean we use different intents for the top activity,
// so make sure the task now has the identity of the new intent.
- top.getTask().setIntent(mStartActivity);
+ top.getTaskRecord().setIntent(mStartActivity);
}
deliverNewIntent(top);
}
}
- mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
+ mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded
+ (false /* forceSend */, reusedActivity);
reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
@@ -1401,7 +1461,7 @@
if (mStartActivity.packageName == null) {
final ActivityStack sourceStack = mStartActivity.resultTo != null
- ? mStartActivity.resultTo.getStack() : null;
+ ? mStartActivity.resultTo.getActivityStack() : null;
if (sourceStack != null) {
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
@@ -1413,12 +1473,12 @@
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
- final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
final ActivityRecord topFocused = topStack.getTopActivity();
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
- && top.realActivity.equals(mStartActivity.realActivity)
- && top.userId == mStartActivity.userId
+ && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
+ && top.mUserId == mStartActivity.mUserId
&& top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
@@ -1430,7 +1490,7 @@
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
ActivityOptions.abort(mOptions);
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1443,7 +1503,7 @@
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
- mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
+ mSupervisor.handleNonResizableTaskIfNeeded(top.getTaskRecord(), preferredWindowingMode,
mPreferredDisplayId, topStack);
return START_DELIVERED_TO_TOP;
@@ -1451,7 +1511,7 @@
boolean newTask = false;
final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
- ? mSourceRecord.getTask() : null;
+ ? mSourceRecord.getTaskRecord() : null;
// Should this be considered a new task?
int result = START_SUCCESS;
@@ -1473,25 +1533,26 @@
}
mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
- mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
+ mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
mService.getPackageManagerInternalLocked().grantEphemeralAccess(
- mStartActivity.userId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
+ mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
UserHandle.getAppId(mCallingUid));
if (newTask) {
- EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
- mStartActivity.getTask().taskId);
+ EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
+ mStartActivity.getTaskRecord().taskId);
}
ActivityStack.logStartActivity(
- EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
+ EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord());
mTargetStack.mLastPausedActivity = null;
- mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
+ mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+ false /* forceSend */, mStartActivity);
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
mOptions);
if (mDoResume) {
final ActivityRecord topTaskActivity =
- mStartActivity.getTask().topRunningActivityLocked();
+ mStartActivity.getTaskRecord().topRunningActivityLocked();
if (!mTargetStack.isFocusable()
|| (topTaskActivity != null && topTaskActivity.mTaskOverlay
&& mStartActivity != topTaskActivity)) {
@@ -1512,19 +1573,19 @@
// task stack to be focusable, then ensure that we now update the focused stack
// accordingly.
if (mTargetStack.isFocusable()
- && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
+ && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
- mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity,
- mOptions);
+ mRootActivityContainer.resumeFocusedStacksTopActivities(
+ mTargetStack, mStartActivity, mOptions);
}
} else if (mStartActivity != null) {
- mSupervisor.mRecentTasks.add(mStartActivity.getTask());
+ mSupervisor.mRecentTasks.add(mStartActivity.getTaskRecord());
}
- mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+ mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
- mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
- mPreferredDisplayId, mTargetStack);
+ mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTaskRecord(),
+ preferredWindowingMode, mPreferredDisplayId, mTargetStack);
return START_SUCCESS;
}
@@ -1642,7 +1703,7 @@
if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
r.mTaskOverlay = true;
if (!mOptions.canTaskOverlayResume()) {
- final TaskRecord task = mSupervisor.anyTaskForIdLocked(
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(
mOptions.getLaunchTaskId());
final ActivityRecord top = task != null ? task.getTopActivity() : null;
if (top != null && !top.isState(RESUMED)) {
@@ -1678,10 +1739,10 @@
if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
- checkedCaller = mSupervisor.getTopDisplayFocusedStack()
+ checkedCaller = mRootActivityContainer.getTopDisplayFocusedStack()
.topRunningNonDelayedActivityLocked(mNotTop);
}
- if (!checkedCaller.realActivity.equals(r.realActivity)) {
+ if (!checkedCaller.mActivityComponent.equals(r.mActivityComponent)) {
// Caller is not the same as launcher, so always needed.
mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
}
@@ -1692,7 +1753,7 @@
private void sendNewTaskResultRequestIfNeeded() {
final ActivityStack sourceStack = mStartActivity.resultTo != null
- ? mStartActivity.resultTo.getStack() : null;
+ ? mStartActivity.resultTo.getActivityStack() : null;
if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new task...
// yet the caller has requested a result back. Well, that is pretty messed up,
@@ -1796,7 +1857,7 @@
return;
}
if (!mSourceRecord.finishing) {
- mSourceStack = mSourceRecord.getStack();
+ mSourceStack = mSourceRecord.getActivityStack();
return;
}
@@ -1814,7 +1875,7 @@
// example, if this method is being called for processing a pending activity launch, it
// is possible that the activity has been removed from the task after the launch was
// enqueued.
- final TaskRecord sourceTask = mSourceRecord.getTask();
+ final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
}
mSourceRecord = null;
@@ -1840,22 +1901,23 @@
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
- final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(mOptions.getLaunchTaskId());
intentActivity = task != null ? task.getTopActivity() : null;
} else if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
- intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+ intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// For the launch adjacent case we only want to put the activity in an existing
// task if the activity already exists in the history.
- intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+ intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
!(LAUNCH_SINGLE_TASK == mLaunchMode));
} else {
// Otherwise find the best task to put the activity in.
- intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
+ intentActivity =
+ mRootActivityContainer.findTask(mStartActivity, mPreferredDisplayId);
}
}
@@ -1876,7 +1938,7 @@
* @return {@link ActivityRecord} brought to front.
*/
private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
- mTargetStack = intentActivity.getStack();
+ mTargetStack = intentActivity.getActivityStack();
mTargetStack.mLastPausedActivity = null;
// If the target task is not in the front, then we need to bring it to the front...
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
@@ -1887,9 +1949,9 @@
final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
final ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
- final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
+ final TaskRecord topTask = curTop != null ? curTop.getTaskRecord() : null;
differentTopTask = topTask != null
- && (topTask != intentActivity.getTask() || topTask != focusStack.topTask());
+ && (topTask != intentActivity.getTaskRecord() || topTask != focusStack.topTask());
} else {
// The existing task should always be different from those in other displays.
differentTopTask = true;
@@ -1898,10 +1960,11 @@
if (differentTopTask && !mAvoidMoveToFront) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
- mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
+ mSourceStack.getTopActivity().getTaskRecord()
+ == mSourceRecord.getTaskRecord())) {
// We really do want to push this one into the user's face, right now.
if (mLaunchTaskBehind && mSourceRecord != null) {
- intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
+ intentActivity.setTaskToAffiliateWith(mSourceRecord.getTaskRecord());
}
// If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
@@ -1915,8 +1978,8 @@
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
if (!willClearTask) {
final ActivityStack launchStack = getLaunchStack(
- mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
- final TaskRecord intentTask = intentActivity.getTask();
+ mStartActivity, mLaunchFlags, mStartActivity.getTaskRecord(), mOptions);
+ final TaskRecord intentTask = intentActivity.getTaskRecord();
if (launchStack == null || launchStack == mTargetStack) {
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
@@ -1946,7 +2009,7 @@
// Target and computed stacks are on different displays and we've
// found a matching task - move the existing instance to that display and
// move it to front.
- intentActivity.getTask().reparent(launchStack, ON_TOP,
+ intentActivity.getTaskRecord().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentToDisplay");
mMovedToFront = true;
@@ -1956,7 +2019,7 @@
// For example, the activity may have been initially started with an intent
// which placed it in the fullscreen stack. To ensure the proper handling of
// the activity based on home stack assumptions, we must move it over.
- intentActivity.getTask().reparent(launchStack, ON_TOP,
+ intentActivity.getTaskRecord().reparent(launchStack, ON_TOP,
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentingHome");
mMovedToFront = true;
@@ -1972,14 +2035,14 @@
}
// Need to update mTargetStack because if task was moved out of it, the original stack may
// be destroyed.
- mTargetStack = intentActivity.getStack();
+ mTargetStack = intentActivity.getActivityStack();
if (!mMovedToFront && mDoResume) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
+ " from " + intentActivity);
mTargetStack.moveToFront("intentActivityFound");
}
- mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
+ mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTaskRecord(),
WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
// If the caller has requested that the target task be reset, then do so.
@@ -2001,14 +2064,14 @@
// launching another activity.
// TODO(b/36119896): We shouldn't trigger activity launches in this path since we are
// already launching one.
- final TaskRecord task = intentActivity.getTask();
+ final TaskRecord task = intentActivity.getTaskRecord();
task.performClearTaskLocked();
mReuseTask = task;
mReuseTask.setIntent(mStartActivity);
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
- ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
- mLaunchFlags);
+ final ActivityRecord top = intentActivity.getTaskRecord().performClearTaskLocked(
+ mStartActivity, mLaunchFlags);
if (top == null) {
// A special case: we need to start the activity because it is not currently
// running, and the caller has asked to clear the current task to have this
@@ -2020,7 +2083,7 @@
// Now pretend like this activity is being started by the top of its task, so it
// is put in the right place.
mSourceRecord = intentActivity;
- final TaskRecord task = mSourceRecord.getTask();
+ final TaskRecord task = mSourceRecord.getTaskRecord();
if (task != null && task.getStack() == null) {
// Target stack got cleared when we all activities were removed above.
// Go ahead and reset it.
@@ -2030,19 +2093,21 @@
!mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
}
}
- } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
+ } else if (mStartActivity.mActivityComponent.equals(
+ intentActivity.getTaskRecord().realActivity)) {
// In this case the top activity on the task is the same as the one being launched,
// so we take that as a request to bring the task to the foreground. If the top
// activity in the task is the root activity, deliver this new intent to it if it
// desires.
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| LAUNCH_SINGLE_TOP == mLaunchMode)
- && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
+ && intentActivity.mActivityComponent.equals(
+ mStartActivity.mActivityComponent)) {
if (intentActivity.frontOfTask) {
- intentActivity.getTask().setIntent(mStartActivity);
+ intentActivity.getTaskRecord().setIntent(mStartActivity);
}
deliverNewIntent(intentActivity);
- } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
+ } else if (!intentActivity.getTaskRecord().isSameIntentFilter(mStartActivity)) {
// In this case we are launching the root activity of the task, but with a
// different intent. We should start a new instance on top.
mAddingToTask = true;
@@ -2055,23 +2120,23 @@
// current task.
mAddingToTask = true;
mSourceRecord = intentActivity;
- } else if (!intentActivity.getTask().rootWasReset) {
+ } else if (!intentActivity.getTaskRecord().rootWasReset) {
// In this case we are launching into an existing task that has not yet been started
// from its front door. The current task has been brought to the front. Ideally,
// we'd probably like to place this new task at the bottom of its stack, but that's
// a little hard to do with the current organization of the code so for now we'll
// just drop it.
- intentActivity.getTask().setIntent(mStartActivity);
+ intentActivity.getTaskRecord().setIntent(mStartActivity);
}
}
private void resumeTargetStackIfNeeded() {
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions);
+ mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions);
} else {
ActivityOptions.abort(mOptions);
}
- mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+ mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
}
private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
@@ -2082,16 +2147,16 @@
if (mReuseTask == null) {
final TaskRecord task = mTargetStack.createTaskRecord(
- mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
+ mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
- updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
+ updateBounds(mStartActivity.getTaskRecord(), mLaunchParams.mBounds);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
- + " in new task " + mStartActivity.getTask());
+ + " in new task " + mStartActivity.getTaskRecord());
} else {
addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
}
@@ -2100,7 +2165,8 @@
mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
}
- if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
+ if (mService.getLockTaskController().isLockTaskModeViolation(
+ mStartActivity.getTaskRecord())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -2116,20 +2182,21 @@
return;
}
- ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());
+ ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord());
activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
mIntentDelivered = true;
}
private int setTaskFromSourceRecord() {
- if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) {
+ if (mService.getLockTaskController().isLockTaskModeViolation(
+ mSourceRecord.getTaskRecord())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
- final TaskRecord sourceTask = mSourceRecord.getTask();
- final ActivityStack sourceStack = mSourceRecord.getStack();
+ final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
+ final ActivityStack sourceStack = mSourceRecord.getActivityStack();
// We only want to allow changing stack in two cases:
// 1. If the target task is not the top one. Otherwise we would move the launching task to
// the other side, rather than show two side by side.
@@ -2139,19 +2206,19 @@
final boolean moveStackAllowed = sourceStack.topTask() != sourceTask
|| !mStartActivity.canBeLaunchedOnDisplay(targetDisplayId);
if (moveStackAllowed) {
- mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(),
- mOptions);
+ mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags,
+ mStartActivity.getTaskRecord(), mOptions);
// If target stack is not found now - we can't just rely on the source stack, as it may
// be not suitable. Let's check other displays.
if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
// Can't use target display, lets find a stack on the source display.
- mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
+ mTargetStack = mRootActivityContainer.getValidLaunchStackOnDisplay(
sourceStack.mDisplayId, mStartActivity, mOptions, mLaunchParams);
}
if (mTargetStack == null) {
// There are no suitable stacks on the target and source display(s). Look on all
// displays.
- mTargetStack = mSupervisor.getNextValidLaunchStackLocked(
+ mTargetStack = mRootActivityContainer.getNextValidLaunchStack(
mStartActivity, -1 /* currentFocus */);
}
}
@@ -2177,12 +2244,12 @@
ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
mKeepCurTransition = true;
if (top != null) {
- ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
+ ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTaskRecord());
deliverNewIntent(top);
// For paranoia, make sure we have correctly resumed the top activity.
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
ActivityOptions.abort(mOptions);
return START_DELIVERED_TO_TOP;
@@ -2193,14 +2260,14 @@
// stack if so.
final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity);
if (top != null) {
- final TaskRecord task = top.getTask();
+ final TaskRecord task = top.getTaskRecord();
task.moveActivityToFrontLocked(top);
top.updateOptionsLocked(mOptions);
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
deliverNewIntent(top);
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
return START_DELIVERED_TO_TOP;
}
@@ -2210,7 +2277,8 @@
// the same task as the one that is starting it.
addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord");
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
- + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord);
+ + " in existing task " + mStartActivity.getTaskRecord()
+ + " from source " + mSourceRecord);
return START_SUCCESS;
}
@@ -2227,8 +2295,8 @@
// Check whether we should actually launch the new activity in to the task,
// or just reuse the current activity on top.
ActivityRecord top = mInTask.getTopActivity();
- if (top != null && top.realActivity.equals(mStartActivity.realActivity)
- && top.userId == mStartActivity.userId) {
+ if (top != null && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
+ && top.mUserId == mStartActivity.mUserId) {
if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) {
mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
@@ -2254,7 +2322,8 @@
if (!mLaunchParams.mBounds.isEmpty()) {
// TODO: Shouldn't we already know what stack to use by the time we get here?
- ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
+ ActivityStack stack = mRootActivityContainer.getLaunchStack(
+ null, null, mInTask, ON_TOP);
if (stack != mInTask.getStack()) {
mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
DEFER_RESUME, "inTaskToFront");
@@ -2269,7 +2338,7 @@
addOrReparentStartingActivity(mInTask, "setTaskFromInTask");
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
- + " in explicit task " + mStartActivity.getTask());
+ + " in explicit task " + mStartActivity.getTaskRecord());
return START_SUCCESS;
}
@@ -2295,17 +2364,18 @@
mTargetStack.moveToFront("addingToTopTask");
}
final ActivityRecord prev = mTargetStack.getTopActivity();
- final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
- mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
+ final TaskRecord task = (prev != null)
+ ? prev.getTaskRecord() : mTargetStack.createTaskRecord(
+ mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info,
mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
mTargetStack.positionChildWindowContainerAtTop(task);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
- + " in new guessed " + mStartActivity.getTask());
+ + " in new guessed " + mStartActivity.getTaskRecord());
}
private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
- if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
+ if (mStartActivity.getTaskRecord() == null || mStartActivity.getTaskRecord() == parent) {
parent.addActivityToTop(mStartActivity);
} else {
mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
@@ -2341,14 +2411,14 @@
private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
ActivityOptions aOptions) {
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
if (stack != null) {
return stack;
}
final ActivityStack currentStack = task != null ? task.getStack() : null;
- final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
if (currentStack != null) {
if (focusedStack != currentStack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
@@ -2369,18 +2439,18 @@
if (mPreferredDisplayId != DEFAULT_DISPLAY) {
// Try to put the activity in a stack on a secondary display.
- stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions,
- mLaunchParams);
+ stack = mRootActivityContainer.getValidLaunchStackOnDisplay(
+ mPreferredDisplayId, r, aOptions, mLaunchParams);
if (stack == null) {
// If source display is not suitable - look for topmost valid stack in the system.
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Can't launch on mPreferredDisplayId="
+ mPreferredDisplayId + ", looking on all displays.");
- stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId);
+ stack = mRootActivityContainer.getNextValidLaunchStack(r, mPreferredDisplayId);
}
}
if (stack == null) {
- stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+ stack = mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP);
}
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
@@ -2390,7 +2460,7 @@
/** Check if provided activity record can launch in currently focused stack. */
// TODO: This method can probably be consolidated into getLaunchStack() below.
private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
- final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
final boolean canUseFocusedStack;
if (focusedStack.isActivityTypeAssistant()) {
canUseFocusedStack = r.isActivityTypeAssistant();
@@ -2436,14 +2506,14 @@
// full resolution.
mLaunchParams.mPreferredDisplayId =
mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
- final ActivityStack stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP,
- mLaunchParams);
+ final ActivityStack stack =
+ mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams);
mLaunchParams.mPreferredDisplayId = mPreferredDisplayId;
return stack;
}
// Otherwise handle adjacent launch.
- final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
// The parent activity doesn't want to launch the activity on top of itself, but
// instead tries to put it onto other side in side-by-side mode.
final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
@@ -2461,7 +2531,8 @@
if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
// If parent was in docked stack, the natural place to launch another activity
// will be fullscreen, so it can appear alongside the docked window.
- final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
+ final int activityType =
+ mRootActivityContainer.resolveActivityType(r, mOptions, task);
return parentStack.getDisplay().getOrCreateStack(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
} else {
@@ -2469,10 +2540,10 @@
// and if yes, we will launch into that stack. If not, we just put the new
// activity into parent's stack, because we can't find a better place.
final ActivityStack dockedStack =
- mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
// There is a docked stack, but it isn't visible, so we can't launch into that.
- return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+ return mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP);
} else {
return dockedStack;
}
@@ -2660,7 +2731,7 @@
prefix = prefix + " ";
pw.print(prefix);
pw.print("mCurrentUser=");
- pw.println(mSupervisor.mCurrentUser);
+ pw.println(mRootActivityContainer.mCurrentUser);
pw.print(prefix);
pw.print("mLastStartReason=");
pw.println(mLastStartReason);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index d665592..0cdbedb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -473,4 +473,6 @@
public abstract void setProfileApp(String profileApp);
public abstract void setProfileProc(WindowProcessController wpc);
public abstract void setProfilerInfo(ProfilerInfo profilerInfo);
+
+ public abstract ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d0e3fb4..0967afd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -91,8 +91,6 @@
.PACKAGE;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -122,6 +120,8 @@
import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
@@ -190,6 +190,7 @@
import android.os.IBinder;
import android.os.IUserManager;
import android.os.LocaleList;
+import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
@@ -246,7 +247,6 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
-import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
@@ -352,6 +352,7 @@
/* Global service lock used by the package the owns this service. */
final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
ActivityStackSupervisor mStackSupervisor;
+ RootActivityContainer mRootActivityContainer;
WindowManagerService mWindowManager;
private UserManagerService mUserManager;
private AppOpsService mAppOpsService;
@@ -752,9 +753,9 @@
return mGlobalLock;
}
- public void setActivityManagerService(IntentFirewall intentFirewall,
- PendingIntentController intentController) {
- mH = new H();
+ public void initialize(IntentFirewall intentFirewall, PendingIntentController intentController,
+ Looper looper) {
+ mH = new H(looper);
mUiHandler = new UiHandler();
mIntentFirewall = intentFirewall;
final File systemDir = SystemServiceManager.ensureSystemDir();
@@ -766,7 +767,8 @@
mTempConfig.setLocales(LocaleList.getDefault());
mConfigurationSeq = mTempConfig.seq = 1;
mStackSupervisor = createStackSupervisor();
- mStackSupervisor.onConfigurationChanged(mTempConfig);
+ mRootActivityContainer = new RootActivityContainer(this);
+ mRootActivityContainer.onConfigurationChanged(mTempConfig);
mTaskChangeNotificationController =
new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
@@ -801,6 +803,7 @@
mWindowManager = wm;
mLockTaskController.setWindowManager(wm);
mStackSupervisor.setWindowManager(wm);
+ mRootActivityContainer.setWindowManager(wm);
}
}
@@ -1255,7 +1258,7 @@
sourceToken = resultTo;
}
- sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken);
+ sourceRecord = mRootActivityContainer.isInAnyStack(sourceToken);
if (sourceRecord == null) {
throw new SecurityException("Called with bad activity token: " + sourceToken);
}
@@ -1439,7 +1442,7 @@
return true;
}
// Keep track of the root activity of the task before we finish it
- TaskRecord tr = r.getTask();
+ final TaskRecord tr = r.getTaskRecord();
ActivityRecord rootR = tr.getRootActivity();
if (rootR == null) {
Slog.w(TAG, "Finishing task with all activities already finished");
@@ -1454,7 +1457,7 @@
// We should consolidate.
if (mController != null) {
// Find the first activity that is not finishing.
- ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
+ final ActivityRecord next = r.getActivityStack().topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
@@ -1516,7 +1519,7 @@
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
// can finish.
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (getLockTaskController().activityBlockedFromFinish(r)) {
return false;
}
@@ -1799,7 +1802,7 @@
}
final boolean translucentChanged = r.changeWindowTranslucency(true);
if (translucentChanged) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
mWindowManager.setAppFullscreen(token, true);
return translucentChanged;
@@ -1819,7 +1822,7 @@
if (r == null) {
return false;
}
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
int index = task.mActivities.lastIndexOf(r);
if (index > 0) {
ActivityRecord under = task.mActivities.get(index - 1);
@@ -1827,9 +1830,9 @@
}
final boolean translucentChanged = r.changeWindowTranslucency(false);
if (translucentChanged) {
- r.getStack().convertActivityToTranslucent(r);
+ r.getActivityStack().convertActivityToTranslucent(r);
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mWindowManager.setAppFullscreen(token, false);
return translucentChanged;
}
@@ -1842,9 +1845,9 @@
public void notifyActivityDrawn(IBinder token) {
if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
synchronized (mGlobalLock) {
- ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
+ ActivityRecord r = mRootActivityContainer.isInAnyStack(token);
if (r != null) {
- r.getStack().notifyActivityDrawnLocked(r);
+ r.getActivityStack().notifyActivityDrawnLocked(r);
}
}
}
@@ -1879,7 +1882,7 @@
synchronized (mGlobalLock) {
ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack != null) {
- return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+ return mRootActivityContainer.getStackInfo(focusedStack.mStackId);
}
return null;
}
@@ -1895,14 +1898,14 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
return;
}
final ActivityRecord r = stack.topRunningActivityLocked();
if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
} finally {
@@ -1917,14 +1920,14 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
return;
}
final ActivityRecord r = task.topRunningActivityLocked();
if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
} finally {
@@ -1964,7 +1967,7 @@
synchronized (mGlobalLock) {
final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
if (srec != null) {
- return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity);
+ return srec.getActivityStack().shouldUpRecreateTaskLocked(srec, destAffinity);
}
}
return false;
@@ -1977,7 +1980,8 @@
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
- return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData);
+ return r.getActivityStack().navigateUpToLocked(
+ r, destIntent, resultCode, resultData);
}
return false;
}
@@ -2009,7 +2013,7 @@
final long origId = Binder.clearCallingIdentity();
try {
int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
if (task != null) {
return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
}
@@ -2027,7 +2031,7 @@
Rect rect = new Rect();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
@@ -2058,7 +2062,7 @@
synchronized (mGlobalLock) {
enforceCallerIsRecentsOrHasPermission(
MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
- final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
+ final TaskRecord tr = mRootActivityContainer.anyTaskForId(id,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
return tr.lastTaskDescription;
@@ -2078,7 +2082,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
@@ -2167,7 +2171,7 @@
}
final long origId = Binder.clearCallingIdentity();
try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
if (task == null) {
Slog.d(TAG, "Could not find task for id: "+ taskId);
SafeActivityOptions.abort(options);
@@ -2284,7 +2288,7 @@
final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
callingUid);
- mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
+ mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType,
ignoreWindowingMode, callingUid, allowed);
}
@@ -2297,7 +2301,7 @@
final long origId = Binder.clearCallingIdentity();
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
+ r.getActivityStack().finishSubActivityLocked(r, resultWho, requestCode);
}
Binder.restoreCallingIdentity(origId);
}
@@ -2320,7 +2324,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
return;
@@ -2329,7 +2333,7 @@
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
throw new IllegalStateException(
"moveTaskToStack: No stack for stackId=" + stackId);
@@ -2359,7 +2363,7 @@
try {
synchronized (mGlobalLock) {
if (animate) {
- final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+ final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
return;
@@ -2371,12 +2375,12 @@
stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
animationDuration, false /* fromFullscreen */);
} else {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
return;
}
- mStackSupervisor.resizeStackLocked(stack, destBounds,
+ mRootActivityContainer.resizeStack(stack, destBounds,
null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
preserveWindows, allowResizeInDockedMode, !DEFER_RESUME);
}
@@ -2410,7 +2414,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
@@ -2452,7 +2456,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+ mRootActivityContainer.removeStacksInWindowingModes(windowingModes);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2467,7 +2471,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
+ mRootActivityContainer.removeStacksWithActivityTypes(activityTypes);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2498,7 +2502,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- return mStackSupervisor.getAllStackInfosLocked();
+ return mRootActivityContainer.getAllStackInfos();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2511,7 +2515,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- return mStackSupervisor.getStackInfo(windowingMode, activityType);
+ return mRootActivityContainer.getStackInfo(windowingMode, activityType);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2542,7 +2546,7 @@
if (r == null) {
return;
}
- startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
+ startLockTaskModeLocked(r.getTaskRecord(), false /* isSystemCaller */);
}
}
@@ -2553,7 +2557,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
return;
@@ -2575,7 +2579,7 @@
if (r == null) {
return;
}
- stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
+ stopLockTaskModeInternal(r.getTaskRecord(), false /* isSystemCaller */);
}
}
@@ -2595,7 +2599,7 @@
return;
}
- final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
if (stack == null || task != stack.topTask()) {
throw new IllegalArgumentException("Invalid task, not in foreground");
}
@@ -2610,7 +2614,7 @@
long ident = Binder.clearCallingIdentity();
try {
// When a task is locked, dismiss the pinned stack if it exists
- mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
} finally {
@@ -2668,7 +2672,7 @@
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
r.setTaskDescription(td);
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
task.updateTaskDescription();
mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
}
@@ -2712,7 +2716,7 @@
try {
// TODO: VI Consider treating local voice interactions and voice tasks
// differently here
- mStackSupervisor.finishVoiceTask(session);
+ mRootActivityContainer.finishVoiceTask(session);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2724,7 +2728,7 @@
public boolean isTopOfTask(IBinder token) {
synchronized (mGlobalLock) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
- return r != null && r.getTask().getTopActivity() == r;
+ return r != null && r.getTaskRecord().getTopActivity() == r;
}
}
@@ -2763,8 +2767,8 @@
}
if (structure != null) {
// Pre-fill the task/activity component for all assist data receivers
- structure.setTaskId(pae.activity.getTask().taskId);
- structure.setActivityComponent(pae.activity.realActivity);
+ structure.setTaskId(pae.activity.getTaskRecord().taskId);
+ structure.setActivityComponent(pae.activity.mActivityComponent);
structure.setHomeActivity(pae.isHome);
}
pae.haveResult = true;
@@ -2872,9 +2876,9 @@
+ ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
}
- final ActivityStack stack = r.getStack();
+ final ActivityStack stack = r.getActivityStack();
final TaskRecord task = stack.createTaskRecord(
- mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
+ mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), ainfo, intent,
null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
@@ -2902,7 +2906,7 @@
@Override
public void setTaskResizeable(int taskId, int resizeableMode) {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(
taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
@@ -2918,7 +2922,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
@@ -2970,7 +2974,7 @@
if (r == null) {
return false;
}
- return r.getStack().safelyDestroyActivityLocked(r, "app-req");
+ return r.getActivityStack().safelyDestroyActivityLocked(r, "app-req");
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2983,7 +2987,7 @@
final long origId = Binder.clearCallingIdentity();
try {
final WindowProcessController app = getProcessController(appInt);
- mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
+ mRootActivityContainer.releaseSomeActivitiesLocked(app, "low-mem");
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3077,7 +3081,7 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "removeStack: No stack with id=" + stackId);
return;
@@ -3102,7 +3106,7 @@
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
+ " to displayId=" + displayId);
- mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
+ mRootActivityContainer.moveStackToDisplay(stackId, displayId, ON_TOP);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3120,7 +3124,7 @@
"exitFreeformMode: No activity record matching token=" + token);
}
- final ActivityStack stack = r.getStack();
+ final ActivityStack stack = r.getActivityStack();
if (stack == null || !stack.inFreeformWindowingMode()) {
throw new IllegalStateException(
"exitFreeformMode: You can only go fullscreen from freeform.");
@@ -3406,7 +3410,7 @@
if (activity == null) {
return false;
}
- userId = activity.userId;
+ userId = activity.mUserId;
}
return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
}
@@ -3564,13 +3568,13 @@
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
+ taskId + " in stackId=" + stackId + " at position=" + position);
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
if (task == null) {
throw new IllegalArgumentException("positionTaskInStack: no task for id="
+ taskId);
}
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException("positionTaskInStack: no stack for id="
@@ -3625,7 +3629,7 @@
try {
synchronized (mGlobalLock) {
final ActivityStack stack =
- mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
if (stack == null) {
Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
return;
@@ -3635,7 +3639,7 @@
// Caller wants the current split-screen primary stack to be the top stack after
// it goes fullscreen, so move it to the front.
stack.moveToFront("dismissSplitScreenMode");
- } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
+ } else if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
// In this case the current split-screen primary stack shouldn't be the top
// stack after it goes fullscreen, but it current has focus, so we move the
// focus to the top-most split-screen secondary stack next to it.
@@ -3666,7 +3670,7 @@
try {
synchronized (mGlobalLock) {
final PinnedActivityStack stack =
- mStackSupervisor.getDefaultDisplay().getPinnedStack();
+ mRootActivityContainer.getDefaultDisplay().getPinnedStack();
if (stack == null) {
Slog.w(TAG, "dismissPip: pinned stack not found.");
return;
@@ -3708,7 +3712,7 @@
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+ final ActivityStack stack = mRootActivityContainer.getStack(fromStackId);
if (stack != null){
if (!stack.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException(
@@ -3743,7 +3747,7 @@
long ident = Binder.clearCallingIdentity();
try {
- return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+ return mRootActivityContainer.moveTopStackActivityToPinnedStack(stackId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3780,14 +3784,14 @@
}
private boolean isInPictureInPictureMode(ActivityRecord r) {
- if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
- || r.getStack().isInStackLocked(r) == null) {
+ if (r == null || r.getActivityStack() == null || !r.inPinnedWindowingMode()
+ || r.getActivityStack().isInStackLocked(r) == null) {
return false;
}
// If we are animating to fullscreen then we have already dispatched the PIP mode
// changed, so we should reflect that check here as well.
- final PinnedActivityStack stack = r.getStack();
+ final PinnedActivityStack stack = r.getActivityStack();
final PinnedStackWindowController windowController = stack.getWindowContainerController();
return !windowController.isAnimatingBoundsToFullscreen();
}
@@ -3821,9 +3825,9 @@
// Adjust the source bounds by the insets for the transition down
final Rect sourceBounds = new Rect(
r.pictureInPictureArgs.getSourceRectHint());
- mStackSupervisor.moveActivityToPinnedStackLocked(
+ mRootActivityContainer.moveActivityToPinnedStack(
r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
- final PinnedActivityStack stack = r.getStack();
+ final PinnedActivityStack stack = r.getActivityStack();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
@@ -3867,7 +3871,7 @@
// If the activity is already in picture-in-picture, update the pinned stack now
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP
- final PinnedActivityStack stack = r.getStack();
+ final PinnedActivityStack stack = r.getActivityStack();
if (!stack.isAnimatingBoundsToFullscreen()) {
stack.setPictureInPictureAspectRatio(
r.pictureInPictureArgs.getAspectRatio());
@@ -3925,8 +3929,8 @@
}
if (params.hasSetAspectRatio()
- && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
- params.getAspectRatio())) {
+ && !mWindowManager.isValidPictureInPictureAspectRatio(
+ r.getActivityStack().mDisplayId, params.getAspectRatio())) {
final float minAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
final float maxAspectRatio = mContext.getResources().getFloat(
@@ -4012,7 +4016,7 @@
}
int err;
- if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
+ if ((err = vrService.hasVrPackage(packageName, r.mUserId)) !=
VrManagerInternal.NO_ERROR) {
return err;
}
@@ -4042,7 +4046,7 @@
if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
throw new SecurityException("Only focused activity can call startVoiceInteraction");
}
- if (mRunningVoice != null || activity.getTask().voiceSession != null
+ if (mRunningVoice != null || activity.getTaskRecord().voiceSession != null
|| activity.voiceSession != null) {
Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
return;
@@ -4100,7 +4104,7 @@
synchronized (mGlobalLock) {
// Check if display is initialized in AM.
- if (!mStackSupervisor.isDisplayAdded(displayId)) {
+ if (!mRootActivityContainer.isDisplayAdded(displayId)) {
// Call might come when display is not yet added or has already been removed.
if (DEBUG_CONFIGURATION) {
Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
@@ -4190,7 +4194,7 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
@@ -4210,7 +4214,7 @@
try {
final TaskRecord task;
synchronized (mGlobalLock) {
- task = mStackSupervisor.anyTaskForIdLocked(taskId,
+ task = mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
@@ -4252,7 +4256,7 @@
if (mLastResumedActivity == null) {
return getCurrentUserId();
}
- return mLastResumedActivity.userId;
+ return mLastResumedActivity.mUserId;
}
}
@@ -4430,9 +4434,9 @@
if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
+ " to main stack for VR");
- final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
- moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
+ moveTaskToStack(r.getTaskRecord().taskId, stack.mStackId, true /* toTop */);
}
mH.post(() -> {
if (!mVrController.onVrModeChanged(r)) {
@@ -4444,7 +4448,7 @@
if (disableNonVrUi) {
// If we are in a VR mode where Picture-in-Picture mode is unsupported,
// then remove the pinned stack.
- mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
}
}
});
@@ -4496,7 +4500,7 @@
}
ActivityStack getTopDisplayFocusedStack() {
- return mStackSupervisor.getTopDisplayFocusedStack();
+ return mRootActivityContainer.getTopDisplayFocusedStack();
}
/** Pokes the task persister. */
@@ -4508,6 +4512,21 @@
return mKeyguardController.isKeyguardLocked();
}
+ /**
+ * Clears launch params for the given package.
+ * @param packageNames the names of the packages of which the launch params are to be cleared
+ */
+ @Override
+ public void clearLaunchParamsForPackages(List<String> packageNames) {
+ mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "clearLaunchParamsForPackages");
+ synchronized (mGlobalLock) {
+ for (int i = 0; i < packageNames.size(); ++i) {
+ mStackSupervisor.mLaunchParamsPersister.removeRecordForPackage(packageNames.get(i));
+ }
+ }
+ }
+
void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
@@ -4557,12 +4576,12 @@
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
pw.println(header);
- boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
+ boolean printedAnything = mRootActivityContainer.dumpActivities(fd, pw, dumpAll, dumpClient,
dumpPackage);
boolean needSep = printedAnything;
boolean printed = ActivityStackSupervisor.printThisActivity(pw,
- mStackSupervisor.getTopResumedActivity(), dumpPackage, needSep,
+ mRootActivityContainer.getTopResumedActivity(), dumpPackage, needSep,
" ResumedActivity: ");
if (printed) {
printedAnything = true;
@@ -4584,7 +4603,7 @@
void dumpActivityContainersLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
- mStackSupervisor.dumpChildrenNames(pw, " ");
+ mRootActivityContainer.dumpChildrenNames(pw, " ");
pw.println(" ");
}
@@ -4608,7 +4627,7 @@
ArrayList<ActivityRecord> activities;
synchronized (mGlobalLock) {
- activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+ activities = mRootActivityContainer.getDumpActivities(name, dumpVisibleStacksOnly,
dumpFocusedStackOnly);
}
@@ -4628,7 +4647,7 @@
}
needSep = true;
synchronized (mGlobalLock) {
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (lastTask != task) {
lastTask = task;
pw.print("TASK "); pw.print(lastTask.affinity);
@@ -4683,7 +4702,7 @@
}
void writeSleepStateToProto(ProtoOutputStream proto) {
- for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) {
+ for (ActivityTaskManagerInternal.SleepToken st : mRootActivityContainer.mSleepTokens) {
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
st.toString());
}
@@ -4728,7 +4747,7 @@
* also corresponds to the merged configuration of the default display.
*/
Configuration getGlobalConfiguration() {
- return mStackSupervisor.getConfiguration();
+ return mRootActivityContainer.getConfiguration();
}
boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
@@ -4860,7 +4879,7 @@
mTempConfig.seq = increaseConfigurationSeqLocked();
// Update stored global config and notify everyone about the change.
- mStackSupervisor.onConfigurationChanged(mTempConfig);
+ mRootActivityContainer.onConfigurationChanged(mTempConfig);
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
// TODO(multi-display): Update UsageEvents#Event to include displayId.
@@ -4907,7 +4926,7 @@
// Override configuration of the default display duplicates global config, so we need to
// update it also. This will also notify WindowManager about changes.
- performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+ performDisplayOverrideConfigUpdate(mRootActivityContainer.getConfiguration(), deferResume,
DEFAULT_DISPLAY);
return changes;
@@ -4961,12 +4980,12 @@
private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
int displayId) {
- mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+ mTempConfig.setTo(mRootActivityContainer.getDisplayOverrideConfiguration(displayId));
final int changes = mTempConfig.updateFrom(values);
if (changes != 0) {
Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
+ mTempConfig + " for displayId=" + displayId);
- mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+ mRootActivityContainer.setDisplayOverrideConfiguration(mTempConfig, displayId);
final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
if (isDensityChange && displayId == DEFAULT_DISPLAY) {
@@ -5016,6 +5035,10 @@
return mAmInternal.isActivityStartsLoggingEnabled();
}
+ boolean isBackgroundActivityStartsEnabled() {
+ return mAmInternal.isBackgroundActivityStartsEnabled();
+ }
+
void enableScreenAfterBoot(boolean booted) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
@@ -5088,7 +5111,7 @@
/** Update AMS states when an activity is resumed. */
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (task.isActivityTypeStandard()) {
if (mCurAppTimeTracker != r.appTimeTracker) {
// We are switching app tracking. Complete the current one.
@@ -5096,7 +5119,7 @@
mCurAppTimeTracker.stop();
mH.obtainMessage(
REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
- mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
+ mRootActivityContainer.clearOtherAppTimeTrackers(r.appTimeTracker);
mCurAppTimeTracker = null;
}
if (r.appTimeTracker != null) {
@@ -5120,7 +5143,7 @@
if (mLastResumedActivity != null) {
final IVoiceInteractionSession session;
- final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
+ final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTaskRecord();
if (lastResumedActivityTask != null
&& lastResumedActivityTask.voiceSession != null) {
session = lastResumedActivityTask.voiceSession;
@@ -5138,8 +5161,8 @@
}
}
- if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
- mAmInternal.sendForegroundProfileChanged(r.userId);
+ if (mLastResumedActivity != null && r.mUserId != mLastResumedActivity.mUserId) {
+ mAmInternal.sendForegroundProfileChanged(r.mUserId);
}
updateResumedAppTrace(r);
mLastResumedActivity = r;
@@ -5150,21 +5173,22 @@
applyUpdateVrModeLocked(r);
EventLogTags.writeAmSetResumedActivity(
- r == null ? -1 : r.userId,
+ r == null ? -1 : r.mUserId,
r == null ? "NULL" : r.shortComponentName,
reason);
}
ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
synchronized (mGlobalLock) {
- final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+ final ActivityTaskManagerInternal.SleepToken token =
+ mRootActivityContainer.createSleepToken(tag, displayId);
updateSleepIfNeededLocked();
return token;
}
}
void updateSleepIfNeededLocked() {
- final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+ final boolean shouldSleep = !mRootActivityContainer.hasAwakeDisplay();
final boolean wasSleeping = mSleeping;
boolean updateOomAdj = false;
@@ -5180,7 +5204,7 @@
mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
mStackSupervisor.comeOutOfSleepIfNeededLocked();
}
- mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+ mRootActivityContainer.applySleepTokens(true /* applyToStacks */);
if (wasSleeping) {
updateOomAdj = true;
}
@@ -5211,7 +5235,8 @@
void updateUsageStats(ActivityRecord component, boolean resumed) {
final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
- mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed);
+ mAmInternal, component.mActivityComponent, component.app.mUid, component.mUserId,
+ resumed);
mH.sendMessage(m);
}
@@ -5246,7 +5271,7 @@
mHeavyWeightProcess = root.app;
final Message m = PooledLambda.obtainMessage(
ActivityTaskManagerService::postHeavyWeightProcessNotification, this,
- root.app, root.intent, root.userId);
+ root.app, root.intent, root.mUserId);
mH.sendMessage(m);
}
@@ -5356,7 +5381,7 @@
// TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
private void startTimeTrackingFocusedActivityLocked() {
- final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
+ final ActivityRecord resumedActivity = mRootActivityContainer.getTopResumedActivity();
if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
mCurAppTimeTracker.start(resumedActivity.packageName);
}
@@ -5381,7 +5406,7 @@
/** Applies latest configuration and/or visibility updates if needed. */
private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
boolean kept = true;
- final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
+ final ActivityStack mainStack = mRootActivityContainer.getTopDisplayFocusedStack();
// mainStack is null during startup.
if (mainStack != null) {
if (changes != 0 && starting == null) {
@@ -5396,7 +5421,7 @@
false /* preserveWindow */);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
- mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+ mRootActivityContainer.ensureActivitiesVisible(starting, changes,
!PRESERVE_WINDOWS);
}
}
@@ -5568,8 +5593,8 @@
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_SUPERVISOR_STACK_MSG = 200;
- public H() {
- super(DisplayThread.get().getLooper());
+ H(Looper looper) {
+ super(looper);
}
@Override
@@ -5612,9 +5637,9 @@
@Override
public ComponentName getHomeActivityForUser(int userId) {
synchronized (mGlobalLock) {
- ActivityRecord homeActivity =
- mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
- return homeActivity == null ? null : homeActivity.realActivity;
+ final ActivityRecord homeActivity =
+ mRootActivityContainer.getDefaultDisplayHomeActivityForUser(userId);
+ return homeActivity == null ? null : homeActivity.mActivityComponent;
}
}
@@ -5651,14 +5676,14 @@
@Override
public List<IBinder> getTopVisibleActivities() {
synchronized (mGlobalLock) {
- return mStackSupervisor.getTopVisibleActivities();
+ return mRootActivityContainer.getTopVisibleActivities();
}
}
@Override
public void notifyDockedStackMinimizedChanged(boolean minimized) {
synchronized (mGlobalLock) {
- mStackSupervisor.setDockedStackMinimized(minimized);
+ mRootActivityContainer.setDockedStackMinimized(minimized);
}
}
@@ -5739,7 +5764,7 @@
// We might change the visibilities here, so prepare an empty app transition which
// might be overridden later if we actually change visibilities.
final ActivityDisplay activityDisplay =
- mStackSupervisor.getActivityDisplay(displayId);
+ mRootActivityContainer.getActivityDisplay(displayId);
if (activityDisplay == null) {
return;
}
@@ -5748,7 +5773,7 @@
if (!wasTransitionSet) {
dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// If there was a transition set already we don't want to interfere with it as we
// might be starting it too early.
@@ -5765,7 +5790,7 @@
public void notifyKeyguardTrustedChanged() {
synchronized (mGlobalLock) {
if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
}
}
@@ -5792,7 +5817,7 @@
"setFocusedActivity: No activity record matching token=" + token);
}
if (r.moveFocusableActivityToTop("setFocusedActivity")) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
}
@@ -5943,7 +5968,7 @@
public boolean shuttingDown(boolean booted, int timeout) {
synchronized (mGlobalLock) {
mShuttingDown = true;
- mStackSupervisor.prepareForShutdownLocked();
+ mRootActivityContainer.prepareForShutdown();
updateEventDispatchingLocked(booted);
notifyTaskPersisterLocked(null, true);
return mStackSupervisor.shutdownLocked(timeout);
@@ -6050,7 +6075,7 @@
@Override
public void onPackageReplaced(ApplicationInfo aInfo) {
synchronized (mGlobalLock) {
- mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+ mRootActivityContainer.updateActivityApplicationInfo(aInfo);
}
}
@@ -6080,7 +6105,7 @@
mH.post(() -> {
synchronized (mGlobalLock) {
final ActivityDisplay activityDisplay =
- mStackSupervisor.getActivityDisplay(displayId);
+ mRootActivityContainer.getActivityDisplay(displayId);
if (activityDisplay == null) {
// Call might come when display is not yet added or has been removed.
if (DEBUG_CONFIGURATION) {
@@ -6108,9 +6133,9 @@
int requestCode, int resultCode, Intent data) {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
- if (r != null && r.getStack() != null) {
- r.getStack().sendActivityResultLocked(callingUid, r, resultWho, requestCode,
- resultCode, data);
+ if (r != null && r.getActivityStack() != null) {
+ r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
+ requestCode, resultCode, data);
}
}
}
@@ -6163,14 +6188,14 @@
@Override
public boolean startHomeActivity(int userId, String reason) {
synchronized (mGlobalLock) {
- return mStackSupervisor.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
+ return mRootActivityContainer.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
}
}
@Override
public boolean startHomeOnAllDisplays(int userId, String reason) {
synchronized (mGlobalLock) {
- return mStackSupervisor.startHomeOnAllDisplays(userId, reason);
+ return mRootActivityContainer.startHomeOnAllDisplays(userId, reason);
}
}
@@ -6234,7 +6259,7 @@
Runnable finishInstrumentationCallback) {
synchronized (mGlobalLock) {
// Remove this application's activities from active lists.
- boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc);
+ boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc);
wpc.clearRecentTasks();
wpc.clearActivities();
@@ -6246,12 +6271,12 @@
mWindowManager.deferSurfaceLayout();
try {
if (!restarting && hasVisibleActivities
- && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
+ && !mRootActivityContainer.resumeFocusedStacksTopActivities()) {
// If there was nothing to resume, and we are not already restarting this
// process, but there is a visible activity that is hosted by the process...
// then make sure all visible activities are running, taking care of
// restarting this process.
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
} finally {
mWindowManager.continueSurfaceLayout();
@@ -6280,7 +6305,7 @@
}
mWindowManager.closeSystemDialogs(reason);
- mStackSupervisor.closeSystemDialogsLocked();
+ mRootActivityContainer.closeSystemDialogs();
}
// Call into AM outside the synchronized block.
mAmInternal.broadcastCloseSystemDialogs(reason);
@@ -6294,9 +6319,9 @@
String packageName, Set<String> disabledClasses, int userId, boolean booted) {
synchronized (mGlobalLock) {
// Clean-up disabled activities.
- if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
+ if (mRootActivityContainer.finishDisabledPackageActivities(
packageName, disabledClasses, true, false, userId) && booted) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
mStackSupervisor.scheduleIdleLocked();
}
@@ -6313,7 +6338,7 @@
boolean didSomething =
getActivityStartController().clearPendingActivityLaunches(packageName);
- didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
+ didSomething |= mRootActivityContainer.finishDisabledPackageActivities(packageName,
null, doit, evenPersistent, userId);
return didSomething;
}
@@ -6322,7 +6347,7 @@
@Override
public void resumeTopActivities(boolean scheduleIdle) {
synchronized (mGlobalLock) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
if (scheduleIdle) {
mStackSupervisor.scheduleIdleLocked();
}
@@ -6339,7 +6364,7 @@
@Override
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLock) {
- return mStackSupervisor.attachApplicationLocked(wpc);
+ return mRootActivityContainer.attachApplication(wpc);
}
}
@@ -6361,7 +6386,7 @@
// Showing launcher to avoid user entering credential twice.
startHomeActivity(currentUserId, "notifyLockedProfile");
}
- mStackSupervisor.lockAllProfileTasks(userId);
+ mRootActivityContainer.lockAllProfileTasks(userId);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -6382,9 +6407,9 @@
ActivityOptions activityOptions = options != null
? new ActivityOptions(options) : ActivityOptions.makeBasic();
final ActivityRecord homeActivity =
- mStackSupervisor.getDefaultDisplayHomeActivity();
+ mRootActivityContainer.getDefaultDisplayHomeActivity();
if (homeActivity != null) {
- activityOptions.setLaunchTaskId(homeActivity.getTask().taskId);
+ activityOptions.setLaunchTaskId(homeActivity.getTaskRecord().taskId);
}
mContext.startActivityAsUser(intent, activityOptions.toBundle(),
UserHandle.CURRENT);
@@ -6399,7 +6424,7 @@
synchronized (mGlobalLock) {
// The output proto of "activity --proto activities"
// is ActivityManagerServiceDumpActivitiesProto
- mStackSupervisor.writeToProto(proto,
+ mRootActivityContainer.writeToProto(proto,
ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
}
}
@@ -6494,7 +6519,7 @@
}
if (dumpPackage == null) {
pw.println(" mGlobalConfiguration: " + getGlobalConfiguration());
- mStackSupervisor.dumpDisplayConfigs(pw, " ");
+ mRootActivityContainer.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
if (dumpPackage == null) {
@@ -6522,7 +6547,7 @@
if (dumpPackage == null) {
pw.println(" mWakefulness="
+ PowerManagerInternal.wakefulnessToString(wakefulness));
- pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens);
+ pw.println(" mSleepTokens=" + mRootActivityContainer.mSleepTokens);
if (mRunningVoice != null) {
pw.println(" mRunningVoice=" + mRunningVoice);
pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
@@ -6649,14 +6674,14 @@
@Override
public boolean canGcNow() {
synchronized (mGlobalLock) {
- return isSleeping() || mStackSupervisor.allResumedActivitiesIdle();
+ return isSleeping() || mRootActivityContainer.allResumedActivitiesIdle();
}
}
@Override
public WindowProcessController getTopApp() {
synchronized (mGlobalLock) {
- final ActivityRecord top = mStackSupervisor.getTopResumedActivity();
+ final ActivityRecord top = mRootActivityContainer.getTopResumedActivity();
return top != null ? top.app : null;
}
}
@@ -6664,8 +6689,8 @@
@Override
public void rankTaskLayersIfNeeded() {
synchronized (mGlobalLock) {
- if (mStackSupervisor != null) {
- mStackSupervisor.rankTaskLayersIfNeeded();
+ if (mRootActivityContainer != null) {
+ mRootActivityContainer.rankTaskLayersIfNeeded();
}
}
}
@@ -6673,35 +6698,35 @@
@Override
public void scheduleDestroyAllActivities(String reason) {
synchronized (mGlobalLock) {
- mStackSupervisor.scheduleDestroyAllActivities(null, reason);
+ mRootActivityContainer.scheduleDestroyAllActivities(null, reason);
}
}
@Override
public void removeUser(int userId) {
synchronized (mGlobalLock) {
- mStackSupervisor.removeUserLocked(userId);
+ mRootActivityContainer.removeUser(userId);
}
}
@Override
public boolean switchUser(int userId, UserState userState) {
synchronized (mGlobalLock) {
- return mStackSupervisor.switchUserLocked(userId, userState);
+ return mRootActivityContainer.switchUser(userId, userState);
}
}
@Override
public void onHandleAppCrash(WindowProcessController wpc) {
synchronized (mGlobalLock) {
- mStackSupervisor.handleAppCrashLocked(wpc);
+ mRootActivityContainer.handleAppCrash(wpc);
}
}
@Override
public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
synchronized (mGlobalLock) {
- return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason);
+ return mRootActivityContainer.finishTopCrashedActivities(crashedApp, reason);
}
}
@@ -6871,5 +6896,12 @@
mProfilerInfo = profilerInfo;
}
}
+
+ @Override
+ public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
+ synchronized (mGlobalLock) {
+ return mStackSupervisor.getActivityMetricsLogger().getLaunchObserverRegistry();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 04fef02..441c593 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import android.app.ActivityManager;
import android.app.IAppTask;
@@ -77,7 +77,7 @@
synchronized (mService.mGlobalLock) {
long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
@@ -115,7 +115,7 @@
TaskRecord tr;
IApplicationThread appThread;
synchronized (mService.mGlobalLock) {
- tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
@@ -143,7 +143,7 @@
synchronized (mService.mGlobalLock) {
long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 32a6f74..bf00ffb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -305,7 +305,7 @@
AppWindowToken wtoken = openingApps.valueAt(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
- if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) {
+ if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
// This token isn't going to be animating. Add it to the list of tokens to
// be notified of app transition complete since the notification will not be
// sent be the app window animator.
@@ -341,7 +341,7 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
// TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
// animating?
- wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
+ wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
wtoken.updateReportedVisibilityLocked();
// Force the allDrawn flag, because we want to start
// this guy's animations regardless of whether it's
@@ -350,9 +350,8 @@
wtoken.deferClearAllDrawn = false;
// Ensure that apps that are mid-starting are also scheduled to have their
// starting windows removed after the animation is complete
- if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
- && wtoken.getController() != null) {
- wtoken.getController().removeStartingWindow();
+ if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
+ wtoken.removeStartingWindow();
}
if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index 0436857..6c3fbc1 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -123,7 +123,8 @@
// TODO(b/75318890): Need to move this to when the app actually crashes.
if (/*ActivityManager.isRunningInTestHarness()
- &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) {
+ &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(
+ r.mActivityComponent)) {
// Don't show warning if we are running in a test harness and we don't have to always
// show for this activity.
return;
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
deleted file mode 100644
index bd1460a..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- * Copyright (C) 2016 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.wm;
-
-import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
-import static android.app.ActivityOptions.ANIM_CUSTOM;
-import static android.app.ActivityOptions.ANIM_NONE;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
-import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
-import static android.app.ActivityOptions.ANIM_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_UNSET;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.view.AppTransitionAnimationSpec;
-import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IApplicationToken;
-import android.view.RemoteAnimationDefinition;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.AttributeCache;
-import com.android.server.policy.WindowManagerPolicy.StartingSurface;
-
-/**
- * Controller for the app window token container. This is created by activity manager to link
- * activity records to the app window token container they use in window manager.
- *
- * Test class: {@link AppWindowContainerControllerTests}
- */
-public class AppWindowContainerController
- extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
-
- private static final int STARTING_WINDOW_TYPE_NONE = 0;
- private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
- private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
-
- private final IApplicationToken mToken;
- private final Handler mHandler;
-
- private final class H extends Handler {
- public static final int NOTIFY_WINDOWS_DRAWN = 1;
- public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
- public static final int NOTIFY_WINDOWS_NOTDRAWN = 3;
-
- public H(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case NOTIFY_WINDOWS_DRAWN:
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsDrawn(true /* drawn */, msg.getWhen());
- break;
- case NOTIFY_STARTING_WINDOW_DRAWN:
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in "
- + AppWindowContainerController.this.mToken);
- mListener.onStartingWindowDrawn(msg.getWhen());
- break;
- case NOTIFY_WINDOWS_NOTDRAWN:
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsDrawn(false /* drawn */, msg.getWhen());
- break;
- default:
- break;
- }
- }
- }
-
- private final Runnable mOnWindowsVisible = () -> {
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsVisible();
- };
-
- private final Runnable mOnWindowsGone = () -> {
- if (mListener == null) {
- return;
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in "
- + AppWindowContainerController.this.mToken);
- mListener.onWindowsGone();
- };
-
- private final Runnable mAddStartingWindow = new Runnable() {
-
- @Override
- public void run() {
- final StartingData startingData;
- final AppWindowToken container;
-
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
- + " add starting window");
- return;
- }
-
- // There can only be one adding request, silly caller!
- mService.mAnimationHandler.removeCallbacks(this);
-
- startingData = mContainer.startingData;
- container = mContainer;
- }
-
- if (startingData == null) {
- // Animation has been canceled... do nothing.
- if (DEBUG_STARTING_WINDOW)
- Slog.v(TAG_WM, "startingData was nulled out before handling"
- + " mAddStartingWindow: " + mContainer);
- return;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
- + AppWindowContainerController.this + ": startingData="
- + container.startingData);
-
- StartingSurface surface = null;
- try {
- surface = startingData.createStartingSurface(container);
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when adding starting window", e);
- }
- if (surface != null) {
- boolean abort = false;
- synchronized (mGlobalLock) {
- // If the window was successfully added, then
- // we need to remove it.
- if (container.removed || container.startingData == null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Aborted starting " + container
- + ": removed=" + container.removed
- + " startingData=" + container.startingData);
- container.startingWindow = null;
- container.startingData = null;
- abort = true;
- } else {
- container.startingSurface = surface;
- }
- if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
- "Added starting " + mContainer
- + ": startingWindow="
- + container.startingWindow + " startingView="
- + container.startingSurface);
- }
- if (abort) {
- surface.remove();
- }
- } else if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
- }
- }
- };
-
- public AppWindowContainerController(TaskWindowContainerController taskController,
- IApplicationToken token, ComponentName activityComponent,
- AppWindowContainerListener listener, int index, int requestedOrientation,
- boolean fullscreen, boolean showForAllUsers, int configChanges,
- boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
- int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) {
- this(taskController, token, activityComponent, listener, index, requestedOrientation,
- fullscreen, showForAllUsers, configChanges, voiceInteraction, launchTaskBehind,
- alwaysFocusable, targetSdkVersion, rotationAnimationHint,
- inputDispatchingTimeoutNanos, WindowManagerService.getInstance());
- }
-
- public AppWindowContainerController(TaskWindowContainerController taskController,
- IApplicationToken token, ComponentName activityComponent,
- AppWindowContainerListener listener, int index, int requestedOrientation,
- boolean fullscreen, boolean showForAllUsers, int configChanges,
- boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
- int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
- WindowManagerService service) {
- super(listener, service);
- mHandler = new H(service.mH.getLooper());
- mToken = token;
- synchronized (mGlobalLock) {
- AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
- if (atoken != null) {
- // TODO: Should this throw an exception instead?
- Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
- return;
- }
-
- final Task task = taskController.mContainer;
- if (task == null) {
- throw new IllegalArgumentException("AppWindowContainerController: invalid "
- + " controller=" + taskController);
- }
-
- atoken = createAppWindow(mService, token, activityComponent, voiceInteraction,
- task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen,
- showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint,
- configChanges, launchTaskBehind, alwaysFocusable, this);
- if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
- + " controller=" + taskController + " at " + index);
- task.addChild(atoken, index);
- }
- }
-
- @VisibleForTesting
- AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
- ComponentName component, boolean voiceInteraction, DisplayContent dc,
- long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
- int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
- boolean launchTaskBehind, boolean alwaysFocusable,
- AppWindowContainerController controller) {
- return new AppWindowToken(service, token, component, voiceInteraction, dc,
- inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
- rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
- controller);
- }
-
- public void removeContainer(int displayId) {
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
- + mToken + " from non-existing displayId=" + displayId);
- return;
- }
- dc.removeAppToken(mToken.asBinder());
- super.removeContainer();
- }
- }
-
- @Override
- public void removeContainer() {
- throw new UnsupportedOperationException("Use removeContainer(displayId) instead.");
- }
-
- public void reparent(TaskWindowContainerController taskController, int position) {
- synchronized (mGlobalLock) {
- if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
- + " to task=" + taskController + " at " + position);
- if (mContainer == null) {
- if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM,
- "reparent: could not find app token=" + mToken);
- return;
- }
- final Task task = taskController.mContainer;
- if (task == null) {
- throw new IllegalArgumentException("reparent: could not find task="
- + taskController);
- }
- mContainer.reparent(task, position);
- mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
- }
-
- public Configuration setOrientation(int requestedOrientation, int displayId,
- Configuration displayConfig, boolean freezeScreenIfNeeded) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM,
- "Attempted to set orientation of non-existing app token: " + mToken);
- return null;
- }
-
- mContainer.setOrientation(requestedOrientation);
-
- final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;
- return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);
-
- }
- }
-
- public int getOrientation() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- return SCREEN_ORIENTATION_UNSPECIFIED;
- }
-
- return mContainer.getOrientationIgnoreVisibility();
- }
- }
-
- public void setDisablePreviewScreenshots(boolean disable) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
- + " token: " + mToken);
- return;
- }
- mContainer.setDisablePreviewScreenshots(disable);
- }
- }
-
- public void setVisibility(boolean visible, boolean deferHidingClient) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
- + mToken);
- return;
- }
-
- final AppWindowToken wtoken = mContainer;
- final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition;
-
- // Don't set visibility to false if we were already not visible. This prevents WM from
- // adding the app to the closing app list which doesn't make sense for something that is
- // already not visible. However, set visibility to true even if we are already visible.
- // This makes sure the app is added to the opening apps list so that the right
- // transition can be selected.
- // TODO: Probably a good idea to separate the concept of opening/closing apps from the
- // concept of setting visibility...
- if (!visible && wtoken.hiddenRequested) {
-
- if (!deferHidingClient && wtoken.mDeferHidingClient) {
- // We previously deferred telling the client to hide itself when visibility was
- // initially set to false. Now we would like it to hide, so go ahead and set it.
- wtoken.mDeferHidingClient = deferHidingClient;
- wtoken.setClientHidden(true);
- }
- return;
- }
-
- if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
- + mToken + ", visible=" + visible + "): " + appTransition
- + " hidden=" + wtoken.isHidden() + " hiddenRequested="
- + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
-
- final DisplayContent displayContent = mContainer.getDisplayContent();
- displayContent.mOpeningApps.remove(wtoken);
- displayContent.mClosingApps.remove(wtoken);
- wtoken.waitingToShow = false;
- wtoken.hiddenRequested = !visible;
- wtoken.mDeferHidingClient = deferHidingClient;
-
- if (!visible) {
- // If the app is dead while it was visible, we kept its dead window on screen.
- // Now that the app is going invisible, we can remove it. It will be restarted
- // if made visible again.
- wtoken.removeDeadWindows();
- } else {
- if (!appTransition.isTransitionSet()
- && appTransition.isReady()) {
- // Add the app mOpeningApps if transition is unset but ready. This means
- // we're doing a screen freeze, and the unfreeze will wait for all opening
- // apps to be ready.
- displayContent.mOpeningApps.add(wtoken);
- }
- wtoken.startingMoved = false;
- // If the token is currently hidden (should be the common case), or has been
- // stopped, then we need to set up to wait for its windows to be ready.
- if (wtoken.isHidden() || wtoken.mAppStopped) {
- wtoken.clearAllDrawn();
-
- // If the app was already visible, don't reset the waitingToShow state.
- if (wtoken.isHidden()) {
- wtoken.waitingToShow = true;
- }
- }
-
- // In the case where we are making an app visible but holding off for a transition,
- // we still need to tell the client to make its windows visible so they get drawn.
- // Otherwise, we will wait on performing the transition until all windows have been
- // drawn, they never will be, and we are sad.
- wtoken.setClientHidden(false);
-
- wtoken.requestUpdateWallpaperIfNeeded();
-
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
- wtoken.mAppStopped = false;
-
- mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
- }
-
- // If we are preparing an app transition, then delay changing
- // the visibility of this token until we execute that transition.
- if (wtoken.okToAnimate() && appTransition.isTransitionSet()) {
- wtoken.inPendingTransaction = true;
- if (visible) {
- displayContent.mOpeningApps.add(wtoken);
- wtoken.mEnteringAnimation = true;
- } else {
- displayContent.mClosingApps.add(wtoken);
- wtoken.mEnteringAnimation = false;
- }
- if (appTransition.getAppTransition()
- == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
- // We're launchingBehind, add the launching activity to mOpeningApps.
- final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
- if (win != null) {
- final AppWindowToken focusedToken = win.mAppToken;
- if (focusedToken != null) {
- if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
- + " adding " + focusedToken + " to mOpeningApps");
- // Force animation to be loaded.
- focusedToken.setHidden(true);
- displayContent.mOpeningApps.add(focusedToken);
- }
- }
- }
- return;
- }
-
- wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction);
- wtoken.updateReportedVisibilityLocked();
- }
- }
-
- /**
- * Notifies that we launched an app that might be visible or not visible depending on what kind
- * of Keyguard flags it's going to set on its windows.
- */
- public void notifyUnknownVisibilityLaunched() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(
- mContainer);
- }
- }
- }
-
- public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
- CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
- IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
- synchronized (mGlobalLock) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
- + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
- + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
- + " allowTaskSnapshot=" + allowTaskSnapshot);
-
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken);
- return false;
- }
-
- // If the display is frozen, we won't do anything until the actual window is
- // displayed so there is no reason to put in the starting window.
- if (!mContainer.okToDisplay()) {
- return false;
- }
-
- if (mContainer.startingData != null) {
- return false;
- }
-
- final WindowState mainWin = mContainer.findMainWindow();
- if (mainWin != null && mainWin.mWinAnimator.getShown()) {
- // App already has a visible window...why would you want a starting window?
- return false;
- }
-
- final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
- mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
- false /* restoreFromDisk */, false /* reducedResolution */);
- final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated, fromRecents, snapshot);
-
- if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
- return createSnapshot(snapshot);
- }
-
- // If this is a translucent window, then don't show a starting window -- the current
- // effect (a full-screen opaque starting window that fades away to the real contents
- // when it is ready) does not work for this.
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x"
- + Integer.toHexString(theme));
- if (theme != 0) {
- AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
- com.android.internal.R.styleable.Window, mService.mCurrentUserId);
- if (ent == null) {
- // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
- // see that.
- return false;
- }
- final boolean windowIsTranslucent = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- final boolean windowIsFloating = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsFloating, false);
- final boolean windowShowWallpaper = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowShowWallpaper, false);
- final boolean windowDisableStarting = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowDisablePreview, false);
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent
- + " Floating=" + windowIsFloating
- + " ShowWallpaper=" + windowShowWallpaper);
- if (windowIsTranslucent) {
- return false;
- }
- if (windowIsFloating || windowDisableStarting) {
- return false;
- }
- if (windowShowWallpaper) {
- if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget()
- == null) {
- // If this theme is requesting a wallpaper, and the wallpaper
- // is not currently visible, then this effectively serves as
- // an opaque window and our starting window transition animation
- // can still work. We just need to make sure the starting window
- // is also showing the wallpaper.
- windowFlags |= FLAG_SHOW_WALLPAPER;
- } else {
- return false;
- }
- }
- }
-
- if (mContainer.transferStartingWindow(transferFrom)) {
- return true;
- }
-
- // There is no existing starting window, and we don't want to create a splash screen, so
- // that's it!
- if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
- return false;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
- mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
- compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
- mContainer.getMergedOverrideConfiguration());
- scheduleAddStartingWindow();
- }
- return true;
- }
-
- private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
- TaskSnapshot snapshot) {
- if (mContainer.getDisplayContent().mAppTransition.getAppTransition()
- == TRANSIT_DOCK_TASK_FROM_RECENTS) {
- // TODO(b/34099271): Remove this statement to add back the starting window and figure
- // out why it causes flickering, the starting window appears over the thumbnail while
- // the docked from recents transition occurs
- return STARTING_WINDOW_TYPE_NONE;
- } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
- return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- } else if (taskSwitch && allowTaskSnapshot) {
- return snapshot == null ? STARTING_WINDOW_TYPE_NONE
- : snapshotOrientationSameAsTask(snapshot) || fromRecents
- ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- } else {
- return STARTING_WINDOW_TYPE_NONE;
- }
- }
-
- void scheduleAddStartingWindow() {
- // Note: we really want to do sendMessageAtFrontOfQueue() because we
- // want to process the message ASAP, before any other queued
- // messages.
- if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
- mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
- }
- }
-
- private boolean createSnapshot(TaskSnapshot snapshot) {
- if (snapshot == null) {
- return false;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
- mContainer.startingData = new SnapshotStartingData(mService, snapshot);
- scheduleAddStartingWindow();
- return true;
- }
-
- private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) {
- if (snapshot == null) {
- return false;
- }
- return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
- }
-
- public void removeStartingWindow() {
- synchronized (mGlobalLock) {
- if (mContainer.startingWindow == null) {
- if (mContainer.startingData != null) {
- // Starting window has not been added yet, but it is scheduled to be added.
- // Go ahead and cancel the request.
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Clearing startingData for token=" + mContainer);
- mContainer.startingData = null;
- }
- return;
- }
-
- final StartingSurface surface;
- if (mContainer.startingData != null) {
- surface = mContainer.startingSurface;
- mContainer.startingData = null;
- mContainer.startingSurface = null;
- mContainer.startingWindow = null;
- mContainer.startingDisplayed = false;
- if (surface == null) {
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
- + "remove");
- }
- return;
- }
- } else {
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
- + mContainer);
- }
- return;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer
- + " startingWindow=" + mContainer.startingWindow
- + " startingView=" + mContainer.startingSurface
- + " Callers=" + Debug.getCallers(5));
-
- // Use the same thread to remove the window as we used to add it, as otherwise we end up
- // with things in the view hierarchy being called from different threads.
- mService.mAnimationHandler.post(() -> {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
- try {
- surface.remove();
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when removing starting window", e);
- }
- });
- }
- }
-
- public void pauseKeyDispatching() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer);
- }
- }
- }
-
- public void resumeKeyDispatching() {
- synchronized (mGlobalLock) {
- if (mContainer != null) {
- mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer);
- }
- }
- }
-
- public void notifyAppResumed(boolean wasStopped) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
- return;
- }
- mContainer.notifyAppResumed(wasStopped);
- }
- }
-
- public void notifyAppStopping() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
- + mToken);
- return;
- }
- mContainer.detachChildren();
- }
- }
-
- public void notifyAppStopped() {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
- + mToken);
- return;
- }
- mContainer.notifyAppStopped();
- }
- }
-
- public void startFreezingScreen(int configChanges) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM,
- "Attempted to freeze screen with non-existing app token: " + mContainer);
- return;
- }
-
- if (configChanges == 0 && mContainer.okToDisplay()) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
- return;
- }
-
- mContainer.startFreezingScreen();
- }
- }
-
- public void stopFreezingScreen(boolean force) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- return;
- }
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
- + mContainer.isHidden() + " freezing=" + mContainer.isFreezingScreen());
- mContainer.stopFreezingScreen(true, force);
- }
- }
-
- public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
- + " token: " + mToken);
- return;
- }
- mContainer.registerRemoteAnimations(definition);
- }
- }
-
- void reportStartingWindowDrawn() {
- mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
- }
-
- void reportWindowsDrawn() {
- mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
- }
-
- void reportWindowsNotDrawn() {
- mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN));
- }
-
- void reportWindowsVisible() {
- mHandler.post(mOnWindowsVisible);
- }
-
- void reportWindowsGone() {
- mHandler.post(mOnWindowsGone);
- }
-
- /** Calls directly into activity manager so window manager lock shouldn't held. */
- boolean keyDispatchingTimedOut(String reason, int windowPid) {
- return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid);
- }
-
- /**
- * Apply override app transition base on options & animation type.
- */
- public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
- synchronized (mGlobalLock) {
- final int animationType = pendingOptions.getAnimationType();
- final DisplayContent displayContent = mContainer.getDisplayContent();
- switch (animationType) {
- case ANIM_CUSTOM:
- displayContent.mAppTransition.overridePendingAppTransition(
- pendingOptions.getPackageName(),
- pendingOptions.getCustomEnterResId(),
- pendingOptions.getCustomExitResId(),
- pendingOptions.getOnAnimationStartListener());
- break;
- case ANIM_CLIP_REVEAL:
- displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight());
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
- }
- break;
- case ANIM_SCALE_UP:
- displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight());
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
- }
- break;
- case ANIM_THUMBNAIL_SCALE_UP:
- case ANIM_THUMBNAIL_SCALE_DOWN:
- final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
- final GraphicBuffer buffer = pendingOptions.getThumbnail();
- displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getOnAnimationStartListener(),
- scaleUp);
- if (intent.getSourceBounds() == null && buffer != null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + buffer.getWidth(),
- pendingOptions.getStartY() + buffer.getHeight()));
- }
- break;
- case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
- case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
- final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
- final IAppTransitionAnimationSpecsFuture specsFuture =
- pendingOptions.getSpecsFuture();
- if (specsFuture != null) {
- // TODO(multidisplay): Shouldn't be really used anymore from next CL.
- displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
- specsFuture, pendingOptions.getOnAnimationStartListener(),
- animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
- } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
- && specs != null) {
- displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
- specs, pendingOptions.getOnAnimationStartListener(),
- pendingOptions.getAnimationFinishedListener(), false);
- } else {
- displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
- pendingOptions.getThumbnail(),
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight(),
- pendingOptions.getOnAnimationStartListener(),
- (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
- }
- }
- break;
- case ANIM_OPEN_CROSS_PROFILE_APPS:
- displayContent.mAppTransition
- .overridePendingAppTransitionStartCrossProfileApps();
- break;
- case ANIM_REMOTE_ANIMATION:
- // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
- displayContent.mAppTransition.overridePendingAppTransitionRemote(
- pendingOptions.getRemoteAnimationAdapter());
- break;
- case ANIM_NONE:
- break;
- default:
- Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
- break;
- }
- }
- }
-
- /**
- * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
- * This information helps AWT know that the app is in the process of pausing before it gets the
- * signal on the WM side.
- */
- public void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- return;
- }
-
- mContainer.setWillCloseOrEnterPip(willCloseOrEnterPip);
- }
- }
-
- @Override
- public String toString() {
- return "AppWindowContainerController{"
- + " token=" + mToken
- + " mContainer=" + mContainer
- + " mListener=" + mListener
- + "}";
- }
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
deleted file mode 100644
index ad27669..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 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.wm;
-
-/** Interface used by the creator of the controller to listen to changes with the container. */
-public interface AppWindowContainerListener extends WindowContainerListener {
- /** Called when the windows associated app window container drawn state changes. */
- void onWindowsDrawn(boolean drawn, long timestamp);
- /** Called when the windows associated app window container are visible. */
- void onWindowsVisible();
- /** Called when the windows associated app window container are no longer visible. */
- void onWindowsGone();
-
- /**
- * Called when the starting window for this container is drawn.
- */
- void onStartingWindowDrawn(long timestamp);
-
- /**
- * Called when the key dispatching to a window associated with the app window container
- * timed-out.
- *
- * @param reason The reason for the key dispatching time out.
- * @param windowPid The pid of the window key dispatching timed out on.
- * @return True if input dispatching should be aborted.
- */
- boolean keyDispatchingTimedOut(String reason, int windowPid);
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 729f89b..bb38f30 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -16,13 +16,13 @@
package com.android.server.wm;
+import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
+import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
-import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
-import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
-import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
@@ -53,7 +53,8 @@
AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) {
mAppToken = appToken;
- mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, appToken.mService);
+ mSurfaceAnimator =
+ new SurfaceAnimator(this, this::onAnimationFinished, appToken.mWmService);
mWidth = thumbnailHeader.getWidth();
mHeight = thumbnailHeader.getHeight();
@@ -65,7 +66,7 @@
// this to the task.
mSurfaceControl = appToken.makeSurface()
.setName("thumbnail anim: " + appToken.toString())
- .setSize(mWidth, mHeight)
+ .setBufferSize(mWidth, mHeight)
.setFormat(PixelFormat.TRANSLUCENT)
.setMetadata(appToken.windowType,
window != null ? window.mOwnerUid : Binder.getCallingUid())
@@ -93,11 +94,11 @@
void startAnimation(Transaction t, Animation anim, Point position) {
anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(mAppToken.mService.getTransitionAnimationScaleLocked());
+ anim.scaleCurrentDuration(mAppToken.mWmService.getTransitionAnimationScaleLocked());
mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
new WindowAnimationSpec(anim, position,
mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame()),
- mAppToken.mService.mSurfaceAnimationRunner), false /* hidden */);
+ mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */);
}
private void onAnimationFinished() {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 52c78ce..c458c94 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -28,10 +28,12 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_UNSET;
import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
@@ -81,7 +83,9 @@
import android.annotation.CallSuper;
import android.app.Activity;
+import android.app.ActivityManager;
import android.content.ComponentName;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.GraphicBuffer;
import android.graphics.Point;
@@ -90,6 +94,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -106,6 +111,8 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
+import com.android.server.AttributeCache;
+import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
import com.android.server.wm.WindowManagerService.H;
@@ -121,7 +128,8 @@
* Version of WindowToken that is specifically for a particular application (or
* really activity) that is displaying windows.
*/
-class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener {
+class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener,
+ ConfigurationContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM;
/**
@@ -226,6 +234,9 @@
private Task mLastParent;
+ // TODO: Remove after unification
+ ActivityRecord mActivityRecord;
+
/**
* See {@link #canTurnScreenOn()}
*/
@@ -273,14 +284,20 @@
/** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
boolean mNeedsAnimationBoundsLayer;
+ private static final int STARTING_WINDOW_TYPE_NONE = 0;
+ private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
+ private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
+
AppWindowToken(WindowManagerService service, IApplicationToken token,
ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
boolean launchTaskBehind, boolean alwaysFocusable,
- AppWindowContainerController controller) {
+ ActivityRecord activityRecord) {
this(service, token, activityComponent, voiceInteraction, dc, fullscreen);
- setController(controller);
+ // TODO: remove after unification
+ mActivityRecord = activityRecord;
+ mActivityRecord.registerConfigurationChangeListener(this);
mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
mShowForAllUsers = showForAllUsers;
mTargetSdk = targetSdk;
@@ -304,7 +321,7 @@
mActivityComponent = activityComponent;
mVoiceInteraction = voiceInteraction;
mFillsParent = fillsParent;
- mInputApplicationHandle = new InputApplicationHandle(this);
+ mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
}
void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
@@ -320,9 +337,7 @@
// it from behind the starting window, so there is no need for it to also be doing its
// own stuff.
win.cancelAnimation();
- if (getController() != null) {
- getController().removeStartingWindow();
- }
+ removeStartingWindow();
}
updateReportedVisibilityLocked();
}
@@ -360,16 +375,9 @@
}
if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
+ numInteresting + " visible=" + numVisible);
- final AppWindowContainerController controller = getController();
if (nowDrawn != reportedDrawn) {
- if (nowDrawn) {
- if (controller != null) {
- controller.reportWindowsDrawn();
- }
- } else {
- if (controller != null) {
- controller.reportWindowsNotDrawn();
- }
+ if (mActivityRecord != null) {
+ mActivityRecord.onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
}
reportedDrawn = nowDrawn;
}
@@ -377,16 +385,36 @@
if (DEBUG_VISIBILITY) Slog.v(TAG,
"Visibility changed in " + this + ": vis=" + nowVisible);
reportedVisible = nowVisible;
- if (controller != null) {
+ if (mActivityRecord != null) {
if (nowVisible) {
- controller.reportWindowsVisible();
+ onWindowsVisible();
} else {
- controller.reportWindowsGone();
+ onWindowsGone();
}
}
}
}
+ private void onWindowsGone() {
+ if (mActivityRecord == null) {
+ return;
+ }
+ if (DEBUG_VISIBILITY) {
+ Slog.v(TAG_WM, "Reporting gone in " + mActivityRecord.appToken);
+ }
+ mActivityRecord.onWindowsGone();
+ }
+
+ private void onWindowsVisible() {
+ if (mActivityRecord == null) {
+ return;
+ }
+ if (DEBUG_VISIBILITY) {
+ Slog.v(TAG_WM, "Reporting visible in " + mActivityRecord.appToken);
+ }
+ mActivityRecord.onWindowsVisible();
+ }
+
boolean isClientHidden() {
return mClientHidden;
}
@@ -401,7 +429,116 @@
sendAppVisibilityToClients();
}
- boolean setVisibility(WindowManager.LayoutParams lp,
+ void setVisibility(boolean visible, boolean deferHidingClient) {
+ final AppTransition appTransition = getDisplayContent().mAppTransition;
+
+ // Don't set visibility to false if we were already not visible. This prevents WM from
+ // adding the app to the closing app list which doesn't make sense for something that is
+ // already not visible. However, set visibility to true even if we are already visible.
+ // This makes sure the app is added to the opening apps list so that the right
+ // transition can be selected.
+ // TODO: Probably a good idea to separate the concept of opening/closing apps from the
+ // concept of setting visibility...
+ if (!visible && hiddenRequested) {
+
+ if (!deferHidingClient && mDeferHidingClient) {
+ // We previously deferred telling the client to hide itself when visibility was
+ // initially set to false. Now we would like it to hide, so go ahead and set it.
+ mDeferHidingClient = deferHidingClient;
+ setClientHidden(true);
+ }
+ return;
+ }
+
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
+ Slog.v(TAG_WM, "setAppVisibility("
+ + appToken + ", visible=" + visible + "): " + appTransition
+ + " hidden=" + isHidden() + " hiddenRequested="
+ + hiddenRequested + " Callers=" + Debug.getCallers(6));
+ }
+
+ final DisplayContent displayContent = getDisplayContent();
+ displayContent.mOpeningApps.remove(this);
+ displayContent.mClosingApps.remove(this);
+ waitingToShow = false;
+ hiddenRequested = !visible;
+ mDeferHidingClient = deferHidingClient;
+
+ if (!visible) {
+ // If the app is dead while it was visible, we kept its dead window on screen.
+ // Now that the app is going invisible, we can remove it. It will be restarted
+ // if made visible again.
+ removeDeadWindows();
+ } else {
+ if (!appTransition.isTransitionSet()
+ && appTransition.isReady()) {
+ // Add the app mOpeningApps if transition is unset but ready. This means
+ // we're doing a screen freeze, and the unfreeze will wait for all opening
+ // apps to be ready.
+ displayContent.mOpeningApps.add(this);
+ }
+ startingMoved = false;
+ // If the token is currently hidden (should be the common case), or has been
+ // stopped, then we need to set up to wait for its windows to be ready.
+ if (isHidden() || mAppStopped) {
+ clearAllDrawn();
+
+ // If the app was already visible, don't reset the waitingToShow state.
+ if (isHidden()) {
+ waitingToShow = true;
+ }
+ }
+
+ // In the case where we are making an app visible but holding off for a transition,
+ // we still need to tell the client to make its windows visible so they get drawn.
+ // Otherwise, we will wait on performing the transition until all windows have been
+ // drawn, they never will be, and we are sad.
+ setClientHidden(false);
+
+ requestUpdateWallpaperIfNeeded();
+
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + this);
+ mAppStopped = false;
+
+ transferStartingWindowFromHiddenAboveTokenIfNeeded();
+ }
+
+ // If we are preparing an app transition, then delay changing
+ // the visibility of this token until we execute that transition.
+ if (okToAnimate() && appTransition.isTransitionSet()) {
+ inPendingTransaction = true;
+ if (visible) {
+ displayContent.mOpeningApps.add(this);
+ mEnteringAnimation = true;
+ } else {
+ displayContent.mClosingApps.add(this);
+ mEnteringAnimation = false;
+ }
+ if (appTransition.getAppTransition()
+ == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
+ // We're launchingBehind, add the launching activity to mOpeningApps.
+ final WindowState win = getDisplayContent().findFocusedWindow();
+ if (win != null) {
+ final AppWindowToken focusedToken = win.mAppToken;
+ if (focusedToken != null) {
+ if (DEBUG_APP_TRANSITIONS) {
+ Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
+ + " adding " + focusedToken + " to mOpeningApps");
+ }
+ // Force animation to be loaded.
+ focusedToken.setHidden(true);
+ displayContent.mOpeningApps.add(focusedToken);
+ }
+ }
+ }
+ return;
+ }
+
+ commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
+ updateReportedVisibilityLocked();
+ }
+
+ boolean commitVisibility(WindowManager.LayoutParams lp,
boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
boolean delayed = false;
@@ -417,7 +554,8 @@
// * or this is an opening app and windows are being replaced.
boolean visibilityChanged = false;
if (isHidden() == visible || (isHidden() && mIsExiting) || (visible && waitingForReplacement())) {
- final AccessibilityController accessibilityController = mService.mAccessibilityController;
+ final AccessibilityController accessibilityController =
+ mWmService.mAccessibilityController;
boolean changed = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
"Changing app " + this + " hidden=" + isHidden() + " performLayout=" + performLayout);
@@ -458,18 +596,20 @@
// We are becoming visible, so better freeze the screen with the windows that are
// getting visible so we also wait for them.
- forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true);
+ forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
}
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this
- + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+ if (DEBUG_APP_TRANSITIONS) {
+ Slog.v(TAG_WM, "commitVisibility: " + this
+ + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+ }
if (changed) {
getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw();
if (performLayout) {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
}
@@ -496,13 +636,13 @@
// The token was made immediately visible, there will be no entrance animation.
// We need to inform the client the enter animation was finished.
mEnteringAnimation = true;
- mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
+ mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
+ token);
}
- // If we're becoming visible, immediately change client visibility as well although it
- // usually gets changed in AppWindowContainerController.setVisibility already. However,
- // there seem to be some edge cases where we change our visibility but client visibility
- // never gets updated.
+ // If we're becoming visible, immediately change client visibility as well. there seem
+ // to be some edge cases where we change our visibility but client visibility never gets
+ // updated.
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
if (visible || !isReallyAnimating()) {
@@ -520,7 +660,7 @@
// Take the screenshot before possibly hiding the WSA, otherwise the screenshot
// will not be taken.
- mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
+ mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
}
// If we are hidden but there is no delay needed we immediately
@@ -596,11 +736,6 @@
return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable;
}
- AppWindowContainerController getController() {
- final WindowContainerController controller = super.getController();
- return controller != null ? (AppWindowContainerController) controller : null;
- }
-
@Override
boolean isVisible() {
// If the app token isn't hidden then it is considered visible and there is no need to check
@@ -637,11 +772,11 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
- boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
+ boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
getDisplayContent().mOpeningApps.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
- mService.mTaskSnapshotController.onAppRemoved(this);
+ mWmService.mTaskSnapshotController.onAppRemoved(this);
waitingToShow = false;
if (getDisplayContent().mClosingApps.contains(this)) {
delayed = true;
@@ -656,8 +791,8 @@
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
+ this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
- if (startingData != null && getController() != null) {
- getController().removeStartingWindow();
+ if (startingData != null) {
+ removeStartingWindow();
}
// If this window was animating, then we need to ensure that the app transition notifies
@@ -694,7 +829,7 @@
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this
+ " displayId=" + dc.getDisplayId());
dc.setFocusedApp(null);
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
}
if (!delayed) {
@@ -768,9 +903,7 @@
mAppStopped = true;
destroySurfaces();
// Remove any starting window that was added for this app if they are still around.
- if (getController() != null) {
- getController().removeStartingWindow();
- }
+ removeStartingWindow();
}
void clearAllDrawn() {
@@ -826,9 +959,7 @@
// TODO: Something smells about the code below...Is there a better way?
if (startingWindow == win) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
- if (getController() != null) {
- getController().removeStartingWindow();
- }
+ removeStartingWindow();
} else if (mChildren.size() == 0) {
// If this is the last window and we had requested a starting transition window,
// well there is no point now.
@@ -845,9 +976,7 @@
// we need to get rid of the starting transition.
if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Last window, removing starting window "
+ win);
- if (getController() != null) {
- getController().removeStartingWindow();
- }
+ removeStartingWindow();
}
}
@@ -1014,13 +1143,17 @@
// if we got a replacement window, reset the timeout to give drawing more time
if (gotReplacementWindow) {
- mService.scheduleWindowReplacementTimeouts(this);
+ mWmService.scheduleWindowReplacementTimeouts(this);
}
checkKeyguardFlagsChanged();
}
@Override
void removeChild(WindowState child) {
+ if (!mChildren.contains(child)) {
+ // This can be true when testing.
+ return;
+ }
super.removeChild(child);
checkKeyguardFlagsChanged();
updateLetterboxSurface(child);
@@ -1042,6 +1175,20 @@
}
}
+ void reparent(TaskWindowContainerController taskController, int position) {
+ if (DEBUG_ADD_REMOVE) {
+ Slog.i(TAG_WM, "reparent: moving app token=" + this
+ + " to task=" + taskController + " at " + position);
+ }
+ final Task task = taskController.mContainer;
+ if (task == null) {
+ throw new IllegalArgumentException("reparent: could not find task="
+ + taskController);
+ }
+ reparent(task, position);
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+ }
+
void reparent(Task task, int position) {
final Task currentTask = getTask();
if (task == currentTask) {
@@ -1126,7 +1273,7 @@
final WindowState win = mChildren.get(i);
win.onUnfreezeBounds();
}
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
void setAppLayoutChanges(int changes, String reason) {
@@ -1134,7 +1281,7 @@
final DisplayContent dc = getDisplayContent();
dc.pendingLayoutChanges |= changes;
if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
+ mWmService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
}
}
}
@@ -1155,12 +1302,12 @@
if (!hiddenRequested) {
if (!mFreezingScreen) {
mFreezingScreen = true;
- mService.registerAppFreezeListener(this);
- mService.mAppsFreezingScreen++;
- if (mService.mAppsFreezingScreen == 1) {
- mService.startFreezingDisplayLocked(0, 0, getDisplayContent());
- mService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
+ mWmService.registerAppFreezeListener(this);
+ mWmService.mAppsFreezingScreen++;
+ if (mWmService.mAppsFreezingScreen == 1) {
+ mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
+ mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+ mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
}
}
final int count = mChildren.size();
@@ -1185,15 +1332,15 @@
if (force || unfrozeWindows) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
mFreezingScreen = false;
- mService.unregisterAppFreezeListener(this);
- mService.mAppsFreezingScreen--;
- mService.mLastFinishedFreezeSource = this;
+ mWmService.unregisterAppFreezeListener(this);
+ mWmService.mAppsFreezingScreen--;
+ mWmService.mLastFinishedFreezeSource = this;
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
- mService.stopFreezingDisplayLocked();
+ mWmService.stopFreezingDisplayLocked();
}
}
@@ -1284,10 +1431,10 @@
// pending opening apps.
getDisplayContent().mOpeningApps.remove(this);
- mService.updateFocusedWindowLocked(
+ mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
getDisplayContent().setLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -1300,9 +1447,7 @@
startingData = fromToken.startingData;
fromToken.startingData = null;
fromToken.startingMoved = true;
- if (getController() != null) {
- getController().scheduleAddStartingWindow();
- }
+ scheduleAddStartingWindow();
return true;
}
@@ -1459,7 +1604,7 @@
if (mDisplayContent != null) {
mDisplayContent.setLayoutNeeded();
}
- mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
+ mWmService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
// Notify the pinned stack upon all windows drawn. If there was an animation in
// progress then this signal will resume that animation.
@@ -1471,6 +1616,10 @@
}
}
+ boolean keyDispatchingTimedOut(String reason, int windowPid) {
+ return mActivityRecord != null && mActivityRecord.keyDispatchingTimedOut(reason, windowPid);
+ }
+
/**
* Updated this app token tracking states for interesting and drawn windows based on the window.
*
@@ -1489,8 +1638,8 @@
return false;
}
- if (mLastTransactionSequence != mService.mTransactionSequence) {
- mLastTransactionSequence = mService.mTransactionSequence;
+ if (mLastTransactionSequence != mWmService.mTransactionSequence) {
+ mLastTransactionSequence = mWmService.mTransactionSequence;
mNumDrawnWindows = 0;
startingDisplayed = false;
@@ -1533,8 +1682,8 @@
}
}
} else if (w.isDrawnLw()) {
- if (getController() != null) {
- getController().reportStartingWindowDrawn();
+ if (mActivityRecord != null) {
+ mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis());
}
startingDisplayed = true;
}
@@ -1601,6 +1750,266 @@
return this;
}
+ boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+ CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+ IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+ // If the display is frozen, we won't do anything until the actual window is
+ // displayed so there is no reason to put in the starting window.
+ if (!okToDisplay()) {
+ return false;
+ }
+
+ if (startingData != null) {
+ return false;
+ }
+
+ final WindowState mainWin = findMainWindow();
+ if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+ // App already has a visible window...why would you want a starting window?
+ return false;
+ }
+
+ final ActivityManager.TaskSnapshot snapshot =
+ mWmService.mTaskSnapshotController.getSnapshot(
+ getTask().mTaskId, getTask().mUserId,
+ false /* restoreFromDisk */, false /* reducedResolution */);
+ final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
+ allowTaskSnapshot, activityCreated, fromRecents, snapshot);
+
+ if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
+ return createSnapshot(snapshot);
+ }
+
+ // If this is a translucent window, then don't show a starting window -- the current
+ // effect (a full-screen opaque starting window that fades away to the real contents
+ // when it is ready) does not work for this.
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Checking theme of starting window: 0x" + Integer.toHexString(theme));
+ }
+ if (theme != 0) {
+ AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+ com.android.internal.R.styleable.Window,
+ mWmService.mCurrentUserId);
+ if (ent == null) {
+ // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
+ // see that.
+ return false;
+ }
+ final boolean windowIsTranslucent = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+ final boolean windowIsFloating = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsFloating, false);
+ final boolean windowShowWallpaper = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+ final boolean windowDisableStarting = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowDisablePreview, false);
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Translucent=" + windowIsTranslucent
+ + " Floating=" + windowIsFloating
+ + " ShowWallpaper=" + windowShowWallpaper);
+ }
+ if (windowIsTranslucent) {
+ return false;
+ }
+ if (windowIsFloating || windowDisableStarting) {
+ return false;
+ }
+ if (windowShowWallpaper) {
+ if (getDisplayContent().mWallpaperController
+ .getWallpaperTarget() == null) {
+ // If this theme is requesting a wallpaper, and the wallpaper
+ // is not currently visible, then this effectively serves as
+ // an opaque window and our starting window transition animation
+ // can still work. We just need to make sure the starting window
+ // is also showing the wallpaper.
+ windowFlags |= FLAG_SHOW_WALLPAPER;
+ } else {
+ return false;
+ }
+ }
+
+ if (transferStartingWindow(transferFrom)) {
+ return true;
+ }
+
+ // There is no existing starting window, and we don't want to create a splash screen, so
+ // that's it!
+ if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+ return false;
+ }
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+ startingData = new SplashScreenStartingData(mWmService, pkg,
+ theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+ getMergedOverrideConfiguration());
+ scheduleAddStartingWindow();
+ }
+ return true;
+ }
+
+
+ private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {
+ if (snapshot == null) {
+ return false;
+ }
+
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
+ startingData = new SnapshotStartingData(mWmService, snapshot);
+ scheduleAddStartingWindow();
+ return true;
+ }
+
+ void scheduleAddStartingWindow() {
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
+ mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+ }
+ }
+
+ private final Runnable mAddStartingWindow = new Runnable() {
+
+ @Override
+ public void run() {
+ synchronized (mWmService.mGlobalLock) {
+ // There can only be one adding request, silly caller!
+ mWmService.mAnimationHandler.removeCallbacks(this);
+ }
+
+ if (startingData == null) {
+ // Animation has been canceled... do nothing.
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "startingData was nulled out before handling"
+ + " mAddStartingWindow: " + AppWindowToken.this);
+ }
+ return;
+ }
+
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Add starting " + this + ": startingData=" + startingData);
+ }
+
+ WindowManagerPolicy.StartingSurface surface = null;
+ try {
+ surface = startingData.createStartingSurface(AppWindowToken.this);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception when adding starting window", e);
+ }
+ if (surface != null) {
+ boolean abort = false;
+ synchronized (mWmService.mGlobalLock) {
+ // If the window was successfully added, then
+ // we need to remove it.
+ if (removed || startingData == null) {
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Aborted starting " + AppWindowToken.this
+ + ": removed=" + removed + " startingData=" + startingData);
+ }
+ startingWindow = null;
+ startingData = null;
+ abort = true;
+ } else {
+ startingSurface = surface;
+ }
+ if (DEBUG_STARTING_WINDOW && !abort) {
+ Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow="
+ + startingWindow + " startingView=" + startingSurface);
+ }
+ }
+ if (abort) {
+ surface.remove();
+ }
+ } else if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG, "Surface returned was null: " + AppWindowToken.this);
+ }
+ }
+ };
+
+ private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
+ ActivityManager.TaskSnapshot snapshot) {
+ if (getDisplayContent().mAppTransition.getAppTransition()
+ == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ // TODO(b/34099271): Remove this statement to add back the starting window and figure
+ // out why it causes flickering, the starting window appears over the thumbnail while
+ // the docked from recents transition occurs
+ return STARTING_WINDOW_TYPE_NONE;
+ } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+ return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else if (taskSwitch && allowTaskSnapshot) {
+ return snapshot == null ? STARTING_WINDOW_TYPE_NONE
+ : snapshotOrientationSameAsTask(snapshot) || fromRecents
+ ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else {
+ return STARTING_WINDOW_TYPE_NONE;
+ }
+ }
+
+
+ private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) {
+ if (snapshot == null) {
+ return false;
+ }
+ return getTask().getConfiguration().orientation == snapshot.getOrientation();
+ }
+
+ void removeStartingWindow() {
+ if (startingWindow == null) {
+ if (startingData != null) {
+ // Starting window has not been added yet, but it is scheduled to be added.
+ // Go ahead and cancel the request.
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG_WM, "Clearing startingData for token=" + this);
+ }
+ startingData = null;
+ }
+ return;
+ }
+
+ final WindowManagerPolicy.StartingSurface surface;
+ if (startingData != null) {
+ surface = startingSurface;
+ startingData = null;
+ startingSurface = null;
+ startingWindow = null;
+ startingDisplayed = false;
+ if (surface == null) {
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
+ + "remove");
+ }
+ return;
+ }
+ } else {
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
+ + this);
+ }
+ return;
+ }
+
+ if (DEBUG_STARTING_WINDOW) {
+ Slog.v(TAG_WM, "Schedule remove starting " + this
+ + " startingWindow=" + startingWindow
+ + " startingView=" + startingSurface
+ + " Callers=" + Debug.getCallers(5));
+ }
+
+ // Use the same thread to remove the window as we used to add it, as otherwise we end up
+ // with things in the view hierarchy being called from different threads.
+ mWmService.mAnimationHandler.post(() -> {
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
+ try {
+ surface.remove();
+ } catch (Exception e) {
+ Slog.w(TAG_WM, "Exception when removing starting window", e);
+ }
+ });
+ }
+
@Override
boolean fillsParent() {
return mFillsParent;
@@ -1647,7 +2056,7 @@
final boolean containsShowWhenLocked = containsShowWhenLockedWindow();
if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow
|| containsShowWhenLocked != mLastContainsShowWhenLockedWindow) {
- mService.notifyKeyguardFlagsChanged(null /* callback */,
+ mWmService.notifyKeyguardFlagsChanged(null /* callback */,
getDisplayContent().getDisplayId());
}
mLastContainsDismissKeyguardWindow = containsDismissKeyguard;
@@ -1754,10 +2163,8 @@
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer");
final SurfaceControl.Builder builder = makeAnimationLeash()
.setParent(getAnimationLeashParent())
- .setName(getSurfaceControl() + " - animation-bounds")
- .setSize(getSurfaceWidth(), getSurfaceHeight());
+ .setName(getSurfaceControl() + " - animation-bounds");
final SurfaceControl boundsLayer = builder.build();
- t.setWindowCrop(boundsLayer, getSurfaceWidth(), getSurfaceHeight());
t.show(boundsLayer);
return boundsLayer;
}
@@ -1771,9 +2178,9 @@
final TaskStack stack = getStack();
final Task task = getTask();
if (task != null && task.inFreeformWindowingMode()) {
- task.getRelativePosition(outPosition);
+ task.getRelativeDisplayedPosition(outPosition);
} else if (stack != null) {
- stack.getRelativePosition(outPosition);
+ stack.getRelativeDisplayedPosition(outPosition);
}
// Always use stack bounds in order to have the ability to animate outside the task region.
@@ -1786,10 +2193,22 @@
outBounds.offsetTo(0, 0);
}
+ @Override
+ Rect getDisplayedBounds() {
+ final Task task = getTask();
+ if (task != null) {
+ final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
+ if (!overrideDisplayedBounds.isEmpty()) {
+ return overrideDisplayedBounds;
+ }
+ }
+ return getBounds();
+ }
+
boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
boolean isVoiceInteraction) {
- if (mService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
+ if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
Slog.v(TAG_WM, "applyAnimation: transition animation is disabled or skipped."
+ " atoken=" + this);
@@ -1823,7 +2242,7 @@
getDisplayContent().mAppTransition.canSkipFirstFrame(),
appStackClipMode,
true /* isAppAnimation */),
- mService.mSurfaceAnimationRunner);
+ mWmService.mSurfaceAnimationRunner);
if (a.getZAdjustment() == Animation.ZORDER_TOP) {
mNeedsZBoost = true;
}
@@ -1906,7 +2325,7 @@
final int containingWidth = frame.width();
final int containingHeight = frame.height();
a.initialize(containingWidth, containingHeight, width, height);
- a.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+ a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
}
return a;
}
@@ -2111,7 +2530,7 @@
return;
}
final Rect frame = win.getFrameLw();
- final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
+ final int thumbnailDrawableRes = getTask().mUserId == mWmService.mCurrentUserId
? R.drawable.ic_account_circle
: R.drawable.ic_corp_badge;
final GraphicBuffer thumbnail =
@@ -2215,9 +2634,6 @@
if (mPendingRelaunchCount != 0) {
pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
}
- if (getController() != null) {
- pw.print(prefix); pw.print("controller="); pw.println(getController());
- }
if (mRemovingFromDisplay) {
pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
}
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9633864..c90f5bf 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -48,7 +48,6 @@
surface = dc.makeOverlay()
.setName("BlackSurface")
- .setSize(w, h)
.setColorLayer(true)
.setParent(null) // TODO: Work-around for b/69259549
.build();
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2a216ab..c3d6211 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -69,7 +69,7 @@
try {
ctrl = dc.makeOverlay()
.setName("CircularDisplayMask")
- .setSize(mScreenSize.x, mScreenSize.y) // not a typo
+ .setBufferSize(mScreenSize.x, mScreenSize.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
.build();
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index cc14afc..aea071f 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -131,7 +131,7 @@
if (!mDimming) {
dimAnimatable.getPendingTransaction().destroy(mDimLayer);
}
- }, mHost.mService);
+ }, mHost.mWmService);
}
}
@@ -308,7 +308,6 @@
return false;
} else {
// TODO: Once we use geometry from hierarchy this falls away.
- t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
if (!mDimState.isVisible) {
@@ -334,7 +333,7 @@
SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
- mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
+ mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */);
}
private long getDimDuration(WindowContainer container) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3acacbc..05e8267 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -29,6 +29,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
+import static android.view.InsetsState.TYPE_IME;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
@@ -78,7 +79,6 @@
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
-import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -100,6 +100,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
+import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
@@ -124,6 +125,7 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -151,6 +153,7 @@
import android.view.Gravity;
import android.view.InputChannel;
import android.view.InputDevice;
+import android.view.InsetsState.InternalInsetType;
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -162,6 +165,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.function.TriConsumer;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.DisplayRotationUtil;
import com.android.server.wm.utils.RotationCache;
@@ -205,21 +209,21 @@
/** The containers below are the only child containers the display can have. */
// Contains all window containers that are related to apps (Activities)
- private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mService);
+ private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService);
// Contains all non-app window containers that should be displayed above the app containers
// (e.g. Status bar)
private final AboveAppWindowContainers mAboveAppWindowsContainers =
- new AboveAppWindowContainers("mAboveAppWindowsContainers", mService);
+ new AboveAppWindowContainers("mAboveAppWindowsContainers", mWmService);
// Contains all non-app window containers that should be displayed below the app containers
// (e.g. Wallpaper).
private final NonAppWindowContainers mBelowAppWindowsContainers =
- new NonAppWindowContainers("mBelowAppWindowsContainers", mService);
+ new NonAppWindowContainers("mBelowAppWindowsContainers", mWmService);
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
// WindowContainer which is omitted from screen magnification, as the IME is never magnified.
private final NonMagnifiableWindowContainers mImeWindowsContainers =
- new NonMagnifiableWindowContainers("mImeWindowsContainers", mService);
+ new NonMagnifiableWindowContainers("mImeWindowsContainers", mWmService);
private WindowState mTmpWindow;
private WindowState mTmpWindow2;
@@ -470,14 +474,6 @@
private SurfaceControl mWindowingLayer;
/**
- * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
- * <p>
- * For these surfaces currently we use a surface based on the larger of width or height so we
- * don't have to resize when rotating the display.
- */
- private int mSurfaceSize;
-
- /**
* Sequence number for the current layout pass.
*/
int mLayoutSeq = 0;
@@ -515,6 +511,8 @@
private final PointerEventDispatcher mPointerEventDispatcher;
+ private final InsetsStateController mInsetsStateController;
+
// Last systemUiVisibility we received from status bar.
private int mLastStatusBarVisibility = 0;
// Last systemUiVisibility we dispatched to windows.
@@ -528,7 +526,7 @@
if (w.performShowLocked()) {
pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(
+ mWmService.mWindowPlacerLocked.debugLayoutRepeats(
"updateWindowsAndWallpaperLocked 5", pendingLayoutChanges);
}
}
@@ -559,7 +557,7 @@
private final Consumer<WindowState> mScheduleToastTimeout = w -> {
final int lostFocusUid = mTmpWindow.mOwnerUid;
- final Handler handler = mService.mH;
+ final Handler handler = mWmService.mH;
if (w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == lostFocusUid) {
if (!handler.hasMessages(WINDOW_HIDE_TIMEOUT, w)) {
handler.sendMessageDelayed(handler.obtainMessage(WINDOW_HIDE_TIMEOUT, w),
@@ -621,7 +619,7 @@
private final Consumer<WindowState> mPerformLayout = w -> {
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
- final boolean gone = (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w))
+ final boolean gone = (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w))
|| w.isGoneForLayoutLw();
if (DEBUG_LAYOUT && !w.mLayoutAttached) {
@@ -689,7 +687,7 @@
// If this view is GONE, then skip it -- keep the current frame, and let the caller
// know so they can ignore it if they want. (We do the normal layout for INVISIBLE
// windows, since that means "perform layout as normal, just don't display").
- if (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w)) {
+ if (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w)) {
return;
}
if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame
@@ -724,10 +722,10 @@
mInputMethodTarget);
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
- final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+ final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
final boolean obscuredChanged = w.mObscured !=
mTmpApplySurfaceChangesTransactionState.obscured;
- final RootWindowContainer root = mService.mRoot;
+ final RootWindowContainer root = mWmService.mRoot;
// Update effect.
w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
@@ -818,7 +816,7 @@
}
if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) {
- mService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
+ mWmService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
}
w.updateResizingWindowIfNeeded();
@@ -843,7 +841,7 @@
mDisplay = display;
mDisplayId = display.getDisplayId();
- mWallpaperController = new WallpaperController(mService, this);
+ mWallpaperController = new WallpaperController(mWmService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -860,8 +858,8 @@
mBoundsAnimationController = new BoundsAnimationController(service.mContext,
mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler);
- if (mService.mInputManager != null) {
- final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
+ if (mWmService.mInputManager != null) {
+ final InputChannel inputChannel = mWmService.mInputManager.monitorInput("Display "
+ mDisplayId, mDisplayId);
mPointerEventDispatcher = inputChannel != null
? new PointerEventDispatcher(inputChannel) : null;
@@ -873,29 +871,18 @@
if (isDefaultDisplay) {
// The policy may be invoked right after here, so it requires the necessary default
// fields of this display content.
- mService.mPolicy.setDefaultDisplay(this);
+ mWmService.mPolicy.setDefaultDisplay(this);
}
- if (mService.mDisplayReady) {
+ if (mWmService.mDisplayReady) {
mDisplayPolicy.onConfigurationChanged();
}
- if (mService.mSystemReady) {
+ if (mWmService.mSystemReady) {
mDisplayPolicy.systemReady();
}
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
- // We use this as our arbitrary surface size for buffer-less parents
- // that don't impose cropping on their children. It may need to be larger
- // than the display size because fullscreen windows can be shifted offscreen
- // due to surfaceInsets. 2 times the largest display dimension feels like an
- // appropriately arbitrary number. Eventually we would like to give SurfaceFlinger
- // layers the ability to match their parent sizes and be able to skip
- // such arbitrary size settings.
- mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth) * 2;
-
- final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
- .setSize(mSurfaceSize, mSurfaceSize)
- .setOpaque(true);
+ final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession).setOpaque(true);
mWindowingLayer = b.setName("Display Root").build();
mOverlayLayer = b.setName("Display Overlays").build();
@@ -914,19 +901,20 @@
super.addChild(mImeWindowsContainers, null);
// Add itself as a child to the root container.
- mService.mRoot.addChild(this, null);
+ mWmService.mRoot.addChild(this, null);
// TODO(b/62541591): evaluate whether this is the best spot to declare the
// {@link DisplayContent} ready for use.
mDisplayReady = true;
- mService.mAnimator.addDisplayLocked(mDisplayId);
+ mWmService.mAnimator.addDisplayLocked(mDisplayId);
mInputMonitor = new InputMonitor(service, mDisplayId);
+ mInsetsStateController = new InsetsStateController(this);
}
boolean isReady() {
// The display is ready when the system and the individual display are both ready.
- return mService.mDisplayReady && mDisplayReady;
+ return mWmService.mDisplayReady && mDisplayReady;
}
int getDisplayId() {
@@ -946,7 +934,7 @@
}
private void addWindowToken(IBinder binder, WindowToken token) {
- final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token);
+ final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
if (dc != null) {
// We currently don't support adding a window token to the display if the display
// already has the binder mapped to another token. If there is a use case for supporting
@@ -1058,6 +1046,23 @@
return mDisplayRotation;
}
+ /**
+ * Marks a window as providing insets for the rest of the windows in the system.
+ *
+ * @param type The type of inset this window provides.
+ * @param win The window.
+ * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of
+ * the window should be taken.
+ */
+ void setInsetProvider(@InternalInsetType int type, WindowState win,
+ @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+ mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
+ }
+
+ InsetsStateController getInsetsStateController() {
+ return mInsetsStateController;
+ }
+
@VisibleForTesting
void setDisplayRotation(DisplayRotation displayRotation) {
mDisplayRotation = displayRotation;
@@ -1135,7 +1140,7 @@
/** Notify the configuration change of this display. */
void sendNewConfiguration() {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
+ mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
}
/**
@@ -1210,7 +1215,7 @@
}
screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
// Rotation updates cannot be performed while the previous rotation change
// animation is still in progress. Skip this update. We will try updating
@@ -1218,7 +1223,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
return false;
}
- if (mService.mDisplayFrozen) {
+ if (mWmService.mDisplayFrozen) {
// Even if the screen rotation animation has finished (e.g. isAnimating
// returns false), there is still some time where we haven't yet unfrozen
// the display. We also need to abort rotation here.
@@ -1228,7 +1233,7 @@
}
}
- if (!mService.mDisplayEnabled) {
+ if (!mWmService.mDisplayEnabled) {
// No point choosing a rotation if the display is not enabled.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
return false;
@@ -1259,8 +1264,8 @@
if (hasPinnedStack()) {
mayRotateSeamlessly = false;
}
- for (int i = 0; i < mService.mSessions.size(); i++) {
- if (mService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
+ for (int i = 0; i < mWmService.mSessions.size(); i++) {
+ if (mWmService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
mayRotateSeamlessly = false;
break;
}
@@ -1298,8 +1303,8 @@
mRotation = rotation;
mAltOrientation = altOrientation;
- mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
- mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
+ mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
+ mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
this, WINDOW_FREEZE_TIMEOUT_DURATION);
setLayoutNeeded();
@@ -1307,14 +1312,14 @@
mDisplayPolicy.selectRotationAnimationLw(anim);
if (!rotateSeamlessly) {
- mService.startFreezingDisplayLocked(anim[0], anim[1], this);
+ mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
// startFreezingDisplayLocked can reset the ScreenRotationAnimation.
} else {
// The screen rotation animation uses a screenshot to freeze the screen
// while windows resize underneath.
// When we are rotating seamlessly, we allow the elements to transition
// to their rotated state independently and without a freeze required.
- mService.startSeamlessRotation();
+ mWmService.startSeamlessRotation();
}
return true;
@@ -1329,9 +1334,9 @@
*/
void applyRotationLocked(final int oldRotation, final int rotation) {
mDisplayRotation.setRotation(rotation);
- final boolean rotateSeamlessly = mService.isRotatingSeamlessly();
+ final boolean rotateSeamlessly = mWmService.isRotatingSeamlessly();
ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
- ? null : mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ ? null : mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
// We need to update our screen size information to match the new rotation. If the rotation
// has actually changed then this method will return true and, according to the comment at
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -1344,9 +1349,9 @@
if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
&& screenRotationAnimation.hasScreenshot()) {
if (screenRotationAnimation.setRotation(getPendingTransaction(), rotation,
- MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
+ MAX_ANIMATION_DURATION, mWmService.getTransitionAnimationScaleLocked(),
mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
}
}
@@ -1355,27 +1360,27 @@
rotateSeamlessly);
}, true /* traverseTopToBottom */);
- mService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
+ mWmService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
scheduleAnimation();
forAllWindows(w -> {
if (w.mHasSurface && !rotateSeamlessly) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
w.setOrientationChanging(true);
- mService.mRoot.mOrientationChangeComplete = false;
+ mWmService.mRoot.mOrientationChangeComplete = false;
w.mLastFreezeDuration = 0;
}
w.mReportOrientationChanged = true;
}, true /* traverseTopToBottom */);
if (rotateSeamlessly) {
- mService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
+ mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
}
- for (int i = mService.mRotationWatchers.size() - 1; i >= 0; i--) {
+ for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) {
final WindowManagerService.RotationWatcher rotationWatcher
- = mService.mRotationWatchers.get(i);
+ = mWmService.mRotationWatchers.get(i);
if (rotationWatcher.mDisplayId == mDisplayId) {
try {
rotationWatcher.mWatcher.onRotationChanged(rotation);
@@ -1388,9 +1393,9 @@
// TODO (multi-display): Magnification is supported only for the default display.
// Announce rotation only if we will not animate as we already have the
// windows in final state. Otherwise, we make this call at the rotation end.
- if (screenRotationAnimation == null && mService.mAccessibilityController != null
+ if (screenRotationAnimation == null && mWmService.mAccessibilityController != null
&& isDefaultDisplay) {
- mService.mAccessibilityController.onRotationChangedLocked(this);
+ mWmService.mAccessibilityController.onRotationChangedLocked(this);
}
}
@@ -1424,9 +1429,9 @@
Slog.d(TAG,
"Registering PointerEventListener for DisplayId: " + mDisplayId);
}
- mTapDetector = new TaskTapPointerEventListener(mService, this);
+ mTapDetector = new TaskTapPointerEventListener(mWmService, this);
registerPointerEventListener(mTapDetector);
- registerPointerEventListener(mService.mMousePositionTracker);
+ registerPointerEventListener(mWmService.mMousePositionTracker);
}
}
@@ -1491,7 +1496,7 @@
// because we don't want letter-/pillar-boxing during resize.
final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration
? mDisplayInfo : null;
- mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
+ mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
overrideDisplayInfo);
mBaseDisplayRect.set(0, 0, dw, dh);
@@ -1577,10 +1582,10 @@
config.densityDpi = displayInfo.logicalDensityDpi;
config.colorMode =
- ((displayInfo.isHdr() && mService.hasHdrSupport())
+ ((displayInfo.isHdr() && mWmService.hasHdrSupport())
? Configuration.COLOR_MODE_HDR_YES
: Configuration.COLOR_MODE_HDR_NO)
- | (displayInfo.isWideColorGamut() && mService.hasWideColorGamutSupport()
+ | (displayInfo.isWideColorGamut() && mWmService.hasWideColorGamutSupport()
? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
: Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);
@@ -1592,7 +1597,7 @@
int keyboardPresence = 0;
int navigationPresence = 0;
- final InputDevice[] devices = mService.mInputManager.getInputDevices();
+ final InputDevice[] devices = mWmService.mInputManager.getInputDevices();
final int len = devices != null ? devices.length : 0;
for (int i = 0; i < len; i++) {
InputDevice device = devices[i];
@@ -1603,7 +1608,7 @@
WindowManagerPolicy.PRESENCE_INTERNAL;
// TODO(multi-display): Configure on per-display basis.
- if (mService.mIsTouchDevice) {
+ if (mWmService.mIsTouchDevice) {
if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==
InputDevice.SOURCE_TOUCHSCREEN) {
config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
@@ -1628,7 +1633,7 @@
}
}
- if (config.navigation == Configuration.NAVIGATION_NONAV && mService.mHasPermanentDpad) {
+ if (config.navigation == Configuration.NAVIGATION_NONAV && mWmService.mHasPermanentDpad) {
config.navigation = Configuration.NAVIGATION_DPAD;
navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
}
@@ -1636,10 +1641,10 @@
// Determine whether a hard keyboard is available and enabled.
// TODO(multi-display): Should the hardware keyboard be tied to a display or to a device?
boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
- if (hardKeyboardAvailable != mService.mHardKeyboardAvailable) {
- mService.mHardKeyboardAvailable = hardKeyboardAvailable;
- mService.mH.removeMessages(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
- mService.mH.sendEmptyMessage(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+ if (hardKeyboardAvailable != mWmService.mHardKeyboardAvailable) {
+ mWmService.mHardKeyboardAvailable = hardKeyboardAvailable;
+ mWmService.mH.removeMessages(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+ mWmService.mH.sendEmptyMessage(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
}
mDisplayPolicy.updateConfigurationDependentBehaviors();
@@ -1648,7 +1653,7 @@
config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
- mService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
+ mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}
private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
@@ -1945,7 +1950,7 @@
@Override
void onAppTransitionDone() {
super.onAppTransitionDone();
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
}
/**
@@ -1997,9 +2002,9 @@
@Override
int getOrientation() {
- final WindowManagerPolicy policy = mService.mPolicy;
+ final WindowManagerPolicy policy = mWmService.mPolicy;
- if (mService.mDisplayFrozen) {
+ if (mWmService.mDisplayFrozen) {
if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+ " is frozen, return " + mLastWindowForcedOrientation);
@@ -2040,7 +2045,7 @@
}
void initializeDisplayBaseInfo() {
- final DisplayManagerInternal displayManagerInternal = mService.mDisplayManagerInternal;
+ final DisplayManagerInternal displayManagerInternal = mWmService.mDisplayManagerInternal;
if (displayManagerInternal != null) {
// Bootstrap the default logical display from the display manager.
final DisplayInfo newDisplayInfo = displayManagerInternal.getDisplayInfo(mDisplayId);
@@ -2063,7 +2068,7 @@
*/
private void updateBaseDisplayMetricsIfNeeded() {
// Get real display metrics without overrides from WM.
- mService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo);
+ mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo);
final int orientation = mDisplayInfo.rotation;
final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth;
@@ -2092,7 +2097,7 @@
mInitialDisplayHeight = newHeight;
mInitialDisplayDensity = newDensity;
mInitialDisplayCutout = newCutout;
- mService.reconfigureDisplayLocked(this);
+ mWmService.reconfigureDisplayLocked(this);
}
}
@@ -2143,9 +2148,9 @@
void setForcedDensity(int density, int userId) {
final boolean clear = density == mInitialDisplayDensity;
final boolean updateCurrent = userId == UserHandle.USER_CURRENT;
- if (mService.mCurrentUserId == userId || updateCurrent) {
+ if (mWmService.mCurrentUserId == userId || updateCurrent) {
mBaseDisplayDensity = density;
- mService.reconfigureDisplayLocked(this);
+ mWmService.reconfigureDisplayLocked(this);
}
if (updateCurrent) {
// We are applying existing settings so no need to save it again.
@@ -2155,7 +2160,7 @@
if (density == mInitialDisplayDensity) {
density = 0;
}
- mService.mDisplayWindowSettings.setForcedDensity(this, density, userId);
+ mWmService.mDisplayWindowSettings.setForcedDensity(this, density, userId);
}
/** @param mode {@link #FORCE_SCALING_MODE_AUTO} or {@link #FORCE_SCALING_MODE_DISABLED}. */
@@ -2166,9 +2171,9 @@
mDisplayScalingDisabled = (mode != FORCE_SCALING_MODE_AUTO);
Slog.i(TAG_WM, "Using display scaling mode: " + (mDisplayScalingDisabled ? "off" : "auto"));
- mService.reconfigureDisplayLocked(this);
+ mWmService.reconfigureDisplayLocked(this);
- mService.mDisplayWindowSettings.setForcedScalingMode(this, mode);
+ mWmService.mDisplayWindowSettings.setForcedScalingMode(this, mode);
}
/** If the given width and height equal to initial size, the setting will be cleared. */
@@ -2185,12 +2190,12 @@
Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
updateBaseDisplayMetrics(width, height, mBaseDisplayDensity);
- mService.reconfigureDisplayLocked(this);
+ mWmService.reconfigureDisplayLocked(this);
if (clear) {
width = height = 0;
}
- mService.mDisplayWindowSettings.setForcedSize(this, width, height);
+ mWmService.mDisplayWindowSettings.setForcedSize(this, width, height);
}
void getStableRect(Rect out) {
@@ -2201,7 +2206,7 @@
if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
+ mDisplayId);
- final TaskStack stack = new TaskStack(mService, stackId, controller);
+ final TaskStack stack = new TaskStack(mWmService, stackId, controller);
mTaskStackContainers.addStackToDisplay(stack, onTop);
return stack;
}
@@ -2357,7 +2362,7 @@
@Override
void switchUser() {
super.switchUser();
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
}
private void resetAnimationBackgroundAnimator() {
@@ -2385,24 +2390,24 @@
mUnknownAppVisibilityController.clear();
mAppTransition.removeAppTransitionTimeoutCallbacks();
handleAnimatingStoppedAndTransition();
- mService.stopFreezingDisplayLocked();
+ mWmService.stopFreezingDisplayLocked();
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
if (mPointerEventDispatcher != null && mTapDetector != null) {
unregisterPointerEventListener(mTapDetector);
- unregisterPointerEventListener(mService.mMousePositionTracker);
+ unregisterPointerEventListener(mWmService.mMousePositionTracker);
mTapDetector = null;
}
- mService.mAnimator.removeDisplayLocked(mDisplayId);
+ mWmService.mAnimator.removeDisplayLocked(mDisplayId);
mWindowingLayer.release();
mOverlayLayer.release();
} finally {
mDisplayReady = false;
mRemovingDisplay = false;
}
+
mDisplayPolicy.onDisplayRemoved();
- mInputMonitor.onRemoved();
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
/** Returns true if a removal action is still being deferred. */
@@ -2479,7 +2484,7 @@
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
&& !mDividerControllerLocked.isImeHideRequested();
final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
+ final TaskStack imeTargetStack = mWmService.getImeFocusStackLocked();
final int imeDockSide = (dockVisible && imeTargetStack != null) ?
imeTargetStack.getDockSide() : DOCKED_INVALID;
final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
@@ -2606,13 +2611,12 @@
mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
proto.write(ROTATION, mRotation);
final ScreenRotationAnimation screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
if (screenRotationAnimation != null) {
screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
}
mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
mAppTransition.writeToProto(proto, APP_TRANSITION);
- proto.write(SURFACE_SIZE, mSurfaceSize);
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
@@ -2733,6 +2737,8 @@
mDisplayRotation.dump(prefix, pw);
pw.println();
mInputMonitor.dump(pw, " ");
+ pw.println();
+ mInsetsStateController.dump(prefix, pw);
}
@Override
@@ -2850,11 +2856,11 @@
}
if (imWindowChanged) {
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
setLayoutNeeded();
}
- if (DEBUG_FOCUS_LIGHT || mService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
+ if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
+ mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
+ " Callers=" + Debug.getCallers(4));
final WindowState oldFocus = mCurrentFocus;
@@ -2893,7 +2899,7 @@
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayout(true /*initial*/, updateInputWindows);
} else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
- mService.mRoot.performSurfacePlacement(false);
+ mWmService.mRoot.performSurfacePlacement(false);
}
}
@@ -2958,16 +2964,16 @@
// moving containers or resizing them. Need to investigate the best way to have it automatically
// happen so we don't run into issues with programmers forgetting to do it.
void layoutAndAssignWindowLayersIfNeeded() {
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
setLayoutNeeded();
- if (!mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ if (!mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
assignWindowLayers(false /* setLayoutNeeded */);
}
mInputMonitor.setUpdateInputWindowsNeededLw();
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
@@ -2980,14 +2986,14 @@
if (wsa.mSurfaceController == null) {
return;
}
- if (!mService.mSessions.contains(wsa.mSession)) {
+ if (!mWmService.mSessions.contains(wsa.mSession)) {
Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): "
+ w + " surface=" + wsa.mSurfaceController
+ " token=" + w.mToken
+ " pid=" + w.mSession.mPid
+ " uid=" + w.mSession.mUid);
wsa.destroySurface();
- mService.mForceRemoves.add(w);
+ mWmService.mForceRemoves.add(w);
mTmpWindow = w;
} else if (w.mAppToken != null && w.mAppToken.isClientHidden()) {
Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
@@ -3011,10 +3017,12 @@
// Update display configuration for IME process.
if (mInputMethodWindow != null) {
final int imePid = mInputMethodWindow.mSession.mPid;
- mService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
+ mWmService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
mInputMethodWindow.getDisplayId());
}
computeImeTarget(true /* updateImeTarget */);
+ mInsetsStateController.getSourceProvider(TYPE_IME).setWindow(win,
+ null /* frameProvider */);
}
/**
@@ -3230,7 +3238,7 @@
* Starts the Keyguard exit animation on all windows that don't belong to an app token.
*/
void startKeyguardExitOnNonAppWindows(boolean onWallpaper, boolean goingToShade) {
- final WindowManagerPolicy policy = mService.mPolicy;
+ final WindowManagerPolicy policy = mWmService.mPolicy;
forAllWindows(w -> {
if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)
&& w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
@@ -3259,7 +3267,7 @@
} else if (w.mAttrs.type == TYPE_WALLPAPER) {
mHaveWallpaper = true;
} else if (w.mAttrs.type == TYPE_STATUS_BAR) {
- mHaveKeyguard = mService.mPolicy.isKeyguardDrawnLw();
+ mHaveKeyguard = mWmService.mPolicy.isKeyguardDrawnLw();
}
}
return false;
@@ -3272,28 +3280,28 @@
// if the wallpaper service is disabled on the device, we're never going to have
// wallpaper, don't bother waiting for it
- boolean wallpaperEnabled = mService.mContext.getResources().getBoolean(
+ boolean wallpaperEnabled = mWmService.mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableWallpaperService)
- && mService.mContext.getResources().getBoolean(
+ && mWmService.mContext.getResources().getBoolean(
com.android.internal.R.bool.config_checkWallpaperAtBoot)
- && !mService.mOnlyCore;
+ && !mWmService.mOnlyCore;
if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM,
- "******** booted=" + mService.mSystemBooted
- + " msg=" + mService.mShowingBootMessages
+ "******** booted=" + mWmService.mSystemBooted
+ + " msg=" + mWmService.mShowingBootMessages
+ " haveBoot=" + mHaveBootMsg + " haveApp=" + mHaveApp
+ " haveWall=" + mHaveWallpaper + " wallEnabled=" + wallpaperEnabled
+ " haveKeyguard=" + mHaveKeyguard);
// If we are turning on the screen to show the boot message, don't do it until the boot
// message is actually displayed.
- if (!mService.mSystemBooted && !mHaveBootMsg) {
+ if (!mWmService.mSystemBooted && !mHaveBootMsg) {
return true;
}
// If we are turning on the screen after the boot is completed normally, don't do so until
// we have the application and wallpaper.
- if (mService.mSystemBooted
+ if (mWmService.mSystemBooted
&& ((!mHaveApp && !mHaveKeyguard) || (wallpaperEnabled && !mHaveWallpaper))) {
return true;
}
@@ -3359,7 +3367,7 @@
mLastDispatchedSystemUiVisibility = visibility;
if (isDefaultDisplay) {
- mService.mInputManager.setSystemUiVisibility(visibility);
+ mWmService.mInputManager.setSystemUiVisibility(visibility);
}
updateSystemUiVisibility(visibility, globalDiff);
return true;
@@ -3388,13 +3396,13 @@
void reevaluateStatusBarVisibility() {
int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
if (updateStatusBarVisibilityLocked(visibility)) {
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
}
void onWindowFreezeTimeout() {
Slog.w(TAG_WM, "Window freeze timeout expired.");
- mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
+ mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
forAllWindows(w -> {
if (!w.getOrientationChanging()) {
@@ -3402,28 +3410,28 @@
}
w.orientationChangeTimedOut();
w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mService.mDisplayFreezeTime);
+ - mWmService.mDisplayFreezeTime);
Slog.w(TAG_WM, "Force clearing orientation change: " + w);
}, true /* traverseTopToBottom */);
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
void waitForAllWindowsDrawn() {
- final WindowManagerPolicy policy = mService.mPolicy;
+ final WindowManagerPolicy policy = mWmService.mPolicy;
forAllWindows(w -> {
final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs);
if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
w.mWinAnimator.mDrawState = DRAW_PENDING;
// Force add to mResizingWindows.
w.resetLastContentInsets();
- mService.mWaitingForDrawn.add(w);
+ mWmService.mWaitingForDrawn.add(w);
}
}, true /* traverseTopToBottom */);
}
// TODO: Super crazy long method that should be broken down...
void applySurfaceChangesTransaction(boolean recoveringMemory) {
- final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+ final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
mTmpUpdateAllDrawn.clear();
@@ -3470,6 +3478,7 @@
pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
"after finishPostLayoutPolicyLw", pendingLayoutChanges);
+ mInsetsStateController.onPostLayout();
} while (pendingLayoutChanges != 0);
mTmpApplySurfaceChangesTransactionState.reset();
@@ -3479,7 +3488,7 @@
prepareSurfaces();
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
- mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
+ mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
mTmpApplySurfaceChangesTransactionState.preferredModeId,
@@ -3488,7 +3497,7 @@
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
mLastWallpaperVisible = wallpaperVisible;
- mService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
+ mWmService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
}
while (!mTmpUpdateAllDrawn.isEmpty()) {
@@ -3537,10 +3546,6 @@
}
}
- int getSurfaceSize() {
- return mSurfaceSize;
- }
-
void performLayout(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
@@ -3590,7 +3595,7 @@
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
+ mWmService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
}
/**
@@ -3600,7 +3605,7 @@
* @param config of the output bitmap
*/
Bitmap screenshotDisplayLocked(Bitmap.Config config) {
- if (!mService.mPolicy.isScreenOn()) {
+ if (!mWmService.mPolicy.isScreenOn()) {
if (DEBUG_SCREENSHOT) {
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
}
@@ -3628,7 +3633,7 @@
convertCropForSurfaceFlinger(frame, rot, dw, dh);
final ScreenRotationAnimation screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
+ mWmService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
final boolean inRotation = screenRotationAnimation != null &&
screenRotationAnimation.isAnimating();
if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
@@ -3684,11 +3689,11 @@
mTmpWindow = w;
w.setDisplayLayoutNeeded();
w.finishSeamlessRotation(true /* timeout */);
- mService.markForSeamlessRotation(w, false);
+ mWmService.markForSeamlessRotation(w, false);
}, true /* traverseTopToBottom */);
if (mTmpWindow != null) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -3716,20 +3721,20 @@
@Override
void onDescendantOverrideConfigurationChanged() {
setLayoutNeeded();
- mService.requestTraversal();
+ mWmService.requestTraversal();
}
boolean okToDisplay() {
if (mDisplayId == DEFAULT_DISPLAY) {
- return !mService.mDisplayFrozen
- && mService.mDisplayEnabled && mService.mPolicy.isScreenOn();
+ return !mWmService.mDisplayFrozen
+ && mWmService.mDisplayEnabled && mWmService.mPolicy.isScreenOn();
}
return mDisplayInfo.state == Display.STATE_ON;
}
boolean okToAnimate() {
return okToDisplay() &&
- (mDisplayId != DEFAULT_DISPLAY || mService.mPolicy.okToAnimate());
+ (mDisplayId != DEFAULT_DISPLAY || mWmService.mPolicy.okToAnimate());
}
static final class TaskForResizePointSearchResult {
@@ -3947,7 +3952,7 @@
} else if (stack == mSplitScreenPrimaryStack) {
mSplitScreenPrimaryStack = null;
// Re-set the split-screen create mode whenever the split-screen stack is removed.
- mService.setDockedStackCreateStateLocked(
+ mWmService.setDockedStackCreateStateLocked(
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
@@ -4165,7 +4170,7 @@
}
final int orientation = super.getOrientation();
- boolean isCar = mService.mContext.getPackageManager().hasSystemFeature(
+ boolean isCar = mWmService.mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE);
if (isCar) {
// In a car, you cannot physically rotate the screen, so it doesn't make sense to
@@ -4346,10 +4351,10 @@
wt.assignLayer(t, j);
wt.assignChildLayers(t);
- int layer = mService.mPolicy.getWindowLayerFromTypeLw(
+ int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(
wt.windowType, wt.mOwnerCanManageAppTokens);
- if (needAssignIme && layer >= mService.mPolicy.getWindowLayerFromTypeLw(
+ if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
TYPE_INPUT_METHOD_DIALOG, true)) {
imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
needAssignIme = false;
@@ -4372,9 +4377,9 @@
*/
private final Comparator<WindowToken> mWindowComparator = (token1, token2) ->
// Tokens with higher base layer are z-ordered on-top.
- mService.mPolicy.getWindowLayerFromTypeLw(token1.windowType,
+ mWmService.mPolicy.getWindowLayerFromTypeLw(token1.windowType,
token1.mOwnerCanManageAppTokens)
- < mService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
+ < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
token2.mOwnerCanManageAppTokens) ? -1 : 1;
private final Predicate<WindowState> mGetOrientingWindow = w -> {
@@ -4404,7 +4409,7 @@
@Override
int getOrientation() {
- final WindowManagerPolicy policy = mService.mPolicy;
+ final WindowManagerPolicy policy = mWmService.mPolicy;
// Find a window requesting orientation.
final WindowState win = getWindow(mGetOrientingWindow);
@@ -4412,7 +4417,7 @@
final int req = win.mAttrs.screenOrientation;
if (policy.isKeyguardHostWindow(win.mAttrs)) {
mLastKeyguardForcedOrientation = req;
- if (mService.mKeyguardGoingAway) {
+ if (mWmService.mKeyguardGoingAway) {
// Keyguard can't affect the orientation if it is going away...
mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
return SCREEN_ORIENTATION_UNSET;
@@ -4470,7 +4475,7 @@
};
SurfaceControl.Builder makeSurface(SurfaceSession s) {
- return mService.makeSurfaceBuilder(s)
+ return mWmService.makeSurfaceBuilder(s)
.setParent(mWindowingLayer);
}
@@ -4482,9 +4487,7 @@
@Override
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
SurfaceSession s = child != null ? child.getSession() : getSession();
- final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
- b.setSize(mSurfaceSize, mSurfaceSize);
-
+ final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s);
if (child == null) {
return b;
}
@@ -4500,7 +4503,7 @@
* and other potpourii.
*/
SurfaceControl.Builder makeOverlay() {
- return mService.makeSurfaceBuilder(mSession)
+ return mWmService.makeSurfaceBuilder(mSession)
.setParent(mOverlayLayer);
}
@@ -4599,7 +4602,7 @@
@Override
void prepareSurfaces() {
final ScreenRotationAnimation screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
mPendingTransaction.setMatrix(mWindowingLayer,
@@ -4687,7 +4690,7 @@
+ mDisplayId + " Callers=" + Debug.getCallers(5));
}
mAppTransition.setReady();
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
}
@@ -4717,7 +4720,7 @@
mWallpaperMayChange = true;
// Since the window list has been rebuilt, focus might have to be recomputed since the
// actual order of windows might have changed again.
- mService.mFocusMayChange = true;
+ mWmService.mFocusMayChange = true;
pendingLayoutChanges |= changes;
}
@@ -4730,7 +4733,19 @@
return mDisplay.supportsSystemDecorations()
// TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
// (b/114338689) whenever vr 2d display id is set.
- || mDisplayId == mService.mVr2dDisplayId
- || mService.mForceDesktopModeOnExternalDisplays;
+ || mDisplayId == mWmService.mVr2dDisplayId
+ || mWmService.mForceDesktopModeOnExternalDisplays;
+ }
+
+ /**
+ * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
+ * {@link #mOverlayLayer} to the specified surfaceControl.
+ *
+ * @param surfaceControlHandle The handle for the new SurfaceControl, where the DisplayContent's
+ * surfaces will be re-parented to.
+ */
+ void reparentDisplayContent(IBinder surfaceControlHandle) {
+ mPendingTransaction.reparent(mWindowingLayer, surfaceControlHandle)
+ .reparent(mOverlayLayer, surfaceControlHandle);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c16f95e..581cec9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,6 +25,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -44,7 +45,6 @@
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
@@ -128,6 +128,7 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
@@ -804,6 +805,11 @@
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.setKeyguardCandidateLw(win);
}
+ mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win,
+ (displayFrames, windowState, rect) -> {
+ rect.top = 0;
+ rect.bottom = getStatusBarHeight(displayFrames);
+ });
break;
case TYPE_NAVIGATION_BAR:
mContext.enforceCallingOrSelfPermission(
@@ -818,6 +824,8 @@
mNavigationBarController.setWindow(win);
mNavigationBarController.setOnBarVisibilityChangedListener(
mNavBarVisibilityListener, true);
+ mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR,
+ win, null /* frameProvider */);
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
case TYPE_NAVIGATION_BAR_PANEL:
@@ -845,9 +853,11 @@
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.setKeyguardCandidateLw(null);
}
+ mDisplayContent.setInsetProvider(TYPE_TOP_BAR, null, null);
} else if (mNavigationBar == win) {
mNavigationBar = null;
mNavigationBarController.setWindow(null);
+ mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, null, null);
}
if (mLastFocusedWindow == win) {
mLastFocusedWindow = null;
@@ -855,6 +865,11 @@
mScreenDecorWindows.remove(win);
}
+ private int getStatusBarHeight(DisplayFrames displayFrames) {
+ return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation],
+ displayFrames.mDisplayCutoutSafe.top);
+ }
+
/**
* Control the animation to run when a window's state changes. Return a
* non-0 number to force the animation to a specific resource ID, or 0
@@ -2306,12 +2321,6 @@
&& windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mTopDockedOpaqueOrDimmingWindowState = win;
}
-
- // Take note if a window wants to acquire a sleep token.
- if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
- && win.canAcquireSleepToken()) {
- mWindowSleepTokenNeeded = true;
- }
}
/**
@@ -2825,6 +2834,9 @@
return 0;
}
+ mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(
+ mTopFullscreenOpaqueWindowState);
+
int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
& ~mResettingSystemUiFlags
& ~mForceClearedSystemUiFlags;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index f1d1e49..7aabc15 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,6 +31,7 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.power.V1_0.PowerHint;
+import android.net.Uri;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -57,6 +59,7 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final DisplayPolicy mDisplayPolicy;
+ private final DisplayWindowSettings mDisplayWindowSettings;
private final Context mContext;
private final Object mLock;
@@ -71,10 +74,6 @@
private StatusBarManagerInternal mStatusBarManagerInternal;
private SettingsObserver mSettingsObserver;
- // Default display does not rotate, apps that require non-default orientation will have to
- // have the orientation emulated.
- private boolean mForceDefaultOrientation;
-
private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@VisibleForTesting
@@ -93,6 +92,13 @@
private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
private int mUserRotation = Surface.ROTATION_0;
+ /**
+ * A flag to indicate if the display rotation should be fixed to user specified rotation
+ * regardless of all other states (including app requrested orientation). {@code true} the
+ * display rotation should be fixed to user specified rotation, {@code false} otherwise.
+ */
+ private boolean mFixedToUserRotation;
+
private int mDemoHdmiRotation;
private int mDemoRotation;
private boolean mDemoHdmiRotationLock;
@@ -100,15 +106,17 @@
DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
this(service, displayContent, displayContent.getDisplayPolicy(),
- service.mContext, service.getWindowManagerLock());
+ service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
}
@VisibleForTesting
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
- DisplayPolicy displayPolicy, Context context, Object lock) {
+ DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
+ Context context, Object lock) {
mService = service;
mDisplayContent = displayContent;
mDisplayPolicy = displayPolicy;
+ mDisplayWindowSettings = displayWindowSettings;
mContext = context;
mLock = lock;
isDefaultDisplay = displayContent.isDefaultDisplay;
@@ -204,12 +212,19 @@
// so if the orientation is forced, we need to respect that no matter what.
final boolean isTv = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
- mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
- res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
- // For debug purposes the next line turns this feature off with:
- // $ adb shell setprop config.override_forced_orient true
- // $ adb shell wm size reset
- !"true".equals(SystemProperties.get("config.override_forced_orient"));
+ final boolean forceDefaultOrientationInRes =
+ res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation);
+ final boolean forceDefaultOrienation =
+ ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv)
+ && forceDefaultOrientationInRes
+ // For debug purposes the next line turns this feature off with:
+ // $ adb shell setprop config.override_forced_orient true
+ // $ adb shell wm size reset
+ && !"true".equals(SystemProperties.get("config.override_forced_orient"));
+ // Configuration says we force to use the default orientation. We can fall back to fix
+ // rotation to only user rotation. As long as OEM doesn't change user rotation then the
+ // rotation of this display is effectively stuck at 0 deg.
+ setFixedToUserRotation(forceDefaultOrienation);
}
void setRotation(int rotation) {
@@ -227,7 +242,14 @@
}
}
- void restoreUserRotation(int userRotationMode, int userRotation) {
+ void restoreSettings(int userRotationMode, int userRotation,
+ boolean fixedToUserRotation) {
+ mFixedToUserRotation = fixedToUserRotation;
+
+ // We will retrieve user rotation and user rotation mode from settings for default display.
+ if (isDefaultDisplay) {
+ return;
+ }
if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
&& userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
@@ -243,6 +265,18 @@
mUserRotation = userRotation;
}
+ void setFixedToUserRotation(boolean fixedToUserRotation) {
+ if (mFixedToUserRotation == fixedToUserRotation) {
+ return;
+ }
+
+ mFixedToUserRotation = fixedToUserRotation;
+ mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent,
+ fixedToUserRotation);
+ mService.updateRotation(true /* alwaysSendConfiguration */,
+ false /* forceRelayout */);
+ }
+
private void setUserRotation(int userRotationMode, int userRotation) {
if (isDefaultDisplay) {
// We'll be notified via settings listener, so we don't need to update internal values.
@@ -265,7 +299,7 @@
mUserRotation = userRotation;
changed = true;
}
- mService.mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
+ mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
userRotation);
if (changed) {
mService.updateRotation(true /* alwaysSendConfiguration */,
@@ -291,9 +325,8 @@
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
}
- /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
- boolean isDefaultOrientationForced() {
- return mForceDefaultOrientation;
+ boolean isFixedToUserRotation() {
+ return mFixedToUserRotation;
}
public int getLandscapeRotation() {
@@ -399,6 +432,12 @@
* screen is switched off.
*/
private boolean needSensorRunning() {
+ if (mFixedToUserRotation) {
+ // We are sure we only respect user rotation settings, so we are sure we will not
+ // support sensor rotation.
+ return false;
+ }
+
if (mSupportAutoRotation) {
if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|| mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
@@ -459,8 +498,8 @@
);
}
- if (mForceDefaultOrientation) {
- return Surface.ROTATION_0;
+ if (mFixedToUserRotation) {
+ return mUserRotation;
}
int sensorRotation = mOrientationListener != null
@@ -701,8 +740,8 @@
// demo, hdmi, vr, etc mode.
// Determine if the rotation is currently forced.
- if (mForceDefaultOrientation) {
- return false; // Rotation is forced to default orientation.
+ if (mFixedToUserRotation) {
+ return false; // Rotation is forced to user settings.
}
final int lidState = mDisplayPolicy.getLidState();
@@ -861,6 +900,7 @@
pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
+ pw.println(prefix + " mFixedToUserRotation=" + mFixedToUserRotation);
}
private class OrientationListener extends WindowOrientationListener {
@@ -945,4 +985,10 @@
}
}
}
+
+ @VisibleForTesting
+ interface ContentObserverRegister {
+ void registerContentObserver(Uri uri, boolean notifyForDescendants,
+ ContentObserver observer, @UserIdInt int userHandle);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index 864f7e1..55b3def 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -90,7 +90,7 @@
public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
synchronized (mGlobalLock) {
if (mContainer != null) {
- mContainer.mService.setNewDisplayOverrideConfiguration(overrideConfiguration,
+ mContainer.mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration,
mContainer);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index f7dfd3f..45d77de 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -80,6 +80,7 @@
private boolean mShouldShowWithInsecureKeyguard = false;
private boolean mShouldShowSystemDecors = false;
private boolean mShouldShowIme = false;
+ private boolean mFixedToUserRotation;
private Entry(String name) {
mName = name;
@@ -97,7 +98,8 @@
&& mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
&& !mShouldShowWithInsecureKeyguard
&& !mShouldShowSystemDecors
- && !mShouldShowIme;
+ && !mShouldShowIme
+ && !mFixedToUserRotation;
}
}
@@ -186,6 +188,13 @@
writeSettingsIfNeeded(entry, displayInfo);
}
+ void setFixedToUserRotation(DisplayContent displayContent, boolean fixedToUserRotation) {
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final Entry entry = getOrCreateEntry(displayInfo);
+ entry.mFixedToUserRotation = fixedToUserRotation;
+ writeSettingsIfNeeded(entry, displayInfo);
+ }
+
private int getWindowingModeLocked(Entry entry, int displayId) {
int windowingMode = entry != null ? entry.mWindowingMode
: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -331,7 +340,8 @@
displayInfo.overscanRight = entry.mOverscanRight;
displayInfo.overscanBottom = entry.mOverscanBottom;
- dc.getDisplayRotation().restoreUserRotation(entry.mUserRotationMode, entry.mUserRotation);
+ dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
+ entry.mUserRotation, entry.mFixedToUserRotation);
if (entry.mForcedDensity != 0) {
dc.mBaseDisplayDensity = entry.mForcedDensity;
@@ -458,6 +468,8 @@
"shouldShowWithInsecureKeyguard");
entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
+ entry.mFixedToUserRotation = getBooleanAttribute(parser,
+ "fixedToUserRotation");
mEntries.put(name, entry);
}
XmlUtils.skipCurrentTag(parser);
@@ -541,6 +553,10 @@
if (entry.mShouldShowIme) {
out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
}
+ if (entry.mFixedToUserRotation) {
+ out.attribute(null, "fixedToUserRotation",
+ Boolean.toString(entry.mFixedToUserRotation));
+ }
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 7ed078a..a667d67 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -31,13 +31,12 @@
import android.view.Display;
import android.view.IWindow;
import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
import com.android.internal.util.Preconditions;
-import android.view.InputWindowHandle;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
+
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -71,11 +70,18 @@
new IDragDropCallback() {});
boolean dragDropActiveLocked() {
- return mDragState != null;
+ return mDragState != null && !mDragState.isClosing();
}
- InputWindowHandle getInputWindowHandleLocked() {
- return mDragState.getInputWindowHandle();
+ void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+ mDragState.showInputSurface(t, displayId);
+ }
+
+ void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (mDragState != null) {
+ // TODO: Are we guaranteed to get here?
+ mDragState.hideInputSurface(t, displayId);
+ }
}
void registerCallback(IDragDropCallback callback) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index a379266..607ee76 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -31,23 +31,24 @@
import android.annotation.Nullable;
import android.content.ClipData;
import android.content.ClipDescription;
-import android.content.Context;
import android.graphics.Point;
+import android.graphics.Rect;
import android.hardware.input.InputManager;
+import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.IUserManager;
import android.os.UserManagerInternal;
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
+import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
+import android.view.InputWindowHandle;
import android.view.PointerIcon;
import android.view.SurfaceControl;
import android.view.View;
@@ -57,8 +58,6 @@
import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.LocalServices;
-import android.view.InputApplicationHandle;
-import android.view.InputWindowHandle;
import java.util.ArrayList;
@@ -118,6 +117,17 @@
private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
private Point mDisplaySize = new Point();
+ // A surface used to catch input events for the drag-and-drop operation.
+ SurfaceControl mInputSurface;
+
+ private final Rect mTmpClipRect = new Rect();
+
+ /**
+ * Whether we are finishing this drag and drop. This starts with {@code false}, and is set to
+ * {@code true} when {@link #closeLocked()} is called.
+ */
+ private boolean mIsClosing;
+
DragState(WindowManagerService service, DragDropController controller, IBinder token,
SurfaceControl surface, int flags, IBinder localWin) {
mService = service;
@@ -127,6 +137,46 @@
mFlags = flags;
mLocalWin = localWin;
mNotifiedWindows = new ArrayList<WindowState>();
+
+ }
+
+ boolean isClosing() {
+ return mIsClosing;
+ }
+
+ void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (displayId != mDisplayContent.getDisplayId()) {
+ return;
+ }
+
+ if (mInputSurface != null) {
+ t.hide(mInputSurface);
+ }
+ }
+
+ void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (displayId != mDisplayContent.getDisplayId()) {
+ return;
+ }
+
+ if (mInputSurface == null) {
+ mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
+ .getSession()).setContainerLayer(true)
+ .setName("Drag and Drop Input Consumer").build();
+ }
+ final InputWindowHandle h = getInputWindowHandle();
+ if (h == null) {
+ Slog.w(TAG_WM, "Drag is in progress but there is no "
+ + "drag window handle.");
+ return;
+ }
+
+ t.show(mInputSurface);
+ t.setInputWindowInfo(mInputSurface, h);
+ t.setLayer(mInputSurface, Integer.MAX_VALUE);
+
+ mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+ t.setWindowCrop(mInputSurface, mTmpClipRect);
}
/**
@@ -134,6 +184,7 @@
* DragDropController#mDragState becomes null.
*/
void closeLocked() {
+ mIsClosing = true;
// Unregister the input interceptor.
if (mInputInterceptor != null) {
if (DEBUG_DRAG)
@@ -218,7 +269,7 @@
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController);
- mDragApplicationHandle = new InputApplicationHandle(null);
+ mDragApplicationHandle = new InputApplicationHandle(new Binder());
mDragApplicationHandle.name = "drag";
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -226,7 +277,7 @@
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
display.getDisplayId());
mDragWindowHandle.name = "drag";
- mDragWindowHandle.inputChannel = mServerChannel;
+ mDragWindowHandle.token = mServerChannel.getToken();
mDragWindowHandle.layer = getDragLayerLocked();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index fddf6ca..7cb4a43 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -32,7 +31,6 @@
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
class EmulatorDisplayOverlay {
private static final String TAG = TAG_WITH_CLASS_NAME ? "EmulatorDisplayOverlay" : TAG_WM;
@@ -59,7 +57,7 @@
try {
ctrl = dc.makeOverlay()
.setName("EmulatorDisplayOverlay")
- .setSize(mScreenSize.x, mScreenSize.y)
+ .setBufferSize(mScreenSize.x, mScreenSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
ctrl.setLayer(zOrder);
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 49bedc9..4df5a0b 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import android.graphics.Rect;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -25,6 +27,8 @@
import android.view.InputApplicationHandle;
import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
+import android.util.Slog;
import java.io.PrintWriter;
@@ -39,6 +43,9 @@
final int mClientPid;
final UserHandle mClientUser;
+ final SurfaceControl mInputSurface;
+ Rect mTmpClipRect = new Rect();
+
InputConsumerImpl(WindowManagerService service, IBinder token, String name,
InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
mService = service;
@@ -58,14 +65,14 @@
}
mService.mInputManager.registerInputChannel(mServerChannel, null);
- mApplicationHandle = new InputApplicationHandle(null);
+ mApplicationHandle = new InputApplicationHandle(new Binder());
mApplicationHandle.name = name;
mApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mWindowHandle = new InputWindowHandle(mApplicationHandle, null, displayId);
mWindowHandle.name = name;
- mWindowHandle.inputChannel = mServerChannel;
+ mWindowHandle.token = mServerChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
mWindowHandle.layer = getLayerLw(mWindowHandle.layoutParamsType);
mWindowHandle.layoutParamsFlags = 0;
@@ -80,6 +87,10 @@
mWindowHandle.ownerUid = Process.myUid();
mWindowHandle.inputFeatures = 0;
mWindowHandle.scaleFactor = 1.0f;
+
+ mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
+ .getSession()).setContainerLayer(true).setName("Input Consumer " + name)
+ .build();
}
void linkToDeathRecipient() {
@@ -102,12 +113,33 @@
mToken.unlinkToDeath(this, 0);
}
- void layout(int dw, int dh) {
- mWindowHandle.touchableRegion.set(0, 0, dw, dh);
- mWindowHandle.frameLeft = 0;
- mWindowHandle.frameTop = 0;
- mWindowHandle.frameRight = dw;
- mWindowHandle.frameBottom = dh;
+ void layout(SurfaceControl.Transaction t, int dw, int dh) {
+ t.setPosition(mInputSurface, 0, 0);
+
+ mTmpClipRect.set(0, 0, dw, dh);
+ t.setWindowCrop(mInputSurface, mTmpClipRect);
+ }
+
+ void layout(SurfaceControl.Transaction t, Rect r) {
+ t.setPosition(mInputSurface, r.left, r.top);
+ mTmpClipRect.set(0, 0, r.width(), r.height());
+ t.setWindowCrop(mInputSurface, mTmpClipRect);
+ }
+
+ void hide(SurfaceControl.Transaction t) {
+ t.hide(mInputSurface);
+ }
+
+ void show(SurfaceControl.Transaction t, WindowState w) {
+ t.show(mInputSurface);
+ t.setInputWindowInfo(mInputSurface, mWindowHandle);
+ t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
+ }
+
+ void show(SurfaceControl.Transaction t, int layer) {
+ t.show(mInputSurface);
+ t.setInputWindowInfo(mInputSurface, mWindowHandle);
+ t.setLayer(mInputSurface, layer);
}
private int getLayerLw(int windowType) {
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 92ea1a9..639ed02 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -6,21 +6,16 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.app.ActivityManager;
import android.os.Debug;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Slog;
+import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
-import android.view.InputApplicationHandle;
import com.android.server.input.InputManagerService;
-import android.view.InputWindowHandle;
-import android.view.InputChannel;
import java.io.PrintWriter;
-import java.util.HashMap;
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
private final WindowManagerService mService;
@@ -72,8 +67,7 @@
* Called by the InputManager.
*/
@Override
- public long notifyANR(InputApplicationHandle inputApplicationHandle,
- IBinder token, String reason) {
+ public long notifyANR(IBinder token, String reason) {
AppWindowToken appWindowToken = null;
WindowState windowState = null;
boolean aboveSystem = false;
@@ -84,9 +78,6 @@
appWindowToken = windowState.mAppToken;
}
}
- if (appWindowToken == null && inputApplicationHandle != null) {
- appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
- }
if (windowState != null) {
Slog.i(TAG_WM, "Input event dispatching timed out "
@@ -116,9 +107,7 @@
if (appWindowToken != null && appWindowToken.appToken != null) {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
- final AppWindowContainerController controller = appWindowToken.getController();
- final boolean abort = controller != null
- && controller.keyDispatchingTimedOut(reason,
+ final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,
(windowState != null) ? windowState.mSession.mPid : -1);
if (!abort) {
// The activity manager declined to abort dispatching.
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 83d32c8ad..88b22cb 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -44,6 +44,7 @@
import android.view.InputChannel;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
import com.android.server.policy.WindowManagerPolicy;
@@ -61,9 +62,8 @@
// When true, need to call updateInputWindowsLw().
private boolean mUpdateInputWindowsNeeded = true;
- // Array of window handles to provide to the input dispatcher.
- private InputWindowHandle[] mInputWindowHandles;
- private int mInputWindowHandleCount;
+ // Currently focused input window handle.
+ private InputWindowHandle mFocusedInputWindowHandle;
private boolean mDisableWallpaperTouchEvents;
private final Rect mTmpRect = new Rect();
@@ -72,6 +72,8 @@
private int mDisplayId;
+ SurfaceControl.Transaction mInputTransaction = new SurfaceControl.Transaction();
+
/**
* The set of input consumer added to the window manager by name, which consumes input events
* for the windows below it.
@@ -126,6 +128,7 @@
private boolean disposeInputConsumer(InputConsumerImpl consumer) {
if (consumer != null) {
consumer.disposeChannelsLw();
+ consumer.hide(mInputTransaction);
return true;
}
return false;
@@ -137,7 +140,16 @@
void layoutInputConsumers(int dw, int dh) {
for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
- mInputConsumers.valueAt(i).layout(dw, dh);
+ mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+ }
+ }
+
+ // The visibility of the input consumers is recomputed each time we
+ // update the input windows. We use a model where consumers begin invisible
+ // (set so by this function) and must meet some condition for visibility on each update.
+ void resetInputConsumers(SurfaceControl.Transaction t) {
+ for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
+ mInputConsumers.valueAt(i).hide(t);
}
}
@@ -177,18 +189,7 @@
}
- private void addInputWindowHandle(final InputWindowHandle windowHandle) {
- if (mInputWindowHandles == null) {
- mInputWindowHandles = new InputWindowHandle[16];
- }
- if (mInputWindowHandleCount >= mInputWindowHandles.length) {
- mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
- mInputWindowHandleCount * 2);
- }
- mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
- }
-
- void addInputWindowHandle(final InputWindowHandle inputWindowHandle,
+ void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
final WindowState child, int flags, final int type, final boolean isVisible,
final boolean hasFocus, final boolean hasWallpaper) {
// Add a window to our list of input windows.
@@ -214,6 +215,11 @@
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
+ // Surface insets are hardcoded to be the same in all directions
+ // and we could probably deprecate the "left/right/top/bottom" concept.
+ // we avoid reintroducing this concept by just choosing one of them here.
+ inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left;
+
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
// to be inversely scaled to map from what is on screen
@@ -227,12 +233,9 @@
Slog.d(TAG_WM, "addInputWindowHandle: "
+ child + ", " + inputWindowHandle);
}
- addInputWindowHandle(inputWindowHandle);
- }
- private void clearInputWindowHandlesLw() {
- while (mInputWindowHandleCount != 0) {
- mInputWindowHandles[--mInputWindowHandleCount] = null;
+ if (hasFocus) {
+ mFocusedInputWindowHandle = inputWindowHandle;
}
}
@@ -261,14 +264,9 @@
if (DEBUG_DRAG) {
Log.d(TAG_WM, "Inserting drag window");
}
- final InputWindowHandle dragWindowHandle =
- mService.mDragDropController.getInputWindowHandleLocked();
- if (dragWindowHandle == null) {
- Slog.w(TAG_WM, "Drag is in progress but there is no "
- + "drag window handle.");
- } else if (dragWindowHandle.displayId == mDisplayId) {
- addInputWindowHandle(dragWindowHandle);
- }
+ mService.mDragDropController.showInputSurface(mInputTransaction, mDisplayId);
+ } else {
+ mService.mDragDropController.hideInputSurface(mInputTransaction, mDisplayId);
}
final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked();
@@ -276,14 +274,9 @@
if (DEBUG_TASK_POSITIONING) {
Log.d(TAG_WM, "Inserting window handle for repositioning");
}
- final InputWindowHandle dragWindowHandle =
- mService.mTaskPositioningController.getDragWindowHandleLocked();
- if (dragWindowHandle == null) {
- Slog.e(TAG_WM,
- "Repositioning is in progress but there is no drag window handle.");
- } else if (dragWindowHandle.displayId == mDisplayId) {
- addInputWindowHandle(dragWindowHandle);
- }
+ mService.mTaskPositioningController.showInputSurface(mInputTransaction, mDisplayId);
+ } else {
+ mService.mTaskPositioningController.hideInputSurface(mInputTransaction, mDisplayId);
}
// Add all windows on the default display.
@@ -362,12 +355,6 @@
}
}
- void onRemoved() {
- // If DisplayContent removed, we need find a way to remove window handles of this display
- // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
- mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
- }
-
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
InputConsumerImpl navInputConsumer;
InputConsumerImpl pipInputConsumer;
@@ -401,16 +388,16 @@
final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
wallpaperController = dc.mWallpaperController;
- dc.forAllWindows(this, true /* traverseTopToBottom */);
+ resetInputConsumers(mInputTransaction);
+
+ dc.forAllWindows(this,
+ true /* traverseTopToBottom */);
+
if (mAddWallpaperInputConsumerHandle) {
- // No visible wallpaper found, add the wallpaper input consumer at the end.
- addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+ wallpaperInputConsumer.show(mInputTransaction, 0);
}
- // Send windows to native code.
- mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
-
- clearInputWindowHandlesLw();
+ mInputTransaction.apply();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
@@ -420,7 +407,7 @@
final InputChannel inputChannel = w.mInputChannel;
final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
if (inputChannel == null || inputWindowHandle == null || w.mRemoved
- || w.canReceiveTouchInput()) {
+ || w.cantReceiveTouchInput()) {
// Skip this window because it cannot possibly receive input.
return;
}
@@ -438,41 +425,36 @@
&& recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
if (recentsAnimationController.updateInputConsumerForApp(
recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
- addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
+ recentsAnimationInputConsumer.show(mInputTransaction, w);
mAddRecentsAnimationInputConsumerHandle = false;
}
- // If the target app window does not yet exist, then we don't add the input
- // consumer window, but also don't add the app window below.
- return;
}
}
if (w.inPinnedWindowingMode()) {
- if (mAddPipInputConsumerHandle
- && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
+ if (mAddPipInputConsumerHandle) {
// Update the bounds of the Pip input consumer to match the window bounds.
w.getBounds(mTmpRect);
+ pipInputConsumer.layout(mInputTransaction, mTmpRect);
+
+ // The touchable region is relative to the surface top-left
+ mTmpRect.offsetTo(0, 0);
pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
- addInputWindowHandle(pipInputConsumer.mWindowHandle);
+ pipInputConsumer.show(mInputTransaction, w);
mAddPipInputConsumerHandle = false;
}
- // TODO: Fix w.canReceiveTouchInput() to handle this case
- if (!hasFocus) {
- // Skip this pinned stack window if it does not have focus
- return;
- }
}
if (mAddInputConsumerHandle
&& inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
- addInputWindowHandle(navInputConsumer.mWindowHandle);
+ navInputConsumer.show(mInputTransaction, w);
mAddInputConsumerHandle = false;
}
if (mAddWallpaperInputConsumerHandle) {
if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) {
// Add the wallpaper input consumer above the first visible wallpaper.
- addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+ wallpaperInputConsumer.show(mInputTransaction, w);
mAddWallpaperInputConsumerHandle = false;
}
}
@@ -490,8 +472,13 @@
mService.mDragDropController.sendDragStartedIfNeededLocked(w);
}
- addInputWindowHandle(
+ populateInputWindowHandle(
inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+
+ if (w.mWinAnimator.hasSurface()) {
+ mInputTransaction.setInputWindowInfo(
+ w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
new file mode 100644
index 0000000..282838f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
+
+import com.android.internal.util.function.TriConsumer;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import java.io.PrintWriter;
+
+/**
+ * Controller for a specific inset source on the server. It's called provider as it provides the
+ * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
+ */
+class InsetsSourceProvider {
+
+ private final Rect mTmpRect = new Rect();
+ private final @NonNull InsetsSource mSource;
+ private final DisplayContent mDisplayContent;
+ private final InsetsStateController mStateController;
+ private @Nullable InsetsSourceControl mControl;
+ private @Nullable WindowState mControllingWin;
+ private @Nullable ControlAdapter mAdapter;
+ private WindowState mWin;
+ private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
+
+ InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
+ DisplayContent displayContent) {
+ mSource = source;
+ mDisplayContent = displayContent;
+ mStateController = stateController;
+ }
+
+ InsetsSource getSource() {
+ return mSource;
+ }
+
+ /**
+ * Updates the window that currently backs this source.
+ *
+ * @param win The window that links to this source.
+ * @param frameProvider Based on display frame state and the window, calculates the resulting
+ * frame that should be reported to clients.
+ */
+ void setWindow(@Nullable WindowState win,
+ @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+ if (mWin != null) {
+ mWin.setInsetProvider(null);
+ }
+ mWin = win;
+ mFrameProvider = frameProvider;
+ if (win == null) {
+ mSource.setVisible(false);
+ mSource.setFrame(new Rect());
+ } else {
+ mSource.setVisible(true);
+ mWin.setInsetProvider(this);
+ }
+ }
+
+ /**
+ * Called when a layout pass has occurred.
+ */
+ void onPostLayout() {
+ if (mWin == null) {
+ return;
+ }
+
+ mTmpRect.set(mWin.getFrameLw());
+ if (mFrameProvider != null) {
+ mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+ } else {
+ mTmpRect.inset(mWin.mGivenContentInsets);
+ }
+ mSource.setFrame(mTmpRect);
+ mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
+
+ }
+
+ void updateControlForTarget(@Nullable WindowState target) {
+ if (target == mControllingWin) {
+ return;
+ }
+ if (target == null) {
+ revokeControl();
+ return;
+ }
+ mAdapter = new ControlAdapter();
+ mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
+ false /* TODO hidden */);
+ mControllingWin = target;
+ mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash);
+ }
+
+ InsetsSourceControl getControl() {
+ return mControl;
+ }
+
+ void revokeControl() {
+ if (mControllingWin != null) {
+
+ // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
+ mWin.cancelAnimation();
+ }
+ }
+
+ private class ControlAdapter implements AnimationAdapter {
+
+ private SurfaceControl mCapturedLeash;
+
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, Transaction t,
+ OnAnimationFinishedCallback finishCallback) {
+ mCapturedLeash = animationLeash;
+ t.setPosition(mCapturedLeash, mSource.getFrame().left, mSource.getFrame().top);
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ if (mAdapter == this) {
+ mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
+ mControl = null;
+ mControllingWin = null;
+ mAdapter = null;
+ }
+ }
+
+ @Override
+ public long getDurationHint() {
+ return 0;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return 0;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream proto) {
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
new file mode 100644
index 0000000..592b7fb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wm;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.ViewRootImpl;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Manages global window inset state in the system represented by {@link InsetsState}.
+ */
+class InsetsStateController {
+
+ private final InsetsState mLastState = new InsetsState();
+ private final InsetsState mState = new InsetsState();
+ private final DisplayContent mDisplayContent;
+
+ private final ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
+ private final ArrayMap<WindowState, ArrayList<Integer>> mWinControlTypeMap = new ArrayMap<>();
+ private final SparseArray<WindowState> mTypeWinControlMap = new SparseArray<>();
+ private final ArraySet<WindowState> mPendingControlChanged = new ArraySet<>();
+
+ private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
+ if (w.isVisible()) {
+ w.notifyInsetsChanged();
+ }
+ };
+
+ InsetsStateController(DisplayContent displayContent) {
+ mDisplayContent = displayContent;
+ }
+
+ /**
+ * When dispatching window state to the client, we'll need to exclude the source that represents
+ * the window that is being dispatched.
+ *
+ * @param target The client we dispatch the state to.
+ * @return The state stripped of the necessary information.
+ */
+ InsetsState getInsetsForDispatch(WindowState target) {
+ final InsetsSourceProvider provider = target.getInsetProvider();
+ if (provider == null) {
+ return mState;
+ }
+
+ final InsetsState state = new InsetsState();
+ state.set(mState);
+ final int type = provider.getSource().getType();
+ state.removeSource(type);
+
+ // Navigation bar doesn't get influenced by anything else
+ if (type == TYPE_NAVIGATION_BAR) {
+ state.removeSource(TYPE_IME);
+ state.removeSource(TYPE_TOP_BAR);
+ }
+ return state;
+ }
+
+ @Nullable InsetsSourceControl[] getControlsForDispatch(WindowState target) {
+ ArrayList<Integer> controlled = mWinControlTypeMap.get(target);
+ if (controlled == null) {
+ return null;
+ }
+ final int size = controlled.size();
+ final InsetsSourceControl[] result = new InsetsSourceControl[size];
+ for (int i = 0; i < size; i++) {
+ result[i] = mControllers.get(controlled.get(i)).getControl();
+ }
+ return result;
+ }
+
+ /**
+ * @return The provider of a specific type.
+ */
+ InsetsSourceProvider getSourceProvider(int type) {
+ return mControllers.computeIfAbsent(type,
+ key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
+ }
+
+ /**
+ * Called when a layout pass has occurred.
+ */
+ void onPostLayout() {
+ for (int i = mControllers.size() - 1; i>= 0; i--) {
+ mControllers.valueAt(i).onPostLayout();
+ }
+ if (!mLastState.equals(mState)) {
+ mLastState.set(mState, true /* copySources */);
+ notifyInsetsChanged();
+ }
+ }
+
+ void onImeTargetChanged(@Nullable WindowState imeTarget) {
+ onControlChanged(TYPE_IME, imeTarget);
+ notifyPendingInsetsControlChanged();
+ }
+
+ /**
+ * Called when the top opaque fullscreen window that is able to control the system bars changes.
+ *
+ * @param controllingWindow The window that is now able to control the system bars appearance
+ * and visibility.
+ */
+ void onBarControllingWindowChanged(@Nullable WindowState controllingWindow) {
+ // TODO: Apply policy that determines whether controllingWindow is able to control system
+ // bars
+
+ // TODO: Depending on the form factor, mapping is different
+ onControlChanged(TYPE_TOP_BAR, controllingWindow);
+ onControlChanged(TYPE_NAVIGATION_BAR, controllingWindow);
+ notifyPendingInsetsControlChanged();
+ }
+
+ void notifyControlRevoked(@NonNull WindowState previousControllingWin,
+ InsetsSourceProvider provider) {
+ removeFromControlMaps(previousControllingWin, provider.getSource().getType());
+ }
+
+ private void onControlChanged(int type, @Nullable WindowState win) {
+ if (!ViewRootImpl.USE_NEW_INSETS) {
+ return;
+ }
+ final WindowState previous = mTypeWinControlMap.get(type);
+ if (win == previous) {
+ return;
+ }
+ final InsetsSourceProvider controller = mControllers.get(type);
+ if (controller == null) {
+ return;
+ }
+ controller.updateControlForTarget(win);
+ if (previous != null) {
+ removeFromControlMaps(previous, type);
+ mPendingControlChanged.add(previous);
+ }
+ if (win != null) {
+ addToControlMaps(win, type);
+ mPendingControlChanged.add(win);
+ }
+ }
+
+ private void removeFromControlMaps(@NonNull WindowState win, int type) {
+ final ArrayList<Integer> array = mWinControlTypeMap.get(win);
+ if (array == null) {
+ return;
+ }
+ array.remove((Integer) type);
+ if (array.isEmpty()) {
+ mWinControlTypeMap.remove(win);
+ }
+ mTypeWinControlMap.remove(type);
+ }
+
+ private void addToControlMaps(@NonNull WindowState win, int type) {
+ final ArrayList<Integer> array = mWinControlTypeMap.computeIfAbsent(win,
+ key -> new ArrayList<>());
+ array.add(type);
+ mTypeWinControlMap.put(type, win);
+ }
+
+ private void notifyPendingInsetsControlChanged() {
+ mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+ for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
+ final WindowState controllingWin = mPendingControlChanged.valueAt(i);
+ controllingWin.notifyInsetsControlChanged();
+ }
+ mPendingControlChanged.clear();
+ });
+ }
+
+ private void notifyInsetsChanged() {
+ mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "WindowInsetsStateController");
+ mState.dump(prefix + " ", pw);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c91af73..4ef3513 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -72,6 +72,7 @@
private int mVisibilityTransactionDepth;
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
+ private RootActivityContainer mRootActivityContainer;
KeyguardController(ActivityTaskManagerService service,
ActivityStackSupervisor stackSupervisor) {
@@ -81,6 +82,7 @@
void setWindowManager(WindowManagerService windowManager) {
mWindowManager = windowManager;
+ mRootActivityContainer = mService.mRootActivityContainer;
}
/**
@@ -146,7 +148,7 @@
mDismissalRequested = false;
}
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
updateKeyguardSleepToken();
}
@@ -172,16 +174,17 @@
mWindowManager.deferSurfaceLayout();
try {
setKeyguardGoingAway(true);
- mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+ mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
false /* alwaysKeepCurrent */, convertTransitFlags(flags),
false /* forceOverride */);
updateKeyguardSleepToken();
// Some stack visibility might change (e.g. docked stack)
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.addStartingWindowsForVisibleActivities(
+ true /* taskSwitch */);
mWindowManager.executeAppTransition();
} finally {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
@@ -277,8 +280,9 @@
private void visibilitiesUpdated() {
boolean requestDismissKeyguard = false;
- for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
- final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+ for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
+ displayNdx >= 0; displayNdx--) {
+ final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
final KeyguardDisplayState state = getDisplay(display.mDisplayId);
state.visibilitiesUpdated(this, display);
requestDismissKeyguard |= state.mRequestDismissKeyguard;
@@ -298,12 +302,12 @@
if (isKeyguardLocked()) {
mWindowManager.deferSurfaceLayout();
try {
- mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+ mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
.prepareAppTransition(resolveOccludeTransit(),
false /* alwaysKeepCurrent */, 0 /* flags */,
true /* forceOverride */);
updateKeyguardSleepToken();
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
mWindowManager.executeAppTransition();
} finally {
mWindowManager.continueSurfaceLayout();
@@ -319,21 +323,23 @@
// We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
// reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
// insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
- if (mWindowManager.isKeyguardSecure()) {
- mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
- mDismissalRequested = true;
+ if (!mWindowManager.isKeyguardSecure()) {
+ return;
+ }
- // If we are about to unocclude the Keyguard, but we can dismiss it without security,
- // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
- final DisplayWindowController dwc =
- mStackSupervisor.getDefaultDisplay().getWindowContainerController();
- if (mKeyguardShowing && canDismissKeyguard()
- && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
- dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
- 0 /* flags */, true /* forceOverride */);
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mWindowManager.executeAppTransition();
- }
+ mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+ mDismissalRequested = true;
+
+ // If we are about to unocclude the Keyguard, but we can dismiss it without security,
+ // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
+ final DisplayWindowController dwc =
+ mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
+ if (mKeyguardShowing && canDismissKeyguard()
+ && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+ dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+ 0 /* flags */, true /* forceOverride */);
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mWindowManager.executeAppTransition();
}
}
@@ -350,7 +356,7 @@
private int resolveOccludeTransit() {
final DisplayWindowController dwc =
- mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+ mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
if (mBeforeUnoccludeTransit != TRANSIT_UNSET
&& dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
// TODO(b/113840485): Handle app transition for individual display.
@@ -377,7 +383,8 @@
// show on top of the lock screen. In this can we want to dismiss the docked
// stack since it will be complicated/risky to try to put the activity on top
// of the lock screen in the right fullscreen configuration.
- final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack stack =
+ mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
if (stack == null) {
return;
}
@@ -387,8 +394,9 @@
}
private void updateKeyguardSleepToken() {
- for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
- final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+ for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
+ displayNdx >= 0; displayNdx--) {
+ final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
final KeyguardDisplayState state = getDisplay(display.mDisplayId);
if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
state.acquiredSleepToken();
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
new file mode 100644
index 0000000..93e2d8d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -0,0 +1,162 @@
+/*
+ * 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
+ */
+
+package com.android.server.wm;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.ArrayList;
+
+/**
+ * Multi-cast implementation of {@link ActivityMetricsLaunchObserver}.
+ *
+ * <br /><br />
+ * If this class is called through the {@link ActivityMetricsLaunchObserver} interface,
+ * then the call is forwarded to all registered observers at the time.
+ *
+ * <br /><br />
+ * All calls are invoked asynchronously in-order on a background thread. This fulfills the
+ * sequential ordering guarantee in {@link ActivityMetricsLaunchObserverRegistry}.
+ *
+ * @see ActivityTaskManagerInternal#getLaunchObserverRegistry()
+ */
+class LaunchObserverRegistryImpl implements
+ ActivityMetricsLaunchObserverRegistry, ActivityMetricsLaunchObserver {
+ private final ArrayList<ActivityMetricsLaunchObserver> mList = new ArrayList<>();
+
+ /**
+ * All calls are posted to a handler because:
+ *
+ * 1. We don't know how long the observer will take to handle this call and we don't want
+ * to block the WM critical section on it.
+ * 2. We don't know the lock ordering of the observer so we don't want to expose a chance
+ * of deadlock.
+ */
+ private final Handler mHandler;
+
+ public LaunchObserverRegistryImpl(Looper looper) {
+ mHandler = new Handler(looper);
+ }
+
+ @Override
+ public void registerLaunchObserver(ActivityMetricsLaunchObserver launchObserver) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleRegisterLaunchObserver, this, launchObserver));
+ }
+
+ @Override
+ public void unregisterLaunchObserver(ActivityMetricsLaunchObserver launchObserver) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleUnregisterLaunchObserver, this, launchObserver));
+ }
+
+ @Override
+ public void onIntentStarted(Intent intent) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent));
+ }
+
+ @Override
+ public void onIntentFailed() {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnIntentFailed, this));
+ }
+
+ @Override
+ public void onActivityLaunched(
+ @ActivityRecordProto byte[] activity,
+ int temperature) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunched,
+ this, activity, temperature));
+ }
+
+ @Override
+ public void onActivityLaunchCancelled(
+ @ActivityRecordProto byte[] activity) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunchCancelled, this, activity));
+ }
+
+ @Override
+ public void onActivityLaunchFinished(
+ @ActivityRecordProto byte[] activity) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
+ }
+
+ // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
+ // unbound (i.e. not capture any variables explicitly or implicitly) to fulfill the
+ // singleton-lambda requirement.
+
+ private void handleRegisterLaunchObserver(ActivityMetricsLaunchObserver observer) {
+ mList.add(observer);
+ }
+
+ private void handleUnregisterLaunchObserver(ActivityMetricsLaunchObserver observer) {
+ mList.remove(observer);
+ }
+
+ private void handleOnIntentStarted(Intent intent) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onIntentStarted(intent);
+ }
+ }
+
+ private void handleOnIntentFailed() {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onIntentFailed();
+ }
+ }
+
+ private void handleOnActivityLaunched(
+ @ActivityRecordProto byte[] activity,
+ @Temperature int temperature) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunched(activity, temperature);
+ }
+ }
+
+ private void handleOnActivityLaunchCancelled(
+ @ActivityRecordProto byte[] activity) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunchCancelled(activity);
+ }
+ }
+
+ private void handleOnActivityLaunchFinished(
+ @ActivityRecordProto byte[] activity) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunchFinished(activity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 72d5143..3062d34 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -223,7 +223,8 @@
private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) {
final ActivityStack<?> stack = task.getStack();
final int displayId = stack.mDisplayId;
- final ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+ final ActivityDisplay display =
+ mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
final DisplayInfo info = new DisplayInfo();
display.mDisplay.getDisplayInfo(info);
@@ -245,8 +246,8 @@
}
void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams outParams) {
- final ComponentName name = task != null ? task.realActivity : activity.realActivity;
- final int userId = task != null ? task.userId : activity.userId;
+ final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
+ final int userId = task != null ? task.userId : activity.mUserId;
outParams.reset();
Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
@@ -259,7 +260,7 @@
return;
}
- final ActivityDisplay display = mSupervisor.getActivityDisplay(
+ final ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(
persistableParams.mDisplayUniqueId);
if (display != null) {
outParams.mPreferredDisplayId = display.mDisplayId;
@@ -268,7 +269,7 @@
outParams.mBounds.set(persistableParams.mBounds);
}
- private void onPackageRemoved(String packageName) {
+ void removeRecordForPackage(String packageName) {
final List<File> fileToDelete = new ArrayList<>();
for (int i = 0; i < mMap.size(); ++i) {
int userId = mMap.keyAt(i);
@@ -309,7 +310,7 @@
@Override
public void onPackageRemoved(String packageName) {
- LaunchParamsPersister.this.onPackageRemoved(packageName);
+ removeRecordForPackage(packageName);
}
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index b49d304..1a2aa2f 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -186,7 +186,6 @@
createSurface();
}
t.setPosition(mSurface, mSurfaceFrame.left, mSurfaceFrame.top);
- t.setSize(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
t.setWindowCrop(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
t.show(mSurface);
} else if (mSurface != null) {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 41d0777..d2f2863 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -225,7 +225,7 @@
* of the last locked task and finishing it would mean that lock task mode is ended illegally.
*/
boolean activityBlockedFromFinish(ActivityRecord activity) {
- final TaskRecord task = activity.getTask();
+ final TaskRecord task = activity.getTaskRecord();
if (activity == task.getRootActivity()
&& activity == task.getTopActivity()
&& task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
@@ -447,7 +447,7 @@
return;
}
task.performClearTaskLocked();
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
/**
@@ -579,7 +579,7 @@
if (andResume) {
mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
lockTaskModeState != LOCK_TASK_MODE_NONE);
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
final ActivityStack stack = task.getStack();
if (stack != null) {
stack.getDisplay().getWindowContainerController().executeAppTransition();
@@ -641,12 +641,13 @@
taskChanged = true;
}
- for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated();
+ for (int displayNdx = mSupervisor.mRootActivityContainer.getChildCount() - 1;
+ displayNdx >= 0; --displayNdx) {
+ mSupervisor.mRootActivityContainer.getChildAt(displayNdx).onLockTaskPackagesUpdated();
}
- final ActivityRecord r = mSupervisor.topRunningActivityLocked();
- final TaskRecord task = (r != null) ? r.getTask() : null;
+ final ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity();
+ final TaskRecord task = (r != null) ? r.getTaskRecord() : null;
if (mLockTaskModeTasks.isEmpty() && task!= null
&& task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
// This task must have just been authorized.
@@ -657,7 +658,7 @@
}
if (taskChanged) {
- mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
index 3ef42e7..1c7ebd6 100644
--- a/services/core/java/com/android/server/wm/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java
@@ -41,7 +41,7 @@
PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
Rect outBounds) {
return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
- mStackSupervisor.mWindowManager);
+ mRootActivityContainer.mWindowManager);
}
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 476c1f9..1e287b4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -79,7 +79,7 @@
int callingPid) {
mService = atm;
mStackSupervisor = stackSupervisor;
- mDefaultDisplay = stackSupervisor.getDefaultDisplay();
+ mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay();
mActivityStartController = activityStartController;
mWindowManager = wm;
mCallingPid = callingPid;
@@ -94,7 +94,7 @@
// TODO(multi-display) currently only support recents animation in default display.
final DisplayWindowController dwc =
- mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+ mService.mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
if (!mWindowManager.canStartRecentsAnimation()) {
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
@@ -124,8 +124,8 @@
// Send launch hint if we are actually launching the target. If it's already visible
// (shouldn't happen in general) we don't need to send it.
if (targetActivity == null || !targetActivity.visible) {
- mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */,
- targetActivity);
+ mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+ true /* forceSend */, targetActivity);
}
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
@@ -148,8 +148,8 @@
// If there are multiple tasks in the target stack (ie. the home stack, with 3p
// and default launchers coexisting), then move the task to the top as a part of
// moving the stack to the front
- if (targetStack.topTask() != targetActivity.getTask()) {
- targetStack.addTask(targetActivity.getTask(), true /* toTop */,
+ if (targetStack.topTask() != targetActivity.getTaskRecord()) {
+ targetStack.addTask(targetActivity.getTaskRecord(), true /* toTop */,
"startRecentsActivity");
}
} else {
@@ -192,7 +192,7 @@
// If we updated the launch-behind state, update the visibility of the activities after
// we fetch the visible tasks to be controlled by the animation
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+ mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
targetActivity);
@@ -215,7 +215,8 @@
@Deprecated IAssistDataReceiver assistDataReceiver, int userId) {
final AppOpsManager appOpsManager = (AppOpsManager)
mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
- final List<IBinder> topActivities = mStackSupervisor.getTopVisibleActivities();
+ final List<IBinder> topActivities =
+ mService.mRootActivityContainer.getTopVisibleActivities();
final AssistDataRequester.AssistDataRequesterCallbacks assistDataCallbacks;
if (assistDataReceiver != null) {
assistDataCallbacks = new AssistDataReceiverProxy(assistDataReceiver,
@@ -283,7 +284,7 @@
// Just to be sure end the launch hint in case the target activity was never launched.
// However, if we're keeping the activity and making it visible, we can leave it on.
if (reorderMode != REORDER_KEEP_IN_PLACE) {
- mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
+ mService.mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
}
mService.mH.post(
@@ -343,8 +344,8 @@
}
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, false);
+ mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
// No reason to wait for the pausing activity in this case, as the hiding of
// surfaces needs to be done immediately.
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index b6609e4..83ba384 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -504,10 +504,12 @@
public void binderDied() {
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
- // Clear associated input consumers on runner death
- final InputMonitor inputMonitor =
- mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
- inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+ synchronized (mService.getWindowManagerLock()) {
+ // Clear associated input consumers on runner death
+ final InputMonitor inputMonitor =
+ mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+ inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+ }
}
void checkAnimationReady(WallpaperController wallpaperController) {
@@ -603,8 +605,8 @@
mTask = task;
mIsRecentTaskInvisible = isRecentTaskInvisible;
final WindowContainer container = mTask.getParent();
- container.getRelativePosition(mPosition);
- container.getBounds(mBounds);
+ container.getRelativeDisplayedPosition(mPosition);
+ mBounds.set(container.getDisplayedBounds());
}
RemoteAnimationTarget createRemoteAnimationApp() {
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
new file mode 100644
index 0000000..1d86b46
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -0,0 +1,2297 @@
+/*
+ * 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
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
+import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
+import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
+import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Build;
+import android.os.FactoryTest;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.AppTimeTracker;
+import com.android.server.am.UserState;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Root node for activity containers.
+ * TODO: This class is mostly temporary to separate things out of ActivityStackSupervisor.java. The
+ * intention is to have this merged with RootWindowContainer.java as part of unifying the hierarchy.
+ */
+class RootActivityContainer extends ConfigurationContainer
+ implements DisplayManager.DisplayListener {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "RootActivityContainer" : TAG_ATM;
+ static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+ private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+ static final String TAG_STATES = TAG + POSTFIX_STATES;
+ private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+
+ /**
+ * The modes which affect which tasks are returned when calling
+ * {@link RootActivityContainer#anyTaskForId(int)}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ MATCH_TASK_IN_STACKS_ONLY,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+ })
+ public @interface AnyTaskForIdMatchTaskMode {}
+ // Match only tasks in the current stacks
+ static final int MATCH_TASK_IN_STACKS_ONLY = 0;
+ // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
+ static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
+ // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
+ // provided stack id
+ static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
+
+ ActivityTaskManagerService mService;
+ ActivityStackSupervisor mStackSupervisor;
+ WindowManagerService mWindowManager;
+ DisplayManager mDisplayManager;
+ private DisplayManagerInternal mDisplayManagerInternal;
+ // TODO: Remove after object merge with RootWindowContainer.
+ private RootWindowContainer mRootWindowContainer;
+
+ /**
+ * List of displays which contain activities, sorted by z-order.
+ * The last entry in the list is the topmost.
+ */
+ private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
+
+ /** Reference to default display so we can quickly look it up. */
+ private ActivityDisplay mDefaultDisplay;
+ private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
+ /** The current user */
+ int mCurrentUser;
+ /** Stack id of the front stack when user switched, indexed by userId. */
+ SparseIntArray mUserStackInFront = new SparseIntArray(2);
+
+ /**
+ * A list of tokens that cause the top activity to be put to sleep.
+ * They are used by components that may hide and block interaction with underlying
+ * activities.
+ */
+ final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>();
+
+ /** Is dock currently minimized. */
+ boolean mIsDockMinimized;
+
+ /** Set when a power hint has started, but not ended. */
+ private boolean mPowerHintSent;
+
+ // The default minimal size that will be used if the activity doesn't specify its minimal size.
+ // It will be calculated when the default display gets added.
+ int mDefaultMinSizeOfResizeableTaskDp = -1;
+
+ // Whether tasks have moved and we need to rank the tasks before next OOM scoring
+ private boolean mTaskLayersChanged = true;
+
+ private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
+ private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+ static class FindTaskResult {
+ ActivityRecord mRecord;
+ boolean mIdealMatch;
+
+ void clear() {
+ mRecord = null;
+ mIdealMatch = false;
+ }
+
+ void setTo(FindTaskResult result) {
+ mRecord = result.mRecord;
+ mIdealMatch = result.mIdealMatch;
+ }
+ }
+
+ RootActivityContainer(ActivityTaskManagerService service) {
+ mService = service;
+ mStackSupervisor = service.mStackSupervisor;
+ mStackSupervisor.mRootActivityContainer = this;
+ }
+
+ @VisibleForTesting
+ void setWindowContainer(RootWindowContainer container) {
+ mRootWindowContainer = container;
+ mRootWindowContainer.setRootActivityContainer(this);
+ }
+
+ void setWindowManager(WindowManagerService wm) {
+ mWindowManager = wm;
+ setWindowContainer(mWindowManager.mRoot);
+ mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(this, mService.mH);
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+
+ final Display[] displays = mDisplayManager.getDisplays();
+ for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
+ final Display display = displays[displayNdx];
+ final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
+ if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
+ mDefaultDisplay = activityDisplay;
+ }
+ addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
+ }
+ calculateDefaultMinimalSizeOfResizeableTasks();
+
+ final ActivityDisplay defaultDisplay = getDefaultDisplay();
+
+ defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
+ }
+
+ // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
+ ActivityDisplay getDefaultDisplay() {
+ return mDefaultDisplay;
+ }
+
+ /**
+ * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is
+ * defined in {@link DisplayInfo#uniqueId}.
+ *
+ * @param uniqueId the unique ID of the display
+ * @return the {@link ActivityDisplay} or {@code null} if nothing is found.
+ */
+ ActivityDisplay getActivityDisplay(String uniqueId) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ final boolean isValid = display.mDisplay.isValid();
+ if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
+ return display;
+ }
+ }
+
+ return null;
+ }
+
+ // TODO: Look into consolidating with getActivityDisplayOrCreate()
+ ActivityDisplay getActivityDisplay(int displayId) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+ if (activityDisplay.mDisplayId == displayId) {
+ return activityDisplay;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get an existing instance of {@link ActivityDisplay} or create new if there is a
+ * corresponding record in display manager.
+ */
+ // TODO: Look into consolidating with getActivityDisplay()
+ ActivityDisplay getActivityDisplayOrCreate(int displayId) {
+ ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+ if (activityDisplay != null) {
+ return activityDisplay;
+ }
+ if (mDisplayManager == null) {
+ // The system isn't fully initialized yet.
+ return null;
+ }
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ // The display is not registered in DisplayManager.
+ return null;
+ }
+ // The display hasn't been added to ActivityManager yet, create a new record now.
+ activityDisplay = new ActivityDisplay(this, display);
+ addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
+ return activityDisplay;
+ }
+
+ /** Check if display with specified id is added to the list. */
+ boolean isDisplayAdded(int displayId) {
+ return getActivityDisplayOrCreate(displayId) != null;
+ }
+
+ ActivityRecord getDefaultDisplayHomeActivity() {
+ return getDefaultDisplayHomeActivityForUser(mCurrentUser);
+ }
+
+ ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
+ return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
+ }
+
+ boolean startHomeOnAllDisplays(int userId, String reason) {
+ boolean homeStarted = false;
+ for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+ final int displayId = mActivityDisplays.get(i).mDisplayId;
+ homeStarted |= startHomeOnDisplay(userId, reason, displayId);
+ }
+ return homeStarted;
+ }
+
+ /**
+ * This starts home activity on displays that can have system decorations and only if the
+ * home activity can have multiple instances.
+ */
+ boolean startHomeOnDisplay(int userId, String reason, int displayId) {
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+ if (aInfo == null) {
+ return false;
+ }
+
+ if (!canStartHomeOnDisplay(aInfo, displayId,
+ false /* allowInstrumenting */)) {
+ return false;
+ }
+
+ // Update the reason for ANR debugging to verify if the user activity is the one that
+ // actually launched.
+ final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
+ aInfo.applicationInfo.uid);
+ mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
+ displayId);
+ return true;
+ }
+
+ /**
+ * This resolves the home activity info and updates the home component of the given intent.
+ * @return the home activity info if any.
+ */
+ private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+ final int flags = ActivityManagerService.STOCK_PM_FLAGS;
+ final ComponentName comp = homeIntent.getComponent();
+ ActivityInfo aInfo = null;
+ try {
+ if (comp != null) {
+ // Factory test.
+ aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
+ } else {
+ final String resolvedType =
+ homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+ final ResolveInfo info = AppGlobals.getPackageManager()
+ .resolveIntent(homeIntent, resolvedType, flags, userId);
+ if (info != null) {
+ aInfo = info.activityInfo;
+ }
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+
+ if (aInfo == null) {
+ Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
+ return null;
+ }
+
+ homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+ aInfo = new ActivityInfo(aInfo);
+ aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
+ homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
+ return aInfo;
+ }
+
+ boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
+ if (!mService.isBooting() && !mService.isBooted()) {
+ // Not ready yet!
+ return false;
+ }
+
+ if (displayId == INVALID_DISPLAY) {
+ displayId = DEFAULT_DISPLAY;
+ }
+
+ final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
+ final String myReason = reason + " resumeHomeActivity";
+
+ // Only resume home activity if isn't finishing.
+ if (r != null && !r.finishing) {
+ r.moveFocusableActivityToTop(myReason);
+ return resumeFocusedStacksTopActivities(r.getActivityStack(), prev, null);
+ }
+ return startHomeOnDisplay(mCurrentUser, myReason, displayId);
+ }
+
+ /**
+ * Check if home activity start should be allowed on a display.
+ * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
+ * @param displayId The id of the target display.
+ * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
+ * @return {@code true} if allow to launch, {@code false} otherwise.
+ */
+ boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
+ boolean allowInstrumenting) {
+ if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
+ && mService.mTopAction == null) {
+ // We are running in factory test mode, but unable to find the factory test app, so
+ // just sit around displaying the error message and don't try to start anything.
+ return false;
+ }
+
+ final WindowProcessController app =
+ mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
+ if (!allowInstrumenting && app != null && app.isInstrumenting()) {
+ // Don't do this if the home app is currently being instrumented.
+ return false;
+ }
+
+ if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+ && displayId == mService.mVr2dDisplayId)) {
+ // No restrictions to default display or vr 2d display.
+ return true;
+ }
+
+ final ActivityDisplay display = getActivityDisplay(displayId);
+ if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
+ // Can't launch home on display that doesn't support system decorations.
+ return false;
+ }
+
+ final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
+ && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
+ && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+ if (!supportMultipleInstance) {
+ // Can't launch home on other displays if it requested to be single instance. Also we
+ // don't allow home applications that target before Q to have multiple home activity
+ // instances because they may not be expected to have multiple home scenario and
+ // haven't explicitly request for single instance.
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Ensure all activities visibility, update orientation and configuration.
+ *
+ * @param starting The currently starting activity or {@code null} if there is none.
+ * @param displayId The id of the display where operation is executed.
+ * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
+ * {@code true} if config changed.
+ * @param deferResume Whether to defer resume while updating config.
+ * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+ * because of configuration update.
+ */
+ boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
+ boolean markFrozenIfConfigChanged, boolean deferResume) {
+ // First ensure visibility without updating the config just yet. We need this to know what
+ // activities are affecting configuration now.
+ // Passing null here for 'starting' param value, so that visibility of actual starting
+ // activity will be properly updated.
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */, false /* notifyClients */);
+
+ if (displayId == INVALID_DISPLAY) {
+ // The caller didn't provide a valid display id, skip updating config.
+ return true;
+ }
+
+ // Force-update the orientation from the WindowManager, since we need the true configuration
+ // to send to the client now.
+ final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ getDisplayOverrideConfiguration(displayId),
+ starting != null && starting.mayFreezeScreenLocked(starting.app)
+ ? starting.appToken : null,
+ displayId, true /* forceUpdate */);
+ if (starting != null && markFrozenIfConfigChanged && config != null) {
+ starting.frozenBeforeDestroy = true;
+ }
+
+ // Update the configuration of the activities on the display.
+ return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
+ displayId);
+ }
+
+ /**
+ * @return a list of activities which are the top ones in each visible stack. The first
+ * entry will be the focused activity.
+ */
+ List<IBinder> getTopVisibleActivities() {
+ final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ // Traverse all displays.
+ for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ // Traverse all stacks on a display.
+ for (int j = display.getChildCount() - 1; j >= 0; --j) {
+ final ActivityStack stack = display.getChildAt(j);
+ // Get top activity from a visible stack and add it to the list.
+ if (stack.shouldBeVisible(null /* starting */)) {
+ final ActivityRecord top = stack.getTopActivity();
+ if (top != null) {
+ if (stack == topFocusedStack) {
+ topActivityTokens.add(0, top.appToken);
+ } else {
+ topActivityTokens.add(top.appToken);
+ }
+ }
+ }
+ }
+ }
+ return topActivityTokens;
+ }
+
+ ActivityStack getTopDisplayFocusedStack() {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
+ if (focusedStack != null) {
+ return focusedStack;
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord getTopResumedActivity() {
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ if (focusedStack == null) {
+ return null;
+ }
+ final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+ if (resumedActivity != null && resumedActivity.app != null) {
+ return resumedActivity;
+ }
+ // The top focused stack might not have a resumed activity yet - look on all displays in
+ // focus order.
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+ if (resumedActivityOnDisplay != null) {
+ return resumedActivityOnDisplay;
+ }
+ }
+ return null;
+ }
+
+ boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
+ if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
+ return false;
+ }
+
+ return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
+ }
+
+ boolean isTopDisplayFocusedStack(ActivityStack stack) {
+ return stack != null && stack == getTopDisplayFocusedStack();
+ }
+
+ void updatePreviousProcess(ActivityRecord r) {
+ // Now that this process has stopped, we may want to consider it to be the previous app to
+ // try to keep around in case the user wants to return to it.
+
+ // First, found out what is currently the foreground app, so that we don't blow away the
+ // previous app if this activity is being hosted by the process that is actually still the
+ // foreground.
+ WindowProcessController fgApp = null;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (isTopDisplayFocusedStack(stack)) {
+ final ActivityRecord resumedActivity = stack.getResumedActivity();
+ if (resumedActivity != null) {
+ fgApp = resumedActivity.app;
+ } else if (stack.mPausingActivity != null) {
+ fgApp = stack.mPausingActivity.app;
+ }
+ break;
+ }
+ }
+ }
+
+ // Now set this one as the previous process, only if that really makes sense to.
+ if (r.hasProcess() && fgApp != null && r.app != fgApp
+ && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+ && r.app != mService.mHomeProcess) {
+ mService.mPreviousProcess = r.app;
+ mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
+ }
+ }
+
+ boolean attachApplication(WindowProcessController app) throws RemoteException {
+ final String processName = app.mName;
+ boolean didSomething = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final ActivityStack stack = display.getFocusedStack();
+ if (stack != null) {
+ stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
+ final ActivityRecord top = stack.topRunningActivityLocked();
+ final int size = mTmpActivityList.size();
+ for (int i = 0; i < size; i++) {
+ final ActivityRecord activity = mTmpActivityList.get(i);
+ if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
+ && processName.equals(activity.processName)) {
+ try {
+ if (mStackSupervisor.realStartActivityLocked(activity, app,
+ top == activity /* andResume */, true /* checkConfig */)) {
+ didSomething = true;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception in new application when starting activity "
+ + top.intent.getComponent().flattenToShortString(), e);
+ throw e;
+ }
+ }
+ }
+ }
+ }
+ if (!didSomething) {
+ ensureActivitiesVisible(null, 0, false /* preserve_windows */);
+ }
+ return didSomething;
+ }
+
+ /**
+ * Make sure that all activities that need to be visible in the system actually are and update
+ * their configuration.
+ */
+ void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+ boolean preserveWindows) {
+ ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+ }
+
+ /**
+ * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
+ */
+ void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+ boolean preserveWindows, boolean notifyClients) {
+ mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
+ try {
+ // First the front stacks. In case any are not fullscreen and are in front of home.
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+ notifyClients);
+ }
+ }
+ } finally {
+ mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
+ }
+ }
+
+ boolean switchUser(int userId, UserState uss) {
+ final int focusStackId = getTopDisplayFocusedStack().getStackId();
+ // We dismiss the docked stack whenever we switch users.
+ final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
+ if (dockedStack != null) {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(
+ dockedStack, dockedStack.isFocusedStackOnDisplay());
+ }
+ // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
+ // also cause all tasks to be moved to the fullscreen stack at a position that is
+ // appropriate.
+ removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
+ mUserStackInFront.put(mCurrentUser, focusStackId);
+ final int restoreStackId =
+ mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
+ mCurrentUser = userId;
+
+ mStackSupervisor.mStartingUsers.add(uss);
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.switchUserLocked(userId);
+ TaskRecord task = stack.topTask();
+ if (task != null) {
+ stack.positionChildWindowContainerAtTop(task);
+ }
+ }
+ }
+
+ ActivityStack stack = getStack(restoreStackId);
+ if (stack == null) {
+ stack = getDefaultDisplay().getHomeStack();
+ }
+ final boolean homeInFront = stack.isActivityTypeHome();
+ if (stack.isOnHomeDisplay()) {
+ stack.moveToFront("switchUserOnHomeDisplay");
+ } else {
+ // Stack was moved to another display while user was swapped out.
+ resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
+ }
+ return homeInFront;
+ }
+
+ void removeUser(int userId) {
+ mUserStackInFront.delete(userId);
+ }
+
+ /**
+ * Update the last used stack id for non-current user (current user's last
+ * used stack is the focused stack)
+ */
+ void updateUserStack(int userId, ActivityStack stack) {
+ if (userId != mCurrentUser) {
+ mUserStackInFront.put(userId, stack != null ? stack.getStackId()
+ : getDefaultDisplay().getHomeStack().mStackId);
+ }
+ }
+
+ void resizeStack(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+ Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
+ boolean deferResume) {
+
+ if (stack.inSplitScreenPrimaryWindowingMode()) {
+ mStackSupervisor.resizeDockedStackLocked(bounds, tempTaskBounds,
+ tempTaskInsetBounds, null, null, preserveWindows, deferResume);
+ return;
+ }
+
+ final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
+ if (!allowResizeInDockedMode
+ && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
+ // If the docked stack exists, don't resize non-floating stacks independently of the
+ // size computed from the docked stack size (otherwise they will be out of sync)
+ return;
+ }
+
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
+ mWindowManager.deferSurfaceLayout();
+ try {
+ if (stack.affectedBySplitScreenResize()) {
+ if (bounds == null && stack.inSplitScreenWindowingMode()) {
+ // null bounds = fullscreen windowing mode...at least for now.
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ } else if (splitScreenActive) {
+ // If we are in split-screen mode and this stack support split-screen, then
+ // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
+ stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ }
+ }
+ stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
+ if (!deferResume) {
+ stack.ensureVisibleActivitiesConfigurationLocked(
+ stack.topRunningActivityLocked(), preserveWindows);
+ }
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
+ /**
+ * Move stack with all its existing content to specified display.
+ * @param stackId Id of stack to move.
+ * @param displayId Id of display to move stack to.
+ * @param onTop Indicates whether container should be place on top or on bottom.
+ */
+ void moveStackToDisplay(int stackId, int displayId, boolean onTop) {
+ final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId="
+ + displayId);
+ }
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ throw new IllegalArgumentException("moveStackToDisplay: Unknown stackId="
+ + stackId);
+ }
+
+ final ActivityDisplay currentDisplay = stack.getDisplay();
+ if (currentDisplay == null) {
+ throw new IllegalStateException("moveStackToDisplay: Stack with stack=" + stack
+ + " is not attached to any display.");
+ }
+
+ if (currentDisplay.mDisplayId == displayId) {
+ throw new IllegalArgumentException("Trying to move stack=" + stack
+ + " to its current displayId=" + displayId);
+ }
+
+ stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
+ // TODO(multi-display): resize stacks properly if moved from split-screen.
+ }
+
+ boolean moveTopStackActivityToPinnedStack(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ throw new IllegalArgumentException(
+ "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId);
+ }
+
+ final ActivityRecord r = stack.topRunningActivityLocked();
+ if (r == null) {
+ Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity"
+ + " in stack=" + stack);
+ return false;
+ }
+
+ if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
+ Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for "
+ + " r=" + r);
+ return false;
+ }
+
+ moveActivityToPinnedStack(r, null /* sourceBounds */, 0f /* aspectRatio */,
+ "moveTopActivityToPinnedStack");
+ return true;
+ }
+
+ void moveActivityToPinnedStack(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
+ String reason) {
+
+ mWindowManager.deferSurfaceLayout();
+
+ final ActivityDisplay display = r.getActivityStack().getDisplay();
+ PinnedActivityStack stack = display.getPinnedStack();
+
+ // This will clear the pinned stack by moving an existing task to the full screen stack,
+ // ensuring only one task is present.
+ if (stack != null) {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+ }
+
+ // Need to make sure the pinned stack exist so we can resize it below...
+ stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
+
+ // Calculate the target bounds here before the task is reparented back into pinned windowing
+ // mode (which will reset the saved bounds)
+ final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
+
+ try {
+ final TaskRecord task = r.getTaskRecord();
+ // Resize the pinned stack to match the current size of the task the activity we are
+ // going to be moving is currently contained in. We do this to have the right starting
+ // animation bounds for the pinned stack to the desired bounds the caller wants.
+ resizeStack(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
+ null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+ true /* allowResizeInDockedMode */, !DEFER_RESUME);
+
+ if (task.mActivities.size() == 1) {
+ // Defer resume until below, and do not schedule PiP changes until we animate below
+ task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
+ false /* schedulePictureInPictureModeChange */, reason);
+ } else {
+ // There are multiple activities in the task and moving the top activity should
+ // reveal/leave the other activities in their original task.
+
+ // Currently, we don't support reparenting activities across tasks in two different
+ // stacks, so instead, just create a new task in the same stack, reparent the
+ // activity into that task, and then reparent the whole task to the new stack. This
+ // ensures that all the necessary work to migrate states in the old and new stacks
+ // is also done.
+ final TaskRecord newTask = task.getStack().createTaskRecord(
+ mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), r.info,
+ r.intent, null, null, true);
+ r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+
+ // Defer resume until below, and do not schedule PiP changes until we animate below
+ newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
+ }
+
+ // Reset the state that indicates it can enter PiP while pausing after we've moved it
+ // to the pinned stack
+ r.supportsEnterPipOnTaskSwitch = false;
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ }
+
+ stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
+ true /* fromFullscreen */);
+
+ // Update the visibility of all activities after the they have been reparented to the new
+ // stack. This MUST run after the animation above is scheduled to ensure that the windows
+ // drawn signal is scheduled after the bounds animation start call on the bounds animator
+ // thread.
+ ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ resumeFocusedStacksTopActivities();
+
+ mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+ }
+
+ void executeAppTransitionForAllDisplay() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ display.getWindowContainerController().executeAppTransition();
+ }
+ }
+
+ void setDockedStackMinimized(boolean minimized) {
+ // Get currently focused stack before setting mIsDockMinimized. We do this because if
+ // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+ // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
+ final ActivityStack current = getTopDisplayFocusedStack();
+ mIsDockMinimized = minimized;
+ if (mIsDockMinimized) {
+ if (current.inSplitScreenPrimaryWindowingMode()) {
+ // The primary split-screen stack can't be focused while it is minimize, so move
+ // focus to something else.
+ current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
+ }
+ }
+ }
+
+ ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) {
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
+ mTmpFindTaskResult.clear();
+
+ // Looking up task on preferred display first
+ final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
+ if (preferredDisplay != null) {
+ preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
+ }
+ }
+
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ if (display.mDisplayId == preferredDisplayId) {
+ continue;
+ }
+
+ display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
+ }
+ }
+
+ if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
+ return mTmpFindTaskResult.mRecord;
+ }
+
+ /**
+ * Finish the topmost activities in all stacks that belong to the crashed app.
+ * @param app The app that crashed.
+ * @param reason Reason to perform this action.
+ * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
+ */
+ int finishTopCrashedActivities(WindowProcessController app, String reason) {
+ TaskRecord finishedTask = null;
+ ActivityStack focusedStack = getTopDisplayFocusedStack();
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ // It is possible that request to finish activity might also remove its task and stack,
+ // so we need to be careful with indexes in the loop and check child count every time.
+ for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
+ if (stack == focusedStack || finishedTask == null) {
+ finishedTask = t;
+ }
+ }
+ }
+ return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
+ }
+
+ boolean resumeFocusedStacksTopActivities() {
+ return resumeFocusedStacksTopActivities(null, null, null);
+ }
+
+ boolean resumeFocusedStacksTopActivities(
+ ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
+
+ if (!mStackSupervisor.readyToResume()) {
+ return false;
+ }
+
+ if (targetStack != null && (targetStack.isTopStackOnDisplay()
+ || getTopDisplayFocusedStack() == targetStack)) {
+ return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+ }
+
+ // Resume all top activities in focused stacks on all displays.
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final ActivityStack focusedStack = display.getFocusedStack();
+ if (focusedStack == null) {
+ continue;
+ }
+ final ActivityRecord r = focusedStack.topRunningActivityLocked();
+ if (r == null || !r.isState(RESUMED)) {
+ focusedStack.resumeTopActivityUncheckedLocked(null, null);
+ } else if (r.isState(RESUMED)) {
+ // Kick off any lingering app transitions form the MoveTaskToFront operation.
+ focusedStack.executeAppTransition(targetOptions);
+ }
+ }
+
+ return false;
+ }
+
+ void applySleepTokens(boolean applyToStacks) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ // Set the sleeping state of the display.
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final boolean displayShouldSleep = display.shouldSleep();
+ if (displayShouldSleep == display.isSleeping()) {
+ continue;
+ }
+ display.setIsSleeping(displayShouldSleep);
+
+ if (!applyToStacks) {
+ continue;
+ }
+
+ // Set the sleeping state of the stacks on the display.
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (displayShouldSleep) {
+ stack.goToSleepIfPossible(false /* shuttingDown */);
+ } else {
+ stack.awakeFromSleepingLocked();
+ if (stack.isFocusedStackOnDisplay()
+ && !mStackSupervisor.getKeyguardController()
+ .isKeyguardOrAodShowing(display.mDisplayId)) {
+ // If the keyguard is unlocked - resume immediately.
+ // It is possible that the display will not be awake at the time we
+ // process the keyguard going away, which can happen before the sleep token
+ // is released. As a result, it is important we resume the activity here.
+ resumeFocusedStacksTopActivities();
+ }
+ }
+ }
+
+ if (displayShouldSleep || mStackSupervisor.mGoingToSleepActivities.isEmpty()) {
+ continue;
+ }
+ // The display is awake now, so clean up the going to sleep list.
+ for (Iterator<ActivityRecord> it =
+ mStackSupervisor.mGoingToSleepActivities.iterator(); it.hasNext(); ) {
+ final ActivityRecord r = it.next();
+ if (r.getDisplayId() == display.mDisplayId) {
+ it.remove();
+ }
+ }
+ }
+ }
+
+ protected <T extends ActivityStack> T getStack(int stackId) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final T stack = mActivityDisplays.get(i).getStack(stackId);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ /** @see ActivityDisplay#getStack(int, int) */
+ private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ private ActivityManager.StackInfo getStackInfo(ActivityStack stack) {
+ final int displayId = stack.mDisplayId;
+ final ActivityDisplay display = getActivityDisplay(displayId);
+ ActivityManager.StackInfo info = new ActivityManager.StackInfo();
+ stack.getWindowContainerBounds(info.bounds);
+ info.displayId = displayId;
+ info.stackId = stack.mStackId;
+ info.userId = stack.mCurrentUser;
+ info.visible = stack.shouldBeVisible(null);
+ // A stack might be not attached to a display.
+ info.position = display != null ? display.getIndexOf(stack) : 0;
+ info.configuration.setTo(stack.getConfiguration());
+
+ ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ final int numTasks = tasks.size();
+ int[] taskIds = new int[numTasks];
+ String[] taskNames = new String[numTasks];
+ Rect[] taskBounds = new Rect[numTasks];
+ int[] taskUserIds = new int[numTasks];
+ for (int i = 0; i < numTasks; ++i) {
+ final TaskRecord task = tasks.get(i);
+ taskIds[i] = task.taskId;
+ taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+ : task.realActivity != null ? task.realActivity.flattenToString()
+ : task.getTopActivity() != null ? task.getTopActivity().packageName
+ : "unknown";
+ taskBounds[i] = new Rect();
+ task.getWindowContainerBounds(taskBounds[i]);
+ taskUserIds[i] = task.userId;
+ }
+ info.taskIds = taskIds;
+ info.taskNames = taskNames;
+ info.taskBounds = taskBounds;
+ info.taskUserIds = taskUserIds;
+
+ final ActivityRecord top = stack.topRunningActivityLocked();
+ info.topActivity = top != null ? top.intent.getComponent() : null;
+ return info;
+ }
+
+ ActivityManager.StackInfo getStackInfo(int stackId) {
+ ActivityStack stack = getStack(stackId);
+ if (stack != null) {
+ return getStackInfo(stack);
+ }
+ return null;
+ }
+
+ ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
+ final ActivityStack stack = getStack(windowingMode, activityType);
+ return (stack != null) ? getStackInfo(stack) : null;
+ }
+
+ ArrayList<ActivityManager.StackInfo> getAllStackInfos() {
+ ArrayList<ActivityManager.StackInfo> list = new ArrayList<>();
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ list.add(getStackInfo(stack));
+ }
+ }
+ return list;
+ }
+
+ void deferUpdateBounds(int activityType) {
+ final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ if (stack != null) {
+ stack.deferUpdateBounds();
+ }
+ }
+
+ void continueUpdateBounds(int activityType) {
+ final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ if (stack != null) {
+ stack.continueUpdateBounds();
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
+ synchronized (mService.mGlobalLock) {
+ getActivityDisplayOrCreate(displayId);
+ // Do not start home before booting, or it may accidentally finish booting before it
+ // starts. Instead, we expect home activities to be launched when the system is ready
+ // (ActivityManagerService#systemReady).
+ if (mService.isBooted() || mService.isBooting()) {
+ startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
+ if (displayId == DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException("Can't remove the primary display.");
+ }
+
+ synchronized (mService.mGlobalLock) {
+ final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+ if (activityDisplay == null) {
+ return;
+ }
+
+ activityDisplay.remove();
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
+ synchronized (mService.mGlobalLock) {
+ final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+ if (activityDisplay != null) {
+ activityDisplay.onDisplayChanged();
+ }
+ }
+ }
+
+ /** Update lists of UIDs that are present on displays and have access to them. */
+ void updateUIDsPresentOnDisplay() {
+ mDisplayAccessUIDs.clear();
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+ // Only bother calculating the whitelist for private displays
+ if (activityDisplay.isPrivate()) {
+ mDisplayAccessUIDs.append(
+ activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
+ }
+ }
+ // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
+ mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
+ }
+
+ ActivityStack findStackBehind(ActivityStack stack) {
+ final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
+ if (display != null) {
+ for (int i = display.getChildCount() - 1; i >= 0; i--) {
+ if (display.getChildAt(i) == stack && i > 0) {
+ return display.getChildAt(i - 1);
+ }
+ }
+ }
+ throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+ + " in=" + display);
+ }
+
+ @Override
+ protected int getChildCount() {
+ return mActivityDisplays.size();
+ }
+
+ @Override
+ protected ActivityDisplay getChildAt(int index) {
+ return mActivityDisplays.get(index);
+ }
+
+ @Override
+ protected ConfigurationContainer getParent() {
+ return null;
+ }
+
+ // TODO: remove after object merge with RootWindowContainer
+ void onChildPositionChanged(DisplayWindowController childController, int position) {
+ // Assume AM lock is held from positionChildAt of controller in each hierarchy.
+ final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
+ if (display != null) {
+ positionChildAt(display, position);
+ }
+ }
+
+ /** Change the z-order of the given display. */
+ private void positionChildAt(ActivityDisplay display, int position) {
+ if (position >= mActivityDisplays.size()) {
+ position = mActivityDisplays.size() - 1;
+ } else if (position < 0) {
+ position = 0;
+ }
+
+ if (mActivityDisplays.isEmpty()) {
+ mActivityDisplays.add(display);
+ } else if (mActivityDisplays.get(position) != display) {
+ mActivityDisplays.remove(display);
+ mActivityDisplays.add(position, display);
+ }
+ }
+
+ @VisibleForTesting
+ void addChild(ActivityDisplay activityDisplay, int position) {
+ positionChildAt(activityDisplay, position);
+ mRootWindowContainer.positionChildAt(position,
+ activityDisplay.getWindowContainerController().mContainer);
+ }
+
+ void removeChild(ActivityDisplay activityDisplay) {
+ // The caller must tell the controller of {@link ActivityDisplay} to release its container
+ // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
+ mActivityDisplays.remove(activityDisplay);
+ }
+
+ Configuration getDisplayOverrideConfiguration(int displayId) {
+ final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException("No display found with id: " + displayId);
+ }
+
+ return activityDisplay.getOverrideConfiguration();
+ }
+
+ void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
+ final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException("No display found with id: " + displayId);
+ }
+
+ activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
+ }
+
+ void prepareForShutdown() {
+ for (int i = 0; i < mActivityDisplays.size(); i++) {
+ createSleepToken("shutdown", mActivityDisplays.get(i).mDisplayId);
+ }
+ }
+
+ ActivityTaskManagerInternal.SleepToken createSleepToken(String tag, int displayId) {
+ final ActivityDisplay display = getActivityDisplay(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException("Invalid display: " + displayId);
+ }
+
+ final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
+ mSleepTokens.add(token);
+ display.mAllSleepTokens.add(token);
+ return token;
+ }
+
+ private void removeSleepToken(SleepTokenImpl token) {
+ mSleepTokens.remove(token);
+
+ final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
+ if (display != null) {
+ display.mAllSleepTokens.remove(token);
+ if (display.mAllSleepTokens.isEmpty()) {
+ mService.updateSleepIfNeededLocked();
+ }
+ }
+ }
+
+ void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.addStartingWindowsForVisibleActivities(taskSwitch);
+ }
+ }
+ }
+
+ void invalidateTaskLayers() {
+ mTaskLayersChanged = true;
+ }
+
+ void rankTaskLayersIfNeeded() {
+ if (!mTaskLayersChanged) {
+ return;
+ }
+ mTaskLayersChanged = false;
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ int baseLayer = 0;
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ baseLayer += stack.rankTaskLayers(baseLayer);
+ }
+ }
+ }
+
+ void clearOtherAppTimeTrackers(AppTimeTracker except) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.clearOtherAppTimeTrackers(except);
+ }
+ }
+ }
+
+ void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.scheduleDestroyActivities(app, reason);
+ }
+ }
+ }
+
+ void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
+ // Tasks is non-null only if two or more tasks are found.
+ ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
+ if (tasks == null) {
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
+ return;
+ }
+ // If we have activities in multiple tasks that are in a position to be destroyed,
+ // let's iterate through the tasks and release the oldest one.
+ final int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final int stackCount = display.getChildCount();
+ // Step through all stacks starting from behind, to hit the oldest things first.
+ for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ // Try to release activities in this stack; if we manage to, we are done.
+ if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
+ return;
+ }
+ }
+ }
+ }
+
+ // Tries to put all activity stacks to sleep. Returns true if all stacks were
+ // successfully put to sleep.
+ boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
+ boolean allSleep = true;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (allowDelay) {
+ allSleep &= stack.goToSleepIfPossible(shuttingDown);
+ } else {
+ stack.goToSleep();
+ }
+ }
+ }
+ return allSleep;
+ }
+
+ void handleAppCrash(WindowProcessController app) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.handleAppCrash(app);
+ }
+ }
+ }
+
+ ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityRecord ar = stack.findActivityLocked(
+ intent, info, compareIntentFilters);
+ if (ar != null) {
+ return ar;
+ }
+ }
+ }
+ return null;
+ }
+
+ boolean hasAwakeDisplay() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ if (!display.shouldSleep()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
+ return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
+ }
+
+ /**
+ * Returns the right stack to use for launching factoring in all the input parameters.
+ *
+ * @param r The activity we are trying to launch. Can be null.
+ * @param options The activity options used to the launch. Can be null.
+ * @param candidateTask The possible task the activity might be launched in. Can be null.
+ * @params launchParams The resolved launch params to use.
+ *
+ * @return The stack to use for the launch or INVALID_STACK_ID.
+ */
+ <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
+ @Nullable LaunchParamsController.LaunchParams launchParams) {
+ int taskId = INVALID_TASK_ID;
+ int displayId = INVALID_DISPLAY;
+ //Rect bounds = null;
+
+ // We give preference to the launch preference in activity options.
+ if (options != null) {
+ taskId = options.getLaunchTaskId();
+ displayId = options.getLaunchDisplayId();
+ }
+
+ // First preference for stack goes to the task Id set in the activity options. Use the stack
+ // associated with that if possible.
+ if (taskId != INVALID_TASK_ID) {
+ // Temporarily set the task id to invalid in case in re-entry.
+ options.setLaunchTaskId(INVALID_TASK_ID);
+ final TaskRecord task = anyTaskForId(taskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
+ options.setLaunchTaskId(taskId);
+ if (task != null) {
+ return task.getStack();
+ }
+ }
+
+ final int activityType = resolveActivityType(r, options, candidateTask);
+ T stack;
+
+ // Next preference for stack goes to the display Id set the candidate display.
+ if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
+ displayId = launchParams.mPreferredDisplayId;
+ }
+ if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
+ if (r != null) {
+ stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
+ launchParams);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ final ActivityDisplay display = getActivityDisplayOrCreate(displayId);
+ if (display != null) {
+ stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ }
+
+ // Give preference to the stack and display of the input task and activity if they match the
+ // mode we want to launch into.
+ stack = null;
+ ActivityDisplay display = null;
+ if (candidateTask != null) {
+ stack = candidateTask.getStack();
+ }
+ if (stack == null && r != null) {
+ stack = r.getActivityStack();
+ }
+ if (stack != null) {
+ display = stack.getDisplay();
+ if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
+ int windowingMode = launchParams != null ? launchParams.mWindowingMode
+ : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+ if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+ activityType);
+ }
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
+ && display.getSplitScreenPrimaryStack() == stack
+ && candidateTask == stack.topTask()) {
+ // This is a special case when we try to launch an activity that is currently on
+ // top of split-screen primary stack, but is targeting split-screen secondary.
+ // In this case we don't want to move it to another stack.
+ // TODO(b/78788972): Remove after differentiating between preferred and required
+ // launch options.
+ return stack;
+ }
+ }
+ }
+
+ if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
+ display = getDefaultDisplay();
+ }
+
+ return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+ }
+
+ /** @return true if activity record is null or can be launched on provided display. */
+ private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
+ if (r == null) {
+ return true;
+ }
+ return r.canBeLaunchedOnDisplay(displayId);
+ }
+
+ /**
+ * Get a topmost stack on the display, that is a valid launch stack for specified activity.
+ * If there is no such stack, new dynamic stack can be created.
+ * @param displayId Target display.
+ * @param r Activity that should be launched there.
+ * @param candidateTask The possible task the activity might be put in.
+ * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
+ */
+ private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+ @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options,
+ @Nullable LaunchParamsController.LaunchParams launchParams) {
+ final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+ if (activityDisplay == null) {
+ throw new IllegalArgumentException(
+ "Display with displayId=" + displayId + " not found.");
+ }
+
+ if (!r.canBeLaunchedOnDisplay(displayId)) {
+ return null;
+ }
+
+ // If {@code r} is already in target display and its task is the same as the candidate task,
+ // the intention should be getting a launch stack for the reusable activity, so we can use
+ // the existing stack.
+ if (r.getDisplayId() == displayId && r.getTaskRecord() == candidateTask) {
+ return candidateTask.getStack();
+ }
+
+ // Return the topmost valid stack on the display.
+ for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = activityDisplay.getChildAt(i);
+ if (isValidLaunchStack(stack, r)) {
+ return stack;
+ }
+ }
+
+ // If there is no valid stack on the external display - check if new dynamic stack will do.
+ if (displayId != DEFAULT_DISPLAY) {
+ final int windowingMode;
+ if (launchParams != null) {
+ // When launch params is not null, we always defer to its windowing mode. Sometimes
+ // it could be unspecified, which indicates it should inherit windowing mode from
+ // display.
+ windowingMode = launchParams.mWindowingMode;
+ } else {
+ windowingMode = options != null ? options.getLaunchWindowingMode()
+ : r.getWindowingMode();
+ }
+ final int activityType =
+ options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
+ ? options.getLaunchActivityType() : r.getActivityType();
+ return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
+ }
+
+ Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
+ return null;
+ }
+
+ ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+ @Nullable ActivityOptions options,
+ @Nullable LaunchParamsController.LaunchParams launchParams) {
+ return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
+ launchParams);
+ }
+
+ // TODO: Can probably be consolidated into getLaunchStack()...
+ private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r) {
+ switch (stack.getActivityType()) {
+ case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
+ case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
+ case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+ }
+ // There is a 1-to-1 relationship between stack and task when not in
+ // primary split-windowing mode.
+ if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ return false;
+ } else {
+ return r.supportsSplitScreenWindowingMode();
+ }
+ }
+
+ int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ @Nullable TaskRecord task) {
+ // Preference is given to the activity type for the activity then the task since the type
+ // once set shouldn't change.
+ int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+ if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
+ activityType = task.getActivityType();
+ }
+ if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+ return activityType;
+ }
+ if (options != null) {
+ activityType = options.getLaunchActivityType();
+ }
+ return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
+ }
+
+ /**
+ * Get next focusable stack in the system. This will search through the stack on the same
+ * display as the current focused stack, looking for a focusable and visible stack, different
+ * from the target stack. If no valid candidates will be found, it will then go through all
+ * displays and stacks in last-focused order.
+ *
+ * @param currentFocus The stack that previously had focus.
+ * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
+ * candidate.
+ * @return Next focusable {@link ActivityStack}, {@code null} if not found.
+ */
+ ActivityStack getNextFocusableStack(@NonNull ActivityStack currentFocus,
+ boolean ignoreCurrent) {
+ // First look for next focusable stack on the same display
+ final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
+ final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
+ currentFocus, ignoreCurrent);
+ if (preferredFocusableStack != null) {
+ return preferredFocusableStack;
+ }
+ if (preferredDisplay.supportsSystemDecorations()) {
+ // Stop looking for focusable stack on other displays because the preferred display
+ // supports system decorations. Home activity would be launched on the same display if
+ // no focusable stack found.
+ return null;
+ }
+
+ // Now look through all displays
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ if (display == preferredDisplay) {
+ // We've already checked this one
+ continue;
+ }
+ final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
+ ignoreCurrent);
+ if (nextFocusableStack != null) {
+ return nextFocusableStack;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get next valid stack for launching provided activity in the system. This will search across
+ * displays and stacks in last-focused order for a focusable and visible stack, except those
+ * that are on a currently focused display.
+ *
+ * @param r The activity that is being launched.
+ * @param currentFocus The display that previously had focus and thus needs to be ignored when
+ * searching for the next candidate.
+ * @return Next valid {@link ActivityStack}, null if not found.
+ */
+ ActivityStack getNextValidLaunchStack(@NonNull ActivityRecord r, int currentFocus) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ if (display.mDisplayId == currentFocus) {
+ continue;
+ }
+ final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
+ null /* options */, null /* launchParams */);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ boolean handleAppDied(WindowProcessController app) {
+ boolean hasVisibleActivities = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ hasVisibleActivities |= stack.handleAppDiedLocked(app);
+ }
+ }
+ return hasVisibleActivities;
+ }
+
+ void closeSystemDialogs() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.closeSystemDialogsLocked();
+ }
+ }
+ }
+
+ /** @return true if some activity was finished (or would have finished if doit were true). */
+ boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses,
+ boolean doit, boolean evenPersistent, int userId) {
+ boolean didSomething = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (stack.finishDisabledPackageActivitiesLocked(
+ packageName, filterByClasses, doit, evenPersistent, userId)) {
+ didSomething = true;
+ }
+ }
+ }
+ return didSomething;
+ }
+
+ void updateActivityApplicationInfo(ApplicationInfo aInfo) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.updateActivityApplicationInfoLocked(aInfo);
+ }
+ }
+ }
+
+ void finishVoiceTask(IVoiceInteractionSession session) {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ final int numStacks = display.getChildCount();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.finishVoiceTask(session);
+ }
+ }
+ }
+
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ void removeStacksInWindowingModes(int... windowingModes) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
+ }
+ }
+
+ void removeStacksWithActivityTypes(int... activityTypes) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
+ }
+ }
+
+ ActivityRecord topRunningActivity() {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+ if (topActivity != null) {
+ return topActivity;
+ }
+ }
+ return null;
+ }
+
+ boolean allResumedActivitiesIdle() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ // TODO(b/117135575): Check resumed activities on all visible stacks.
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ if (display.isSleeping()) {
+ // No resumed activities while display is sleeping.
+ continue;
+ }
+
+ // If the focused stack is not null or not empty, there should have some activities
+ // resuming or resumed. Make sure these activities are idle.
+ final ActivityStack stack = display.getFocusedStack();
+ if (stack == null || stack.numActivities() == 0) {
+ continue;
+ }
+ final ActivityRecord resumedActivity = stack.getResumedActivity();
+ if (resumedActivity == null || !resumedActivity.idle) {
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
+ + stack.mStackId + " " + resumedActivity + " not idle");
+ }
+ return false;
+ }
+ }
+ // Send launch end powerhint when idle
+ sendPowerHintForLaunchEndIfNeeded();
+ return true;
+ }
+
+ boolean allResumedActivitiesVisible() {
+ boolean foundResumed = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityRecord r = stack.getResumedActivity();
+ if (r != null) {
+ if (!r.nowVisible
+ || mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
+ return false;
+ }
+ foundResumed = true;
+ }
+ }
+ }
+ return foundResumed;
+ }
+
+ boolean allPausedActivitiesComplete() {
+ boolean pausing = true;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityRecord r = stack.mPausingActivity;
+ if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES,
+ "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
+ pausing = false;
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+ return pausing;
+ }
+
+ /**
+ * Find all visible task stacks containing {@param userId} and intercept them with an activity
+ * to block out the contents and possibly start a credential-confirming intent.
+ *
+ * @param userId user handle for the locked managed profile.
+ */
+ void lockAllProfileTasks(@UserIdInt int userId) {
+ mWindowManager.deferSurfaceLayout();
+ try {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final List<TaskRecord> tasks = stack.getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
+ final TaskRecord task = tasks.get(taskNdx);
+
+ // Check the task for a top activity belonging to userId, or returning a
+ // result to an activity belonging to userId. Example case: a document
+ // picker for personal files, opened by a work app, should still get locked.
+ if (taskTopActivityIsUser(task, userId)) {
+ mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
+ task.taskId, userId);
+ }
+ }
+ }
+ }
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ /**
+ * Detects whether we should show a lock screen in front of this task for a locked user.
+ * <p>
+ * We'll do this if either of the following holds:
+ * <ul>
+ * <li>The top activity explicitly belongs to {@param userId}.</li>
+ * <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
+ * </ul>
+ *
+ * @return {@code true} if the top activity looks like it belongs to {@param userId}.
+ */
+ private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
+ // To handle the case that work app is in the task but just is not the top one.
+ final ActivityRecord activityRecord = task.getTopActivity();
+ final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
+
+ return (activityRecord != null && activityRecord.mUserId == userId)
+ || (resultTo != null && resultTo.mUserId == userId);
+ }
+
+ void cancelInitializingActivities() {
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ stack.cancelInitializingActivities();
+ }
+ }
+ }
+
+ TaskRecord anyTaskForId(int id) {
+ return anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
+ }
+
+ TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
+ return anyTaskForId(id, matchMode, null, !ON_TOP);
+ }
+
+ /**
+ * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
+ * @param id Id of the task we would like returned.
+ * @param matchMode The mode to match the given task id in.
+ * @param aOptions The activity options to use for restoration. Can be null.
+ * @param onTop If the stack for the task should be the topmost on the display.
+ */
+ TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode,
+ @Nullable ActivityOptions aOptions, boolean onTop) {
+ // If options are set, ensure that we are attempting to actually restore a task
+ if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
+ throw new IllegalArgumentException("Should not specify activity options for non-restore"
+ + " lookup");
+ }
+
+ int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final TaskRecord task = stack.taskForIdLocked(id);
+ if (task == null) {
+ continue;
+ }
+ if (aOptions != null) {
+ // Resolve the stack the task should be placed in now based on options
+ // and reparent if needed.
+ final ActivityStack launchStack =
+ getLaunchStack(null, aOptions, task, onTop);
+ if (launchStack != null && stack != launchStack) {
+ final int reparentMode = onTop
+ ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
+ task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
+ "anyTaskForId");
+ }
+ }
+ return task;
+ }
+ }
+
+ // If we are matching stack tasks only, return now
+ if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
+ return null;
+ }
+
+ // Otherwise, check the recent tasks and return if we find it there and we are not restoring
+ // the task from recents
+ if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
+ final TaskRecord task = mStackSupervisor.mRecentTasks.getTask(id);
+
+ if (task == null) {
+ if (DEBUG_RECENTS) {
+ Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
+ }
+
+ return null;
+ }
+
+ if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
+ return task;
+ }
+
+ // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+ if (!mStackSupervisor.restoreRecentTaskLocked(task, aOptions, onTop)) {
+ if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
+ "Couldn't restore task id=" + id + " found in recents");
+ return null;
+ }
+ if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
+ return task;
+ }
+
+ ActivityRecord isInAnyStack(IBinder token) {
+ int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityRecord r = stack.isInStackLocked(token);
+ if (r != null) {
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
+ @WindowConfiguration.ActivityType int ignoreActivityType,
+ @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
+ boolean allowed) {
+ mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType,
+ ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
+ }
+
+ void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
+ boolean sendHint = forceSend;
+
+ if (!sendHint) {
+ // Send power hint if we don't know what we're launching yet
+ sendHint = targetActivity == null || targetActivity.app == null;
+ }
+
+ if (!sendHint) { // targetActivity != null
+ // Send power hint when the activity's process is different than the current resumed
+ // activity on all displays, or if there are no resumed activities in the system.
+ boolean noResumedActivities = true;
+ boolean allFocusedProcessesDiffer = true;
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+ final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+ final WindowProcessController resumedActivityProcess =
+ resumedActivity == null ? null : resumedActivity.app;
+
+ noResumedActivities &= resumedActivityProcess == null;
+ if (resumedActivityProcess != null) {
+ allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+ }
+ }
+ sendHint = noResumedActivities || allFocusedProcessesDiffer;
+ }
+
+ if (sendHint && mService.mPowerManagerInternal != null) {
+ mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
+ mPowerHintSent = true;
+ }
+ }
+
+ void sendPowerHintForLaunchEndIfNeeded() {
+ // Trigger launch power hint if activity is launched
+ if (mPowerHintSent && mService.mPowerManagerInternal != null) {
+ mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
+ mPowerHintSent = false;
+ }
+ }
+
+ private void calculateDefaultMinimalSizeOfResizeableTasks() {
+ final Resources res = mService.mContext.getResources();
+ final float minimalSize = res.getDimension(
+ com.android.internal.R.dimen.default_minimal_size_resizable_task);
+ final DisplayMetrics dm = res.getDisplayMetrics();
+
+ mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
+ }
+
+ /**
+ * Dumps the activities matching the given {@param name} in the either the focused stack
+ * or all visible stacks if {@param dumpVisibleStacks} is true.
+ */
+ ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly,
+ boolean dumpFocusedStackOnly) {
+ if (dumpFocusedStackOnly) {
+ return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
+ } else {
+ ArrayList<ActivityRecord> activities = new ArrayList<>();
+ int numDisplays = mActivityDisplays.size();
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
+ activities.addAll(stack.getDumpActivitiesLocked(name));
+ }
+ }
+ }
+ return activities;
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ display.dump(pw, prefix);
+ }
+ }
+
+ /**
+ * Dump all connected displays' configurations.
+ * @param prefix Prefix to apply to each line of the dump.
+ */
+ void dumpDisplayConfigs(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.println("Display override configurations:");
+ final int displayCount = mActivityDisplays.size();
+ for (int i = 0; i < displayCount; i++) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+ pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
+ pw.println(activityDisplay.getOverrideConfiguration());
+ }
+ }
+
+ public void dumpDisplays(PrintWriter pw) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ pw.print("[id:" + display.mDisplayId + " stacks:");
+ display.dumpStacks(pw);
+ pw.print("]");
+ }
+ }
+
+ boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
+ String dumpPackage) {
+ boolean printed = false;
+ boolean needSep = false;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+ pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
+ pw.println(" (activities from top to bottom):");
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
+ pw.println();
+ pw.println(" Stack #" + stack.mStackId
+ + ": type=" + activityTypeToString(stack.getActivityType())
+ + " mode=" + windowingModeToString(stack.getWindowingMode()));
+ pw.println(" isSleeping=" + stack.shouldSleepActivities());
+ pw.println(" mBounds=" + stack.getOverrideBounds());
+
+ printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+ needSep);
+
+ printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false,
+ !dumpAll, false, dumpPackage, true,
+ " Running activities (most recent first):", null);
+
+ needSep = printed;
+ boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
+ " mPausingActivity: ");
+ if (pr) {
+ printed = true;
+ needSep = false;
+ }
+ pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
+ " mResumedActivity: ");
+ if (pr) {
+ printed = true;
+ needSep = false;
+ }
+ if (dumpAll) {
+ pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
+ " mLastPausedActivity: ");
+ if (pr) {
+ printed = true;
+ needSep = true;
+ }
+ printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
+ needSep, " mLastNoHistoryActivity: ");
+ }
+ needSep = printed;
+ }
+ printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+ " ResumedActivity:");
+ }
+
+ printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ",
+ "Fin", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to finish:", null);
+ printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ",
+ "Stop", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to stop:", null);
+ printed |= dumpHistoryList(fd, pw,
+ mStackSupervisor.mActivitiesWaitingForVisibleActivity, " ", "Wait",
+ false, !dumpAll, false, dumpPackage, true,
+ " Activities waiting for another to become visible:", null);
+ printed |= dumpHistoryList(fd, pw, mStackSupervisor.mGoingToSleepActivities,
+ " ", "Sleep", false, !dumpAll,
+ false, dumpPackage, true, " Activities waiting to sleep:", null);
+
+ return printed;
+ }
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+ activityDisplay.writeToProto(proto, DISPLAYS);
+ }
+ mStackSupervisor.getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
+ // TODO(b/111541062): Update tests to look for resumed activities on all displays
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ if (focusedStack != null) {
+ proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+ final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+ if (focusedActivity != null) {
+ focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ }
+ } else {
+ proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+ }
+ proto.write(IS_HOME_RECENTS_COMPONENT,
+ mStackSupervisor.mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+ mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
+ proto.end(token);
+ }
+
+ private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken {
+ private final String mTag;
+ private final long mAcquireTime;
+ private final int mDisplayId;
+
+ public SleepTokenImpl(String tag, int displayId) {
+ mTag = tag;
+ mDisplayId = displayId;
+ mAcquireTime = SystemClock.uptimeMillis();
+ }
+
+ @Override
+ public void release() {
+ synchronized (mService.mGlobalLock) {
+ removeSleepToken(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{\"" + mTag + "\", display " + mDisplayId
+ + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b483fd3..b98d154 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -82,12 +82,16 @@
import java.util.function.Consumer;
/** Root {@link WindowContainer} for the device. */
-class RootWindowContainer extends WindowContainer<DisplayContent> {
+class RootWindowContainer extends WindowContainer<DisplayContent>
+ implements ConfigurationContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM;
private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
+ // TODO: Remove after object merge with RootActivityContainer.
+ private RootActivityContainer mRootActivityContainer;
+
private Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private float mScreenBrightness = -1;
@@ -145,6 +149,13 @@
mHandler = new MyHandler(service.mH.getLooper());
}
+ void setRootActivityContainer(RootActivityContainer container) {
+ mRootActivityContainer = container;
+ if (container != null) {
+ container.registerConfigurationChangeListener(this);
+ }
+ }
+
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
boolean changed = false;
int topFocusedDisplayId = INVALID_DISPLAY;
@@ -171,7 +182,7 @@
mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL;
if (mTopFocusedDisplayId != topFocusedDisplayId) {
mTopFocusedDisplayId = topFocusedDisplayId;
- mService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
+ mWmService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
+ mTopFocusedDisplayId);
}
@@ -183,15 +194,13 @@
final boolean isTopFocusedDisplay =
topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId;
if (windowFocusChanged || isTopFocusedDisplay) {
- final Message msg = mService.mH.obtainMessage(
+ final Message msg = mWmService.mH.obtainMessage(
WindowManagerService.H.REPORT_FOCUS_CHANGE, dc);
msg.arg1 = topFocusedDisplayChanged ? 1 : 0;
- mService.mH.sendMessage(msg);
+ mWmService.mH.sendMessage(msg);
}
});
- final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
- mService.mInputManager.setFocusedWindow(
- topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
+
return changed;
}
@@ -202,7 +211,7 @@
@Override
void onChildPositionChanged() {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
}
DisplayContent getDisplayContent(int displayId) {
@@ -224,27 +233,42 @@
final DisplayContent existing = getDisplayContent(displayId);
if (existing != null) {
+ initializeDisplayOverrideConfiguration(controller, existing);
existing.setController(controller);
return existing;
}
- final DisplayContent dc = new DisplayContent(display, mService, controller);
+ final DisplayContent dc = new DisplayContent(display, mWmService, controller);
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
- mService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
+ mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
+ initializeDisplayOverrideConfiguration(controller, dc);
- if (mService.mDisplayManagerInternal != null) {
- mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
+ if (mWmService.mDisplayManagerInternal != null) {
+ mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayId, dc.getDisplayInfo());
dc.configureDisplayPolicy();
}
- mService.reconfigureDisplayLocked(dc);
+ mWmService.reconfigureDisplayLocked(dc);
return dc;
}
+ /**
+ * The display content may have configuration set from {@link #DisplayWindowSettings}. This
+ * callback let the owner of container know there is existing configuration to prevent the
+ * values from being replaced by the initializing {@link #ActivityDisplay}.
+ */
+ private void initializeDisplayOverrideConfiguration(DisplayWindowController controller,
+ DisplayContent displayContent) {
+ if (controller != null && controller.mListener != null) {
+ controller.mListener.onInitializeOverrideConfiguration(
+ displayContent.getOverrideConfiguration());
+ }
+ }
+
boolean isLayoutNeeded() {
final int numDisplays = mChildren.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
@@ -412,11 +436,11 @@
void removeReplacedWindows() {
if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
- mService.openSurfaceTransaction();
+ mWmService.openSurfaceTransaction();
try {
forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */);
} finally {
- mService.closeSurfaceTransaction("removeReplacedWindows");
+ mWmService.closeSurfaceTransaction("removeReplacedWindows");
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
}
}
@@ -464,7 +488,7 @@
final SparseIntArray pidCandidates = new SparseIntArray();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
mChildren.get(displayNdx).forAllWindows((w) -> {
- if (mService.mForceRemoves.contains(w)) {
+ if (mWmService.mForceRemoves.contains(w)) {
return;
}
final WindowStateAnimator wsa = w.mWinAnimator;
@@ -479,7 +503,7 @@
pids[i] = pidCandidates.keyAt(i);
}
try {
- if (mService.mActivityManager.killPids(pids, "Free memory", secure)) {
+ if (mWmService.mActivityManager.killPids(pids, "Free memory", secure)) {
killedApps = true;
}
} catch (RemoteException e) {
@@ -497,9 +521,8 @@
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
"RECOVER DESTROY", false);
winAnimator.destroySurface();
- if (winAnimator.mWin.mAppToken != null
- && winAnimator.mWin.mAppToken.getController() != null) {
- winAnimator.mWin.mAppToken.getController().removeStartingWindow();
+ if (winAnimator.mWin.mAppToken != null) {
+ winAnimator.mWin.mAppToken.removeStartingWindow();
}
}
@@ -524,9 +547,9 @@
int i;
boolean updateInputWindowsNeeded = false;
- if (mService.mFocusMayChange) {
- mService.mFocusMayChange = false;
- updateInputWindowsNeeded = mService.updateFocusedWindowLocked(
+ if (mWmService.mFocusMayChange) {
+ mWmService.mFocusMayChange = false;
+ updateInputWindowsNeeded = mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
@@ -542,31 +565,31 @@
mUserActivityTimeout = -1;
mObscureApplicationContentOnSecondaryDisplays = false;
mSustainedPerformanceModeCurrent = false;
- mService.mTransactionSequence++;
+ mWmService.mTransactionSequence++;
// TODO(multi-display): recents animation & wallpaper need support multi-display.
- final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
- final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+ final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
+ final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
- mService.openSurfaceTransaction();
+ mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction(recoveringMemory);
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
- mService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
+ mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
- mService.mAnimator.executeAfterPrepareSurfacesRunnables();
+ mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
// Defer starting the recents animation until the wallpaper has drawn
final RecentsAnimationController recentsAnimationController =
- mService.getRecentsAnimationController();
+ mWmService.getRecentsAnimationController();
if (recentsAnimationController != null) {
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
@@ -583,9 +606,9 @@
}
}
- if (mService.mFocusMayChange) {
- mService.mFocusMayChange = false;
- if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+ if (mWmService.mFocusMayChange) {
+ mWmService.mFocusMayChange = false;
+ if (mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/)) {
updateInputWindowsNeeded = true;
}
@@ -599,23 +622,23 @@
handleResizingWindows();
- if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG,
+ if (DEBUG_ORIENTATION && mWmService.mDisplayFrozen) Slog.v(TAG,
"With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
if (mOrientationChangeComplete) {
- if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
- mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
- mService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
- mService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
+ if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+ mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
+ mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
+ mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
}
- mService.stopFreezingDisplayLocked();
+ mWmService.stopFreezingDisplayLocked();
}
// Destroy the surface of any windows that are no longer visible.
- i = mService.mDestroySurface.size();
+ i = mWmService.mDestroySurface.size();
if (i > 0) {
do {
i--;
- WindowState win = mService.mDestroySurface.get(i);
+ WindowState win = mWmService.mDestroySurface.get(i);
win.mDestroying = false;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
@@ -627,7 +650,7 @@
win.destroySurfaceUnchecked();
win.mWinAnimator.destroyPreservedSurfaceLocked();
} while (i > 0);
- mService.mDestroySurface.clear();
+ mWmService.mDestroySurface.clear();
}
// Time to remove any exiting tokens?
@@ -648,8 +671,8 @@
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
});
- mService.setHoldScreenLocked(mHoldScreen);
- if (!mService.mDisplayFrozen) {
+ mWmService.setHoldScreenLocked(mHoldScreen);
+ if (!mWmService.mDisplayFrozen) {
final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f
? -1 : toBrightnessOverride(mScreenBrightness);
@@ -661,7 +684,7 @@
if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
- mService.mPowerManagerInternal.powerHint(
+ mWmService.mPowerManagerInternal.powerHint(
PowerHint.SUSTAINED_PERFORMANCE,
(mSustainedPerformanceModeEnabled ? 1 : 0));
}
@@ -671,22 +694,22 @@
mUpdateRotation = updateRotationUnchecked();
}
- if (mService.mWaitingForDrawnCallback != null
+ if (mWmService.mWaitingForDrawnCallback != null
|| (mOrientationChangeComplete && !isLayoutNeeded()
&& !mUpdateRotation)) {
- mService.checkDrawnWindowsLocked();
+ mWmService.checkDrawnWindowsLocked();
}
- final int N = mService.mPendingRemove.size();
+ final int N = mWmService.mPendingRemove.size();
if (N > 0) {
- if (mService.mPendingRemoveTmp.length < N) {
- mService.mPendingRemoveTmp = new WindowState[N + 10];
+ if (mWmService.mPendingRemoveTmp.length < N) {
+ mWmService.mPendingRemoveTmp = new WindowState[N + 10];
}
- mService.mPendingRemove.toArray(mService.mPendingRemoveTmp);
- mService.mPendingRemove.clear();
+ mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
+ mWmService.mPendingRemove.clear();
ArrayList<DisplayContent> displayList = new ArrayList();
for (i = 0; i < N; i++) {
- final WindowState w = mService.mPendingRemoveTmp[i];
+ final WindowState w = mWmService.mPendingRemoveTmp[i];
w.removeImmediately();
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null && !displayList.contains(displayContent)) {
@@ -714,12 +737,13 @@
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
- mService.enableScreenIfNeededLocked();
+ mWmService.enableScreenIfNeededLocked();
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
- "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
+ "performSurfacePlacementInner exit: animating="
+ + mWmService.mAnimator.isAnimating());
}
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
@@ -759,23 +783,23 @@
mObscuringWindow = null;
// TODO(multi-display): Support these features on secondary screens.
- final DisplayContent defaultDc = mService.getDefaultDisplayContentLocked();
+ final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
final int defaultDw = defaultInfo.logicalWidth;
final int defaultDh = defaultInfo.logicalHeight;
- if (mService.mWatermark != null) {
- mService.mWatermark.positionSurface(defaultDw, defaultDh);
+ if (mWmService.mWatermark != null) {
+ mWmService.mWatermark.positionSurface(defaultDw, defaultDh);
}
- if (mService.mStrictModeFlash != null) {
- mService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+ if (mWmService.mStrictModeFlash != null) {
+ mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
}
- if (mService.mCircularDisplayMask != null) {
- mService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
- mService.getDefaultDisplayRotation());
+ if (mWmService.mCircularDisplayMask != null) {
+ mWmService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
+ mWmService.getDefaultDisplayRotation());
}
- if (mService.mEmulatorDisplayOverlay != null) {
- mService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
- mService.getDefaultDisplayRotation());
+ if (mWmService.mEmulatorDisplayOverlay != null) {
+ mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
+ mWmService.getDefaultDisplayRotation());
}
final int count = mChildren.size();
@@ -786,7 +810,7 @@
// Give the display manager a chance to adjust properties like display rotation if it needs
// to.
- mService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
+ mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
}
@@ -794,14 +818,14 @@
* Handles resizing windows during surface placement.
*/
private void handleResizingWindows() {
- for (int i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
- WindowState win = mService.mResizingWindows.get(i);
+ for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
+ WindowState win = mWmService.mResizingWindows.get(i);
if (win.mAppFreezing) {
// Don't remove this window until rotation has completed.
continue;
}
win.reportResized();
- mService.mResizingWindows.remove(i);
+ mWmService.mResizingWindows.remove(i);
}
}
@@ -839,7 +863,7 @@
if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
mHoldScreen = w.mSession;
mHoldScreenWindow = w;
- } else if (DEBUG_KEEP_SCREEN_ON && w == mService.mLastWakeLockHoldingWindow) {
+ } else if (DEBUG_KEEP_SCREEN_ON && w == mWmService.mLastWakeLockHoldingWindow) {
Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked: " + w + " was holding "
+ "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by"
+ Debug.getCallers(10));
@@ -888,7 +912,7 @@
boolean copyAnimToLayoutParams() {
boolean doRequest = false;
- final int bulkUpdateParams = mService.mAnimator.mBulkUpdateParams;
+ final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
mUpdateRotation = true;
doRequest = true;
@@ -897,8 +921,8 @@
mOrientationChangeComplete = false;
} else {
mOrientationChangeComplete = true;
- mLastWindowFreezeSource = mService.mAnimator.mLastWindowFreezeSource;
- if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+ mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
+ if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
doRequest = true;
}
}
@@ -924,12 +948,12 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case SET_SCREEN_BRIGHTNESS_OVERRIDE:
- mService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
+ mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
msg.arg1);
break;
case SET_USER_ACTIVITY_TIMEOUT:
- mService.mPowerManagerInternal.setUserActivityTimeoutOverrideFromWindowManager(
- (Long) msg.obj);
+ mWmService.mPowerManagerInternal.
+ setUserActivityTimeoutOverrideFromWindowManager((Long) msg.obj);
break;
default:
break;
@@ -939,7 +963,7 @@
void dumpDisplayContents(PrintWriter pw) {
pw.println("WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)");
- if (mService.mDisplayReady) {
+ if (mWmService.mDisplayReady) {
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final DisplayContent displayContent = mChildren.get(i);
@@ -992,7 +1016,7 @@
public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
final long token = proto.start(fieldId);
super.writeToProto(proto, WINDOW_CONTAINER, trim);
- if (mService.mDisplayReady) {
+ if (mWmService.mDisplayReady) {
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final DisplayContent displayContent = mChildren.get(i);
@@ -1015,9 +1039,8 @@
@Override
void positionChildAt(int position, DisplayContent child, boolean includingParents) {
super.positionChildAt(position, child, includingParents);
- final RootWindowContainerController controller = getController();
- if (controller != null) {
- controller.onChildPositionChanged(child, position);
+ if (mRootActivityContainer != null) {
+ mRootActivityContainer.onChildPositionChanged(child.getController(), position);
}
}
@@ -1027,13 +1050,8 @@
}
@Override
- RootWindowContainerController getController() {
- return (RootWindowContainerController) super.getController();
- }
-
- @Override
void scheduleAnimation() {
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
}
/**
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerController.java b/services/core/java/com/android/server/wm/RootWindowContainerController.java
deleted file mode 100644
index 1176220..0000000
--- a/services/core/java/com/android/server/wm/RootWindowContainerController.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm;
-
-/**
- * Controller for the root container. This is created by activity manager to link activity
- * stack supervisor to the root window container they use in window manager.
- */
-public class RootWindowContainerController
- extends WindowContainerController<RootWindowContainer, RootWindowContainerListener> {
-
- public RootWindowContainerController(RootWindowContainerListener listener) {
- super(listener, WindowManagerService.getInstance());
- synchronized (mGlobalLock) {
- mRoot.setController(this);
- }
- }
-
- void onChildPositionChanged(DisplayContent child, int position) {
- // This callback invokes to AM directly so here assumes AM lock is held. If there is another
- // path called only with WM lock, it should change to use handler to post or move outside of
- // WM lock with adding AM lock.
- mListener.onChildPositionChanged(child.getController(), position);
- }
-
- /** Move the display to the given position. */
- public void positionChildAt(DisplayWindowController child, int position) {
- synchronized (mGlobalLock) {
- mContainer.positionChildAt(position, child.mContainer);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerListener.java b/services/core/java/com/android/server/wm/RootWindowContainerListener.java
deleted file mode 100644
index f413e3f7..0000000
--- a/services/core/java/com/android/server/wm/RootWindowContainerListener.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm;
-
-/**
- * Interface used by the creator of {@link RootWindowContainerController} to notify the changes to
- * the display container in activity manager.
- */
-public interface RootWindowContainerListener extends WindowContainerListener {
- /** Called when the z-order of display is changed. */
- void onChildPositionChanged(DisplayWindowController childController, int position);
-}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index df97027..3947bd4 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -222,7 +222,7 @@
}
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
- boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service) {
+ boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
mService = service;
mContext = context;
mDisplayContent = displayContent;
@@ -234,7 +234,7 @@
final int originalWidth;
final int originalHeight;
DisplayInfo displayInfo = displayContent.getDisplayInfo();
- if (forceDefaultOrientation) {
+ if (fixedToUserRotation) {
// Emulated orientation.
mForceDefaultOrientation = true;
originalWidth = displayContent.mBaseDisplayWidth;
@@ -261,7 +261,7 @@
try {
mSurfaceControl = displayContent.makeOverlay()
.setName("ScreenshotSurface")
- .setSize(mWidth, mHeight)
+ .setBufferSize(mWidth, mHeight)
.setSecure(isSecure)
.build();
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 6838c55..37b5a7c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -50,6 +50,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.view.InsetsState;
import android.view.WindowManager;
import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -153,17 +154,21 @@
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+ DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+ InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
- outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
+ outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
+ outInsetsState);
}
@Override
public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
+ int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
+ InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
- new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */);
+ new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
+ outInsetsState);
}
@Override
@@ -182,7 +187,7 @@
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
- Surface outSurface) {
+ Surface outSurface, InsetsState outInsetsState) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -190,7 +195,7 @@
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
outStableInsets, outsets, outBackdropFrame, cutout,
- mergedConfiguration, outSurface);
+ mergedConfiguration, outSurface, outInsetsState);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 35264a2..8f18aa5 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -252,7 +252,7 @@
* before mContainer has been updated, any relevant properties (like {@param windowingMode})
* need to be passed in.
*/
- public void adjustConfigurationForBounds(Rect bounds, Rect insetBounds,
+ public void adjustConfigurationForBounds(Rect bounds,
Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
boolean overrideHeight, float density, Configuration config,
Configuration parentConfig, int windowingMode) {
@@ -303,11 +303,9 @@
// Additionally task dimensions should not be bigger than its parents dimensions.
// The non decor inset are areas that could never be removed in Honeycomb. See
// {@link WindowManagerPolicy#getNonDecorInsetsLw}.
- intersectDisplayBoundsExcludeInsets(nonDecorBounds,
- insetBounds != null ? insetBounds : bounds, mTmpNonDecorInsets,
+ intersectDisplayBoundsExcludeInsets(nonDecorBounds, bounds, mTmpNonDecorInsets,
mTmpDisplayBounds, overrideWidth, overrideHeight);
- intersectDisplayBoundsExcludeInsets(stableBounds,
- insetBounds != null ? insetBounds : bounds, mTmpStableInsets,
+ intersectDisplayBoundsExcludeInsets(stableBounds, bounds, mTmpStableInsets,
mTmpDisplayBounds, overrideWidth, overrideHeight);
width = Math.min((int) (stableBounds.width() / density),
parentConfig.screenWidthDp);
@@ -323,7 +321,7 @@
config.screenWidthDp = width;
config.screenHeightDp = height;
config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
- insetBounds != null ? insetBounds : bounds, density, windowingMode);
+ bounds, density, windowingMode);
}
}
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index e97b366..82f2ad8 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -24,12 +23,9 @@
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.Display;
-import android.view.Surface.OutOfResourcesException;
import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
class StrictModeFlash {
private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM;
@@ -46,7 +42,7 @@
try {
ctrl = dc.makeOverlay()
.setName("StrictModeFlash")
- .setSize(1, 1)
+ .setBufferSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
@@ -122,7 +118,7 @@
}
mLastDW = dw;
mLastDH = dh;
- mSurfaceControl.setSize(dw, dh);
+ mSurfaceControl.setBufferSize(dw, dh);
mDrawNeeded = true;
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 31c0c7f..11068ce 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -302,8 +302,7 @@
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
.setParent(mAnimatable.getAnimationLeashParent())
- .setName(surface + " - animation-leash")
- .setSize(width, height);
+ .setName(surface + " - animation-leash");
final SurfaceControl leash = builder.build();
t.setWindowCrop(leash, width, height);
if (!hidden) {
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index 0c9a14b..bbe5424 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -1,17 +1,6 @@
{
"presubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
- "options": [
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "android.support.test.filters.FlakyTest"
- }
- ]
- },
- {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c9800f8..c70f075 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,11 +27,11 @@
import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
+import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.ID;
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.TEMP_INSET_BOUNDS;
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -66,8 +66,8 @@
final Rect mPreparedFrozenBounds = new Rect();
final Configuration mPreparedFrozenMergedConfig = new Configuration();
- // Bounds used to calculate the insets.
- private final Rect mTempInsetBounds = new Rect();
+ // If non-empty, bounds used to display the task during animations/interactions.
+ private final Rect mOverrideDisplayedBounds = new Rect();
/** ID of the display which rotation {@link #mRotation} has. */
private int mLastRotationDisplayId = Display.INVALID_DISPLAY;
@@ -302,29 +302,28 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- updateSurfaceSize(dc);
adjustBoundsForDisplayChangeIfNeeded(dc);
super.onDisplayChanged(dc);
}
/**
- * Sets the bounds used to calculate the insets. See
- * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
+ * Sets bounds that override where the task is displayed. Used during transient operations
+ * like animation / interaction.
*/
- void setTempInsetBounds(Rect tempInsetBounds) {
- if (tempInsetBounds != null) {
- mTempInsetBounds.set(tempInsetBounds);
+ void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
+ if (overrideDisplayedBounds != null) {
+ mOverrideDisplayedBounds.set(overrideDisplayedBounds);
} else {
- mTempInsetBounds.setEmpty();
+ mOverrideDisplayedBounds.setEmpty();
}
}
/**
- * Gets the bounds used to calculate the insets. See
+ * Gets the bounds that override where the task is displayed. See
* {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
*/
- void getTempInsetBounds(Rect out) {
- out.set(mTempInsetBounds);
+ Rect getOverrideDisplayedBounds() {
+ return mOverrideDisplayedBounds;
}
void setResizeable(int resizeMode) {
@@ -333,7 +332,7 @@
boolean isResizeable() {
return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
- || mService.mForceResizableTasks;
+ || mWmService.mForceResizableTasks;
}
/**
@@ -381,8 +380,13 @@
} else {
mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
}
- setTempInsetBounds(tempInsetBounds);
- setBounds(mTmpRect2, false /* forced */);
+ if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
+ setOverrideDisplayedBounds(null);
+ setBounds(mTmpRect2);
+ } else {
+ setOverrideDisplayedBounds(mTmpRect2);
+ setBounds(tempInsetBounds);
+ }
}
/** Return true if the current bound can get outputted to the rest of the system as-is. */
@@ -408,6 +412,15 @@
mStack.getDisplayContent().getBounds(out);
}
+ @Override
+ public Rect getDisplayedBounds() {
+ if (mOverrideDisplayedBounds.isEmpty()) {
+ return super.getDisplayedBounds();
+ } else {
+ return mOverrideDisplayedBounds;
+ }
+ }
+
/**
* Calculate the maximum visible area of this task. If the task has only one app,
* the result will be visible frame of that app. If the task has more than one apps,
@@ -582,7 +595,7 @@
}
boolean isTaskAnimating() {
- final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
+ final RecentsAnimationController recentsAnim = mWmService.getRecentsAnimationController();
if (recentsAnim != null) {
if (recentsAnim.isAnimatingTask(this)) {
return true;
@@ -629,13 +642,13 @@
}
void forceWindowsScaleable(boolean force) {
- mService.openSurfaceTransaction();
+ mWmService.openSurfaceTransaction();
try {
for (int i = mChildren.size() - 1; i >= 0; i--) {
mChildren.get(i).forceWindowsScaleableInTransaction(force);
}
} finally {
- mService.closeSurfaceTransaction("forceWindowsScaleable");
+ mWmService.closeSurfaceTransaction("forceWindowsScaleable");
}
}
@@ -724,7 +737,7 @@
}
proto.write(FILLS_PARENT, matchParentBounds());
getBounds().writeToProto(proto, BOUNDS);
- mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
+ mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS);
proto.write(DEFER_REMOVAL, mDeferRemoval);
proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
@@ -740,7 +753,7 @@
pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
pw.println(doublePrefix + "mdr=" + mDeferRemoval);
pw.println(doublePrefix + "appTokens=" + mChildren);
- pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString());
+ pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
final String triplePrefix = doublePrefix + " ";
final String quadruplePrefix = triplePrefix + " ";
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 3b3feac..bb3df02 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -283,8 +283,8 @@
void notifyActivityPinned(ActivityRecord r) {
mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
- r.getTask().taskId, r.getStackId(), r.packageName);
- msg.sendingUid = r.userId;
+ r.getTaskRecord().taskId, r.getStackId(), r.packageName);
+ msg.sendingUid = r.mUserId;
forAllLocalListeners(mNotifyActivityPinned, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 117984a..4ae2a79 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -138,7 +138,7 @@
// STEP 1: Determine the display to launch the activity/task.
final int displayId = getPreferredLaunchDisplay(task, options, source, currentParams);
outParams.mPreferredDisplayId = displayId;
- ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+ ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
if (DEBUG) {
appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode="
+ display.getWindowingMode());
@@ -300,12 +300,14 @@
displayId = stack.mDisplayId;
}
- if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) {
+ if (displayId != INVALID_DISPLAY
+ && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) {
displayId = currentParams.mPreferredDisplayId;
}
displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId;
- return (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) != null)
+ return (displayId != INVALID_DISPLAY
+ && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) != null)
? displayId : DEFAULT_DISPLAY;
}
@@ -606,7 +608,8 @@
|| displayBounds.height() < inOutBounds.height()) {
// There is no way for us to fit the bounds in the display without changing width
// or height. Just move the start to align with the display.
- final int layoutDirection = mSupervisor.getConfiguration().getLayoutDirection();
+ final int layoutDirection =
+ mSupervisor.mRootActivityContainer.getConfiguration().getLayoutDirection();
final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
? displayBounds.width() - inOutBounds.width()
: 0;
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 8120dec..d50af38 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import android.annotation.NonNull;
import android.graphics.Bitmap;
@@ -330,7 +330,7 @@
// mWriteQueue.add(new TaskWriteQueueItem(task));
final int taskId = task.taskId;
- if (mStackSupervisor.anyTaskForIdLocked(taskId,
+ if (mService.mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
// Should not happen.
Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 7182ad6..b88e581 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -33,6 +33,7 @@
import android.app.IActivityTaskManager;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
@@ -261,7 +262,7 @@
mClientChannel, mService.mAnimationHandler.getLooper(),
mService.mAnimator.getChoreographer());
- mDragApplicationHandle = new InputApplicationHandle(null);
+ mDragApplicationHandle = new InputApplicationHandle(new Binder());
mDragApplicationHandle.name = TAG;
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -269,7 +270,7 @@
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
display.getDisplayId());
mDragWindowHandle.name = TAG;
- mDragWindowHandle.inputChannel = mServerChannel;
+ mDragWindowHandle.token = mServerChannel.getToken();
mDragWindowHandle.layer = mService.getDragLayerLocked();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 51567a0..e15bf5b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -21,15 +21,19 @@
import android.annotation.Nullable;
import android.app.IActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.Display;
import android.view.IWindow;
+import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
import com.android.internal.annotations.GuardedBy;
import com.android.server.input.InputManagerService;
-import android.view.InputWindowHandle;
/**
* Controller for task positioning by drag.
@@ -39,10 +43,14 @@
private final InputManagerService mInputManager;
private final IActivityTaskManager mActivityManager;
private final Handler mHandler;
+ private SurfaceControl mInputSurface;
+ private DisplayContent mPositioningDisplay;
@GuardedBy("WindowManagerSerivce.mWindowMap")
private @Nullable TaskPositioner mTaskPositioner;
+ private final Rect mTmpClipRect = new Rect();
+
boolean isPositioningLocked() {
return mTaskPositioner != null;
}
@@ -59,6 +67,43 @@
mHandler = new Handler(looper);
}
+ void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId
+ && mInputSurface != null) {
+ t.hide(mInputSurface);
+ }
+ }
+
+ void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+ if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) {
+ return;
+ }
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ if (mInputSurface == null) {
+ mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
+ .setContainerLayer(true)
+ .setName("Drag and Drop Input Consumer").build();
+ }
+
+ final InputWindowHandle h = getDragWindowHandleLocked();
+ if (h == null) {
+ Slog.w(TAG_WM, "Drag is in progress but there is no "
+ + "drag window handle.");
+ return;
+ }
+
+ t.show(mInputSurface);
+ t.setInputWindowInfo(mInputSurface, h);
+ t.setLayer(mInputSurface, Integer.MAX_VALUE);
+
+ final Display display = dc.getDisplay();
+ final Point p = new Point();
+ display.getRealSize(p);
+
+ mTmpClipRect.set(0, 0, p.x, p.y);
+ t.setWindowCrop(mInputSurface, mTmpClipRect);
+ }
+
boolean startMovingTask(IWindow window, float startX, float startY) {
WindowState win = null;
synchronized (mService.mGlobalLock) {
@@ -122,6 +167,7 @@
Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
return false;
}
+ mPositioningDisplay = displayContent;
mTaskPositioner = TaskPositioner.create(mService);
mTaskPositioner.register(displayContent);
@@ -138,9 +184,7 @@
if (!mInputManager.transferTouchFocus(
transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
- mTaskPositioner.unregister();
- mTaskPositioner = null;
- displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
+ cleanUpTaskPositioner();
return false;
}
@@ -153,11 +197,21 @@
if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
synchronized (mService.mGlobalLock) {
- if (mTaskPositioner != null) {
- mTaskPositioner.unregister();
- mTaskPositioner = null;
- }
+ cleanUpTaskPositioner();
+ mPositioningDisplay = null;
}
});
}
+
+ private void cleanUpTaskPositioner() {
+ final TaskPositioner positioner = mTaskPositioner;
+ if (positioner == null) {
+ return;
+ }
+
+ // We need to assign task positioner to null first to indicate that we're finishing task
+ // positioning.
+ mTaskPositioner = null;
+ positioner.unregister();
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index eec10ab..3cf0bd7 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -310,6 +310,11 @@
// This number will be assigned when we evaluate OOM scores for all visible tasks.
int mLayerRank = -1;
+ // When non-empty, this represents the bounds this task will be drawn at. This gets set during
+ // transient operations such as split-divider dragging and animations.
+ // TODO(b/119687367): This member is temporary.
+ final Rect mDisplayedBounds = new Rect();
+
/** Helper object used for updating override configuration. */
private Configuration mTmpConfig = new Configuration();
@@ -447,6 +452,9 @@
}
mWindowContainerController = controller;
+ if (!mDisplayedBounds.isEmpty() && controller.mContainer != null) {
+ controller.mContainer.setOverrideDisplayedBounds(mDisplayedBounds);
+ }
}
void removeWindowContainer() {
@@ -472,8 +480,8 @@
}
mResizeMode = resizeMode;
mWindowContainerController.setResizeable(resizeMode);
- mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
void setTaskDockedResizing(boolean resizing) {
@@ -544,10 +552,9 @@
// this won't cause tons of irrelevant windows being preserved because only
// activities in this task may experience a bounds change. Configs for other
// activities stay the same.
- mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
- preserveWindow);
+ mService.mRootActivityContainer.ensureActivitiesVisible(r, 0, preserveWindow);
if (!kept) {
- mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
}
@@ -623,6 +630,7 @@
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
+ final RootActivityContainer root = mService.mRootActivityContainer;
final WindowManagerService windowManager = mService.mWindowManager;
final ActivityStack sourceStack = getStack();
final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
@@ -655,7 +663,7 @@
boolean kept = true;
try {
final ActivityRecord r = topRunningActivityLocked();
- final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
+ final boolean wasFocused = r != null && root.isTopDisplayFocusedStack(sourceStack)
&& (topRunningActivityLocked() == r);
final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
@@ -748,8 +756,8 @@
if (!deferResume) {
// The task might have already been running and its visibility needs to be synchronized
// with the visibility of the stack / windows.
- supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
- supervisor.resumeFocusedStacksTopActivitiesLocked();
+ root.ensureActivitiesVisible(null, 0, !mightReplaceWindow);
+ root.resumeFocusedStacksTopActivities();
}
// TODO: Handle incorrect request to move before the actual move, not after.
@@ -903,7 +911,7 @@
// Correct the activity intent for aliasing. The task record intent will always be based on
// the real activity that will be launched not the alias, so we need to use an intent with
// the component name pointing to the real activity not the alias in the activity record.
- intent.setComponent(r.realActivity);
+ intent.setComponent(r.mActivityComponent);
return intent.filterEquals(this.intent);
}
@@ -982,7 +990,7 @@
@Override
protected void onParentChanged() {
super.onParentChanged();
- mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+ mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
}
// Close up recents linked list.
@@ -1143,7 +1151,7 @@
}
boolean okToShowLocked() {
- // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
+ // NOTE: If {@link TaskRecord#topRunningActivity} return is not null then it is
// okay to show the activity when locked.
return mService.mStackSupervisor.isCurrentProfileLocked(userId)
|| topRunningActivityLocked() != null;
@@ -1182,7 +1190,7 @@
mActivities.add(newTop);
// Make sure window manager is aware of the position change.
- mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
+ mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken);
updateEffectiveIntent();
setFrontOfTask();
@@ -1211,7 +1219,7 @@
* be in the current task or unparented to any task.
*/
void addActivityAtIndex(int index, ActivityRecord r) {
- TaskRecord task = r.getTask();
+ TaskRecord task = r.getTaskRecord();
if (task != null && task != this) {
throw new IllegalArgumentException("Can not add r=" + " to task=" + this
+ " current parent=" + task);
@@ -1264,17 +1272,15 @@
mService.notifyTaskPersisterLocked(this, false);
}
- // Sync. with window manager
- final AppWindowContainerController appController = r.getWindowContainerController();
- if (appController != null) {
+ if (r.mAppWindowToken != null) {
// Only attempt to move in WM if the child has a controller. It is possible we haven't
// created controller for the activity we are starting yet.
- mWindowContainerController.positionChildAt(appController, index);
+ mWindowContainerController.positionChildAt(r.mAppWindowToken, index);
}
// Make sure the list of display UID whitelists is updated
// now that this record is in a new task.
- mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+ mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
}
/**
@@ -1287,7 +1293,7 @@
}
boolean removeActivity(ActivityRecord r, boolean reparenting) {
- if (r.getTask() != this) {
+ if (r.getTaskRecord() != this) {
throw new IllegalArgumentException(
"Activity=" + r + " does not belong to task=" + this);
}
@@ -1403,7 +1409,7 @@
if (r.finishing) {
continue;
}
- if (r.realActivity.equals(newR.realActivity)) {
+ if (r.mActivityComponent.equals(newR.mActivityComponent)) {
// Here it is! Now finish everything in front...
final ActivityRecord ret = r;
@@ -1561,13 +1567,13 @@
* the index within the history at which it's found, or < 0 if not found.
*/
final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
- final ComponentName realActivity = r.realActivity;
+ final ComponentName realActivity = r.mActivityComponent;
for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord candidate = mActivities.get(activityNdx);
if (candidate.finishing) {
continue;
}
- if (candidate.realActivity.equals(realActivity)) {
+ if (candidate.mActivityComponent.equals(realActivity)) {
return candidate;
}
}
@@ -1683,9 +1689,9 @@
// to do this for the pinned stack as the bounds are controlled by the system.
if (!inPinnedWindowingMode()) {
final int defaultMinSizeDp =
- mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
+ mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp;
final ActivityDisplay display =
- mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
+ mService.mRootActivityContainer.getActivityDisplay(mStack.mDisplayId);
final float density =
(float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
final int defaultMinSize = (int) (defaultMinSizeDp * density);
@@ -1734,9 +1740,15 @@
final Configuration newOverrideConfig = new Configuration();
if (bounds != null) {
newOverrideConfig.setTo(getOverrideConfiguration());
- mTmpRect.set(bounds);
+ if (insetBounds != null && !insetBounds.isEmpty()) {
+ mTmpRect.set(insetBounds);
+ setDisplayedBounds(bounds);
+ } else {
+ mTmpRect.set(bounds);
+ setDisplayedBounds(null);
+ }
adjustForMinimalTaskDimensions(mTmpRect);
- computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds,
+ computeOverrideConfiguration(newOverrideConfig, mTmpRect,
mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
}
@@ -1784,16 +1796,23 @@
setLastNonFullscreenBounds(currentBounds);
}
setBounds(null);
+ setDisplayedBounds(null);
newConfig.unset();
} else {
- mTmpRect.set(bounds);
+ if (insetBounds != null && !insetBounds.isEmpty()) {
+ mTmpRect.set(insetBounds);
+ setDisplayedBounds(bounds);
+ } else {
+ mTmpRect.set(bounds);
+ setDisplayedBounds(null);
+ }
adjustForMinimalTaskDimensions(mTmpRect);
setBounds(mTmpRect);
if (mStack == null || persistBounds) {
setLastNonFullscreenBounds(getOverrideBounds());
}
- computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
+ computeOverrideConfiguration(newConfig, mTmpRect,
mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
}
onOverrideConfigurationChanged(newConfig);
@@ -1849,11 +1868,44 @@
mService.mStackSupervisor.mLaunchParamsPersister.saveTask(this);
}
+ /**
+ * Displayed bounds are used to set where the task is drawn at any given time. This is
+ * separate from its actual bounds so that the app doesn't see any meaningful configuration
+ * changes during transitionary periods.
+ */
+ void setDisplayedBounds(Rect bounds) {
+ if (bounds == null) {
+ mDisplayedBounds.setEmpty();
+ } else {
+ mDisplayedBounds.set(bounds);
+ }
+ final TaskWindowContainerController controller = getWindowContainerController();
+ if (controller != null && controller.mContainer != null) {
+ controller.mContainer.setOverrideDisplayedBounds(
+ mDisplayedBounds.isEmpty() ? null : mDisplayedBounds);
+ }
+ }
+
+ /**
+ * Gets the current overridden displayed bounds. These will be empty if the task is not
+ * currently overriding where it is displayed.
+ */
+ Rect getDisplayedBounds() {
+ return mDisplayedBounds;
+ }
+
+ /**
+ * @return {@code true} if this has overridden displayed bounds.
+ */
+ boolean hasDisplayedBounds() {
+ return !mDisplayedBounds.isEmpty();
+ }
+
/** Clears passed config and fills it with new override values. */
// TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
// depend on task or stacks, but uses those object to get the display to base the calculation
// on. Probably best to centralize calculations like this in ConfigurationContainer.
- void computeOverrideConfiguration(Configuration config, Rect bounds, Rect insetBounds,
+ void computeOverrideConfiguration(Configuration config, Rect bounds,
boolean overrideWidth, boolean overrideHeight) {
mTmpNonDecorBounds.set(bounds);
mTmpStableBounds.set(bounds);
@@ -1865,7 +1917,7 @@
if (mStack != null) {
final StackWindowController stackController = mStack.getWindowContainerController();
- stackController.adjustConfigurationForBounds(bounds, insetBounds,
+ stackController.adjustConfigurationForBounds(bounds,
mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
config, parentConfig, getWindowingMode());
} else {
@@ -1992,7 +2044,7 @@
? reuseActivitiesReport.base.intent.getComponent()
: null;
info.topActivity = reuseActivitiesReport.top != null
- ? reuseActivitiesReport.top.realActivity
+ ? reuseActivitiesReport.top.mActivityComponent
: null;
info.origActivity = origActivity;
info.realActivity = realActivity;
@@ -2043,7 +2095,7 @@
pw.println(origActivity.flattenToShortString());
}
if (realActivity != null) {
- pw.print(prefix); pw.print("realActivity=");
+ pw.print(prefix); pw.print("mActivityComponent=");
pw.println(realActivity.flattenToShortString());
}
if (autoRemoveRecents || isPersistable || !isActivityTypeStandard() || numFullscreen != 0) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index a7b0272..9a56606 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -32,6 +32,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -65,6 +66,7 @@
import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.view.InsetsState;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -141,6 +143,7 @@
final Rect taskBounds;
final Rect tmpContentInsets = new Rect();
final Rect tmpStableInsets = new Rect();
+ final InsetsState mTmpInsetsState = new InsetsState();
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
int backgroundColor = WHITE;
int statusBarColor = 0;
@@ -201,7 +204,7 @@
try {
final int res = session.addToDisplay(window, window.mSeq, layoutParams,
View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
- tmpRect, tmpCutout, null);
+ tmpRect, tmpCutout, null, mTmpInsetsState);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return null;
@@ -217,7 +220,7 @@
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
- tmpCutout, tmpMergedConfiguration, surface);
+ tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
} catch (RemoteException e) {
// Local call.
}
@@ -312,7 +315,7 @@
// Keep a reference to it such that it doesn't get destroyed when finalized.
mChildSurfaceControl = new SurfaceControl.Builder(session)
.setName(mTitle + " - task-snapshot-surface")
- .setSize(buffer.getWidth(), buffer.getHeight())
+ .setBufferSize(buffer.getWidth(), buffer.getHeight())
.setFormat(buffer.getFormat())
.build();
Surface surface = new Surface();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 64f4ba5..b16e184c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -184,9 +184,15 @@
// Update bounds of containing tasks.
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = mChildren.get(taskNdx);
- task.setBounds(taskBounds.get(task.mTaskId), false /* forced */);
- task.setTempInsetBounds(taskTempInsetBounds != null ?
- taskTempInsetBounds.get(task.mTaskId) : null);
+ final Rect insetBounds =
+ taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId) : null;
+ if (insetBounds != null) {
+ task.setBounds(insetBounds);
+ task.setOverrideDisplayedBounds(taskBounds.get(task.mTaskId));
+ } else {
+ task.setBounds(taskBounds.get(task.mTaskId));
+ task.setOverrideDisplayedBounds(null);
+ }
}
return true;
}
@@ -248,7 +254,6 @@
getRawBounds(mTmpRect);
final Rect stackBounds = getBounds();
getPendingTransaction()
- .setSize(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
.setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
.setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left,
mTmpRect.top - stackBounds.top);
@@ -451,7 +456,7 @@
final int newDockSide = getDockSide(parentConfig, inOutBounds);
// Update the dock create mode and clear the dock create bounds, these
// might change after a rotation and the original values will be invalid.
- mService.setDockedStackCreateStateLocked(
+ mWmService.setDockedStackCreateStateLocked(
(newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
: SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
@@ -524,7 +529,7 @@
mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
displayCutout, outBounds);
final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
- mService.mContext.getResources(), displayWidth, displayHeight,
+ mWmService.mContext.getResources(), displayWidth, displayHeight,
dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
getDockSide(), isMinimizedDockAndHomeStackResizable());
final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
@@ -595,7 +600,7 @@
private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers,
boolean addingNew) {
final boolean canShowTask =
- showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
+ showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId);
final int stackSize = mChildren.size();
int minPosition = 0;
@@ -629,7 +634,7 @@
final Task tmpTask = mChildren.get(minPosition);
final boolean canShowTmpTask =
tmpTask.showForAllUsers()
- || mService.isCurrentProfileLocked(tmpTask.mUserId);
+ || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
if (canShowTmpTask) {
break;
}
@@ -648,7 +653,7 @@
final Task tmpTask = mChildren.get(maxPosition);
final boolean canShowTmpTask =
tmpTask.showForAllUsers()
- || mService.isCurrentProfileLocked(tmpTask.mUserId);
+ || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
if (!canShowTmpTask) {
break;
}
@@ -729,7 +734,7 @@
// We multiply by two to match the client logic for converting view elevation
// to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
- return (int)Math.ceil(mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
+ return (int)Math.ceil(mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
displayMetrics) * 2);
}
return 0;
@@ -740,7 +745,7 @@
return;
}
- final Rect stackBounds = getBounds();
+ final Rect stackBounds = getDisplayedBounds();
int width = stackBounds.width();
int height = stackBounds.height();
@@ -751,7 +756,6 @@
if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
return;
}
- transaction.setSize(mSurfaceControl, width, height);
transaction.setWindowCrop(mSurfaceControl, width, height);
mLastSurfaceSize.set(width, height);
}
@@ -793,7 +797,7 @@
if (dockedBounds == null || dockedBounds.isEmpty()) {
// Calculate the primary docked bounds.
- final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
+ final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode
== SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(parentConfig,
true /* primary */, outStackBounds, dockedBounds,
@@ -858,8 +862,8 @@
outBounds.set(displayRect);
if (primary) {
- if (mService.mDockedStackCreateBounds != null) {
- outBounds.set(mService.mDockedStackCreateBounds);
+ if (mWmService.mDockedStackCreateBounds != null) {
+ outBounds.set(mWmService.mDockedStackCreateBounds);
return;
}
@@ -870,7 +874,7 @@
mDisplayContent.getDisplayPolicy().getStableInsetsLw(
parentConfig.windowConfiguration.getRotation(),
displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
- final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
+ final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(),
displayRect.width(),
displayRect.height(),
dockDividerWidth,
@@ -915,7 +919,7 @@
throw new IllegalStateException("Not a docked stack=" + this);
}
- mService.mDockedStackCreateBounds = null;
+ mWmService.mDockedStackCreateBounds = null;
final Rect bounds = new Rect();
final Rect tempBounds = new Rect();
@@ -957,7 +961,7 @@
}
mDisplayContent = null;
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
void resetAnimationBackgroundAnimator() {
@@ -979,7 +983,7 @@
int top = mChildren.size();
for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
Task task = mChildren.get(taskNdx);
- if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
+ if (mWmService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
mChildren.remove(taskNdx);
mChildren.add(task);
--top;
@@ -1048,7 +1052,7 @@
}
mAdjustedForIme = false;
updateAdjustedBounds();
- mService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
+ mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
} else {
mImeGoingAway |= mAdjustedForIme;
}
@@ -1181,7 +1185,7 @@
}
if (dockSide == DOCKED_TOP) {
- mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
+ mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
int topInset = mTmpRect.top;
mTmpAdjustedBounds.set(getRawBounds());
mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
@@ -1217,7 +1221,7 @@
}
if (dockSide == DOCKED_TOP) {
- mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
+ mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
int topInset = mTmpRect.top;
return getRawBounds().bottom - topInset;
} else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
@@ -1242,11 +1246,11 @@
}
setAdjustedBounds(mTmpAdjustedBounds);
- final boolean isImeTarget = (mService.getImeFocusStackLocked() == this);
+ final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this);
if (mAdjustedForIme && adjust && !isImeTarget) {
final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
* IME_ADJUST_DIM_AMOUNT;
- mService.setResizeDimLayer(true, getWindowingMode(), alpha);
+ mWmService.setResizeDimLayer(true, getWindowingMode(), alpha);
}
}
@@ -1512,14 +1516,14 @@
public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (mCancelCurrentBoundsAnimation) {
return false;
}
}
try {
- mService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
+ mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
} catch (RemoteException e) {
// I don't believe you.
}
@@ -1537,7 +1541,7 @@
@Override // AnimatesBounds
public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (!isAttached()) {
// Don't run the animation if the stack is already detached
return false;
@@ -1557,7 +1561,7 @@
if (inPinnedWindowingMode()) {
try {
- mService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
+ mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
} catch (RemoteException e) {
// I don't believe you...
}
@@ -1600,9 +1604,9 @@
}
try {
- mService.mActivityTaskManager.notifyPinnedStackAnimationEnded();
+ mWmService.mActivityTaskManager.notifyPinnedStackAnimationEnded();
if (moveToFullscreen) {
- mService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId,
+ mWmService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId,
true /* onTop */);
}
} catch (RemoteException e) {
@@ -1616,7 +1620,7 @@
@Override
public boolean isAttached() {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
return mDisplayContent != null;
}
}
@@ -1625,19 +1629,19 @@
* Called immediately prior to resizing the tasks at the end of the pinned stack animation.
*/
public void onPipAnimationEndResize() {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
mBoundsAnimating = false;
for (int i = 0; i < mChildren.size(); i++) {
final Task t = mChildren.get(i);
t.clearPreserveNonFloatingState();
}
- mService.requestTraversal();
+ mWmService.requestTraversal();
}
}
@Override
public boolean shouldDeferStartOnMoveToFullscreen() {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (!isAttached()) {
// Unnecessary to pause the animation because the stack is detached.
return false;
@@ -1756,14 +1760,6 @@
scheduleAnimation();
}
- @Override
- void getRelativePosition(Point outPos) {
- super.getRelativePosition(outPos);
- final int outset = getStackOutset();
- outPos.x -= outset;
- outPos.y -= outset;
- }
-
AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() {
return mAnimatingAppWindowTokenRegistry;
}
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 59b2055..ec64d2e 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -16,6 +16,14 @@
package com.android.server.wm;
+import static com.android.server.EventLogTags.WM_TASK_CREATED;
+import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Rect;
@@ -24,18 +32,11 @@
import android.os.Message;
import android.util.EventLog;
import android.util.Slog;
+
import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.WeakReference;
-import static com.android.server.EventLogTags.WM_TASK_CREATED;
-import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
/**
* Controller for the task container. This is created by activity manager to link task records to
* the task container they use in window manager.
@@ -103,16 +104,15 @@
}
}
- public void positionChildAtTop(AppWindowContainerController childController) {
- positionChildAt(childController, POSITION_TOP);
+ void positionChildAtTop(AppWindowToken aToken) {
+ positionChildAt(aToken, POSITION_TOP);
}
- public void positionChildAt(AppWindowContainerController childController, int position) {
+ void positionChildAt(AppWindowToken aToken, int position) {
synchronized (mService.mGlobalLock) {
- final AppWindowToken aToken = childController.mContainer;
if (aToken == null) {
Slog.w(TAG_WM,
- "Attempted to position of non-existing app : " + childController);
+ "Attempted to position of non-existing app");
return;
}
diff --git a/services/core/java/com/android/server/wm/VrController.java b/services/core/java/com/android/server/wm/VrController.java
index abe40a7..3e136d35 100644
--- a/services/core/java/com/android/server/wm/VrController.java
+++ b/services/core/java/com/android/server/wm/VrController.java
@@ -187,7 +187,7 @@
synchronized (mGlobalAmLock) {
vrMode = record.requestedVrComponent != null;
requestedPackage = record.requestedVrComponent;
- userId = record.userId;
+ userId = record.mUserId;
callingPackage = record.info.getComponentName();
// Tell the VrController that a VR mode change is requested.
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 9216b66..e6ac059 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -20,19 +20,18 @@
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
-import android.graphics.Paint.FontMetricsInt;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
-import android.view.Surface.OutOfResourcesException;
import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
/**
* Displays a watermark on top of the window manager's windows.
@@ -116,7 +115,7 @@
try {
ctrl = dc.makeOverlay()
.setName("WatermarkSurface")
- .setSize(1, 1)
+ .setBufferSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
ctrl.setLayerStack(mDisplay.getLayerStack());
@@ -133,7 +132,7 @@
if (mLastDW != dw || mLastDH != dh) {
mLastDW = dw;
mLastDH = dh;
- mSurfaceControl.setSize(dw, dh);
+ mSurfaceControl.setBufferSize(dw, dh);
mDrawNeeded = true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 449c409..b8a0739 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -260,8 +258,7 @@
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
- + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
+ + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 266006d..c30cc17 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -115,7 +115,7 @@
*/
protected final Transaction mPendingTransaction;
protected final SurfaceAnimator mSurfaceAnimator;
- protected final WindowManagerService mService;
+ protected final WindowManagerService mWmService;
private final Point mTmpPos = new Point();
protected final Point mLastSurfacePosition = new Point();
@@ -129,10 +129,10 @@
*/
private boolean mCommittedReparentToAnimationLeash;
- WindowContainer(WindowManagerService service) {
- mService = service;
- mPendingTransaction = service.mTransactionFactory.make();
- mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
+ WindowContainer(WindowManagerService wms) {
+ mWmService = wms;
+ mPendingTransaction = wms.mTransactionFactory.make();
+ mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
}
@Override
@@ -515,24 +515,6 @@
}
}
- /**
- * Update the surface size when display changed in order to avoid children being bound by the
- * old display size.
- *
- * Note that we don't want to apply this to all layers, but only limiting this to layers that
- * don't set their own size ({@link Task}, {@link WindowState} and {@link WindowToken}).
- */
- void updateSurfaceSize(DisplayContent dc) {
- if (mSurfaceControl == null) {
- return;
- }
-
- final int newSurfaceSize = dc.getSurfaceSize();
- if (mSurfaceControl.getWidth() != newSurfaceSize) {
- getPendingTransaction().setSize(mSurfaceControl, newSurfaceSize, newSurfaceSize);
- }
- }
-
void setWaitingForDrawnIfResizingChanged() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
@@ -1284,7 +1266,7 @@
return;
}
- getRelativePosition(mTmpPos);
+ getRelativeDisplayedPosition(mTmpPos);
if (mTmpPos.equals(mLastSurfacePosition)) {
return;
}
@@ -1293,12 +1275,22 @@
mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
}
- void getRelativePosition(Point outPos) {
- final Rect bounds = getBounds();
- outPos.set(bounds.left, bounds.top);
+ /**
+ * Displayed bounds specify where to display this container at. It differs from bounds during
+ * certain operations (like animation or interactive dragging).
+ *
+ * @return the bounds to display this container at.
+ */
+ Rect getDisplayedBounds() {
+ return getBounds();
+ }
+
+ void getRelativeDisplayedPosition(Point outPos) {
+ final Rect dispBounds = getDisplayedBounds();
+ outPos.set(dispBounds.left, dispBounds.top);
final WindowContainer parent = getParent();
if (parent != null) {
- final Rect parentBounds = parent.getBounds();
+ final Rect parentBounds = parent.getDisplayedBounds();
outPos.offset(-parentBounds.left, -parentBounds.top);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainerListener.java b/services/core/java/com/android/server/wm/WindowContainerListener.java
index 4b3cd36..3d3d2e0 100644
--- a/services/core/java/com/android/server/wm/WindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/WindowContainerListener.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import android.content.res.Configuration;
+
/**
* Interface used by the owner/creator of the container to listen to changes with the container.
* @see WindowContainerController
@@ -23,4 +25,5 @@
public interface WindowContainerListener {
void registerConfigurationChangeListener(ConfigurationContainerListener listener);
void unregisterConfigurationChangeListener(ConfigurationContainerListener listener);
+ default void onInitializeOverrideConfiguration(Configuration config) {}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 25f3128..52b24b3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -66,6 +66,7 @@
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
import static com.android.server.LockGuard.INDEX_WINDOW;
@@ -208,6 +209,7 @@
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEventReceiver;
+import android.view.InsetsState;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
@@ -1111,7 +1113,8 @@
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+ DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+ InsetsState outInsetsState) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1459,6 +1462,7 @@
outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
}
+ outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -1856,7 +1860,7 @@
long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
- Surface outSurface) {
+ Surface outSurface, InsetsState outInsetsState) {
int result = 0;
boolean configChanged;
final boolean hasStatusBarPermission =
@@ -2157,6 +2161,7 @@
outStableInsets, outOutsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
+ outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
if (localLOGV) Slog.v(
TAG_WM, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
@@ -2475,26 +2480,36 @@
@Override
public void overridePendingAppTransitionMultiThumbFuture(
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
- boolean scaleUp) {
+ boolean scaleUp, int displayId) {
synchronized (mGlobalLock) {
- // TODO(multi-display): sysui using this api only support default display.
- mRoot.getDisplayContent(DEFAULT_DISPLAY)
- .mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ Slog.w(TAG, "Attempted to call overridePendingAppTransitionMultiThumbFuture"
+ + " for the display " + displayId + " that does not exist.");
+ return;
+ }
+ displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
callback, scaleUp);
}
}
@Override
- public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+ public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
+ int displayId) {
if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"overridePendingAppTransitionRemote()")) {
throw new SecurityException(
"Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
}
synchronized (mGlobalLock) {
- // TODO(multi-display): sysui using this api only support default display.
- mRoot.getDisplayContent(DEFAULT_DISPLAY)
- .mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ Slog.w(TAG, "Attempted to call overridePendingAppTransitionRemote"
+ + " for the display " + displayId + " that does not exist.");
+ return;
+ }
+ displayContent.mAppTransition.overridePendingAppTransitionRemote(
+ remoteAnimationAdapter);
}
}
@@ -3577,6 +3592,17 @@
}
}
+ void setRotateForApp(int displayId, boolean enabled) {
+ synchronized (mGlobalLock) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to set rotate for app for a missing display.");
+ return;
+ }
+ display.getDisplayRotation().setFixedToUserRotation(enabled);
+ }
+ }
+
@Override
public void freezeRotation(int rotation) {
freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
@@ -5383,7 +5409,7 @@
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure,
+ displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
@@ -5615,22 +5641,12 @@
}
}
- // TODO: Make the callers use getNavBarPosition(int) only.
- /**
- * Used by SystemUI and shared SystemUI libraries.
- * @see DisplayPolicy#getNavBarPosition()
- */
- @Override
- @WindowManagerPolicy.NavigationBarPosition
- public int getNavBarPosition() {
- return getNavBarPosition(Display.DEFAULT_DISPLAY);
- }
-
/**
* Used by ActivityManager to determine where to position an app with aspect ratio shorter then
* the screen is.
* @see DisplayPolicy#getNavBarPosition()
*/
+ @Override
@WindowManagerPolicy.NavigationBarPosition
public int getNavBarPosition(int displayId) {
synchronized (mGlobalLock) {
@@ -5640,7 +5656,7 @@
if (displayContent == null) {
Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId
+ " callers=" + Debug.getCallers(3));
- return -1;
+ return NAV_BAR_INVALID;
}
displayContent.performLayout(false /* initial */,
false /* updateInputWindows */);
@@ -5707,8 +5723,14 @@
}
@Override
- public boolean hasNavigationBar() {
- return mPolicy.hasNavigationBar();
+ public boolean hasNavigationBar(int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ return false;
+ }
+ return dc.getDisplayPolicy().hasNavigationBar();
+ }
}
@Override
@@ -7426,4 +7448,29 @@
}
}
}
+
+ @Override
+ public void reparentDisplayContent(int displayId, IBinder surfaceControlHandle) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ throw new IllegalArgumentException(
+ "Can't reparent display for non-existent displayId: " + displayId);
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int displayOwnerUid = display.getOwnerUid();
+ if (callingUid != displayOwnerUid) {
+ throw new SecurityException("Only owner of the display can reparent surfaces to it.");
+ }
+
+ synchronized (mGlobalLock) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+ displayContent.reparentDisplayContent(surfaceControlHandle);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bf77ba8..6865ce3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -76,6 +76,8 @@
getNextArgRequired());
case "set-user-rotation":
return runSetDisplayUserRotation(pw);
+ case "set-fix-to-user-rotation":
+ return runSetFixToUserRotation(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -297,6 +299,32 @@
}
}
+ private int runSetFixToUserRotation(PrintWriter pw) {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArgRequired();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ arg = getNextArgRequired();
+ }
+
+ final boolean enabled;
+ switch (arg) {
+ case "enabled":
+ enabled = true;
+ break;
+ case "disabled":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expecting enabled or disabled, but we get "
+ + arg);
+ return -1;
+ }
+
+ mInternal.setRotateForApp(displayId, enabled);
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -316,6 +344,8 @@
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
pw.println(" Set user rotation mode and user rotation.");
+ pw.println(" set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]");
+ pw.println(" Enable or disable rotating display for app requested orientation.");
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 484bd8c..2dec4dd 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -443,7 +443,7 @@
for (int i = 0; i < activities.size(); i++) {
final ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
- r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
+ r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
null, "finish-heavy", true);
}
}
@@ -513,7 +513,7 @@
}
ActivityRecord hist = mActivities.get(0);
intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
- intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId);
+ intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTaskRecord().taskId);
}
}
@@ -524,7 +524,7 @@
// Don't kill process(es) that has an activity not stopped.
return false;
}
- final TaskRecord otherTask = activity.getTask();
+ final TaskRecord otherTask = activity.getTaskRecord();
if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
// Don't kill process(es) that has an activity in a different task that is
// also in recents.
@@ -557,7 +557,7 @@
continue;
}
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (task != null) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
+ " from " + r);
@@ -600,7 +600,7 @@
}
if (r.visible) {
callback.onVisibleActivity();
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
if (task != null && minTaskLayer > 0) {
final int layer = task.mLayerRank;
if (layer >= 0 && minTaskLayer > layer) {
@@ -755,9 +755,9 @@
return;
}
final ActivityDisplay activityDisplay =
- mAtm.mStackSupervisor.getActivityDisplay(mDisplayId);
+ mAtm.mRootActivityContainer.getActivityDisplay(mDisplayId);
if (activityDisplay != null) {
- mAtm.mStackSupervisor.getActivityDisplay(
+ mAtm.mRootActivityContainer.getActivityDisplay(
mDisplayId).unregisterConfigurationChangeListener(this);
}
mDisplayId = INVALID_DISPLAY;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6f044f3..5cc3623 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -143,6 +143,7 @@
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -578,6 +579,8 @@
*/
private boolean mIsDimming = false;
+ private @Nullable InsetsSourceProvider mInsetProvider;
+
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@@ -594,7 +597,7 @@
if (mForceSeamlesslyRotate || requested) {
mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo());
mPendingSeamlessRotate.unrotate(transaction, this);
- mService.markForSeamlessRotation(this, true);
+ mWmService.markForSeamlessRotation(this, true);
}
}
@@ -603,7 +606,7 @@
mPendingSeamlessRotate.finish(this, timeout);
mFinishSeamlessRotateFrameNumber = getFrameNumber();
mPendingSeamlessRotate = null;
- mService.markForSeamlessRotation(this, false);
+ mWmService.markForSeamlessRotation(this, false);
}
}
@@ -647,8 +650,8 @@
mAttrs.copyFrom(a);
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
mViewVisibility = viewVisibility;
- mPolicy = mService.mPolicy;
- mContext = mService.mContext;
+ mPolicy = mWmService.mPolicy;
+ mContext = mWmService.mContext;
DeathRecipient deathRecipient = new DeathRecipient();
mSeq = seq;
mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
@@ -787,6 +790,18 @@
}
@Override
+ public Rect getDisplayedBounds() {
+ final Task task = getTask();
+ if (task != null) {
+ Rect bounds = task.getOverrideDisplayedBounds();
+ if (!bounds.isEmpty()) {
+ return bounds;
+ }
+ }
+ return super.getDisplayedBounds();
+ }
+
+ @Override
public void computeFrameLw() {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
@@ -802,16 +817,7 @@
final boolean windowsAreFloating = task != null && task.isFloating();
final DisplayContent dc = getDisplayContent();
- // If the task has temp inset bounds set, we have to make sure all its windows uses
- // the temp inset frame. Otherwise different display frames get applied to the main
- // window and the child window, making them misaligned.
- // Otherwise we need to clear the inset frame, to avoid using a stale frame after leaving
- // multi window mode.
- if (task != null && isInMultiWindowMode()) {
- task.getTempInsetBounds(mInsetFrame);
- } else {
- mInsetFrame.setEmpty();
- }
+ mInsetFrame.set(getBounds());
// Denotes the actual frame used to calculate the insets and to perform the layout. When
// resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
@@ -831,7 +837,7 @@
layoutXDiff = 0;
layoutYDiff = 0;
} else {
- getBounds(mWindowFrames.mContainingFrame);
+ mWindowFrames.mContainingFrame.set(getDisplayedBounds());
if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
// If the bounds are frozen, we still want to translate the window freely and only
@@ -842,7 +848,7 @@
mWindowFrames.mContainingFrame.bottom =
mWindowFrames.mContainingFrame.top + frozen.height();
}
- final WindowState imeWin = mService.mRoot.getCurrentInputMethodWindow();
+ final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
@@ -881,14 +887,9 @@
layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
- layoutXDiff =
- !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
- : 0;
- layoutYDiff =
- !mInsetFrame.isEmpty() ? mInsetFrame.top - mWindowFrames.mContainingFrame.top
- : 0;
- layoutContainingFrame =
- !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
+ layoutXDiff = mInsetFrame.left - mWindowFrames.mContainingFrame.left;
+ layoutYDiff = mInsetFrame.top - mWindowFrames.mContainingFrame.top;
+ layoutContainingFrame = mInsetFrame;
mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
mTmpRect);
@@ -1200,7 +1201,7 @@
}
updateLastInsetValues();
- mService.makeWindowFreezingScreenIfNeededLocked(this);
+ mWmService.makeWindowFreezingScreenIfNeededLocked(this);
// If the orientation is changing, or we're starting or ending a drag resizing action,
// then we need to hold off on unfreezing the display until this window has been
@@ -1217,9 +1218,9 @@
mAppToken.clearAllDrawn();
}
}
- if (!mService.mResizingWindows.contains(this)) {
+ if (!mWmService.mResizingWindows.contains(this)) {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
- mService.mResizingWindows.add(this);
+ mWmService.mResizingWindows.add(this);
}
} else if (getOrientationChanging()) {
if (isDrawnLw()) {
@@ -1227,7 +1228,7 @@
+ this + ", surfaceController " + winAnimator.mSurfaceController);
setOrientationChanging(false);
mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mService.mDisplayFreezeTime);
+ - mWmService.mDisplayFreezeTime);
}
}
}
@@ -1261,7 +1262,6 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- updateSurfaceSize(dc);
super.onDisplayChanged(dc);
// Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
if (dc != null) {
@@ -1601,7 +1601,7 @@
// 3. WS is currently visible
if (!runningAppAnimation && isVisibleNow) {
final AccessibilityController accessibilityController =
- mService.mAccessibilityController;
+ mWmService.mAccessibilityController;
final int winTransit = TRANSIT_EXIT;
mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
//TODO (multidisplay): Magnification is supported only for the default
@@ -1623,8 +1623,8 @@
if (isVisibleNow()) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
//TODO (multidisplay): Magnification is supported only for the default
- if (mService.mAccessibilityController != null && isDefaultDisplay()) {
- mService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
+ if (mWmService.mAccessibilityController != null && isDefaultDisplay()) {
+ mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
}
changed = true;
if (displayContent != null) {
@@ -1642,7 +1642,7 @@
@Override
void onResize() {
- final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
+ final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows;
if (mHasSurface && !isGoneForLayoutLw() && !resizingWindows.contains(this)) {
if (DEBUG_RESIZE) Slog.d(TAG, "onResize: Resizing " + this);
resizingWindows.add(this);
@@ -1666,8 +1666,8 @@
mLayoutNeeded = true;
setDisplayLayoutNeeded();
- if (!mService.mResizingWindows.contains(this)) {
- mService.mResizingWindows.add(this);
+ if (!mWmService.mResizingWindows.contains(this)) {
+ mWmService.mResizingWindows.add(this);
}
}
@@ -1699,9 +1699,9 @@
}
//TODO (multidisplay): Accessibility supported only for the default display.
- if (mService.mAccessibilityController != null
+ if (mWmService.mAccessibilityController != null
&& getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
try {
@@ -1815,7 +1815,7 @@
// we are doing this as part of processing a death note.)
}
- mService.postWindowRemoveCleanupLocked(this);
+ mWmService.postWindowRemoveCleanupLocked(this);
}
@Override
@@ -1856,7 +1856,7 @@
+ " mWillReplaceWindow=" + mWillReplaceWindow
+ " inPendingTransaction="
+ (mAppToken != null ? mAppToken.inPendingTransaction : false)
- + " mDisplayFrozen=" + mService.mDisplayFrozen
+ + " mDisplayFrozen=" + mWmService.mDisplayFrozen
+ " callers=" + Debug.getCallers(6));
// Visibility of the removed window. Will be used later to update orientation later on.
@@ -1891,7 +1891,7 @@
mAppDied = true;
setDisplayLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
// Set up a replacement input channel since the app is now dead.
// We need to catch tapping on the dead window to restart the app.
@@ -1910,11 +1910,12 @@
// mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
// any change from that is performed immediately.
setDisplayLayoutNeeded();
- mService.requestTraversal();
+ mWmService.requestTraversal();
}
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+ if (mWmService.mAccessibilityController != null
+ && displayId == DEFAULT_DISPLAY) {
+ mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
}
}
final boolean isAnimating = isAnimating()
@@ -1948,7 +1949,7 @@
displayContent.sendNewConfiguration();
}
}
- mService.updateFocusedWindowLocked(isFocused()
+ mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
@@ -1962,9 +1963,9 @@
setDisplayLayoutNeeded();
// Request a focus update as this window's input channel is already gone. Otherwise
// we could have no focused window in input manager.
- final boolean focusChanged = mService.updateFocusedWindowLocked(
+ final boolean focusChanged = mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
if (focusChanged) {
getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
}
@@ -2017,7 +2018,7 @@
private final class DeadWindowEventReceiver extends InputEventReceiver {
DeadWindowEventReceiver(InputChannel inputChannel) {
- super(inputChannel, mService.mH.getLooper());
+ super(inputChannel, mWmService.mH.getLooper());
}
@Override
public void onInputEvent(InputEvent event) {
@@ -2037,7 +2038,7 @@
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
- mInputWindowHandle.inputChannel = inputChannels[0];
+ mInputWindowHandle.token = mClient.asBinder();
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
@@ -2048,7 +2049,7 @@
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
- mService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
+ mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
}
void disposeInputChannel() {
@@ -2059,7 +2060,7 @@
// unregister server channel first otherwise it complains about broken channel
if (mInputChannel != null) {
- mService.mInputManager.unregisterInputChannel(mInputChannel);
+ mWmService.mInputManager.unregisterInputChannel(mInputChannel);
mInputChannel.dispose();
mInputChannel = null;
@@ -2068,7 +2069,7 @@
mClientChannel.dispose();
mClientChannel = null;
}
- mInputWindowHandle.inputChannel = null;
+ mInputWindowHandle.token = null;
}
/** Returns true if the replacement window was removed. */
@@ -2167,11 +2168,12 @@
mTmpRect.inset(-delta, -delta);
}
region.set(mTmpRect);
- cropRegionToStackBoundsIfNeeded(region);
+ region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
} else {
// Not modal or full screen modal
getTouchableRegion(region);
}
+
return flags;
}
@@ -2187,13 +2189,13 @@
if (isFocused()) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"setAnimationLocked: setting mFocusMayChange true");
- mService.mFocusMayChange = true;
+ mWmService.mFocusMayChange = true;
}
setDisplayLayoutNeeded();
// Window is no longer visible -- make sure if we were waiting
// for it to be displayed before enabling the display, that
// we allow the display to be enabled now.
- mService.enableScreenIfNeededLocked();
+ mWmService.enableScreenIfNeededLocked();
}
}
}
@@ -2210,8 +2212,8 @@
// We need to turn on screen regardless of visibility.
boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0;
boolean allowTheaterMode =
- mService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
- mService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
+ mWmService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
+ mWmService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
== 0;
boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
@@ -2261,8 +2263,8 @@
// For child windows we want to use the pid for the parent window in case the the child
// window was added from another process.
final int pid = getParentWindow() != null ? getParentWindow().mSession.mPid : mSession.mPid;
- mTempConfiguration.setTo(mService.mProcessConfigurations.get(
- pid, mService.mRoot.getConfiguration()));
+ mTempConfiguration.setTo(mWmService.mProcessConfigurations.get(
+ pid, mWmService.mRoot.getConfiguration()));
return mTempConfiguration;
}
@@ -2318,13 +2320,14 @@
public void binderDied() {
try {
boolean resetSplitScreenResizing = false;
- synchronized (mService.mGlobalLock) {
- final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
+ synchronized (mWmService.mGlobalLock) {
+ final WindowState win = mWmService
+ .windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
final DisplayContent dc = getDisplayContent();
if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) {
- mService.mTaskSnapshotController.onAppDied(win.mAppToken);
+ mWmService.mTaskSnapshotController.onAppDied(win.mAppToken);
}
win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
@@ -2348,7 +2351,7 @@
try {
// Note: this calls into ActivityManager, so we must *not* hold the window
// manager lock while calling this.
- mService.mActivityTaskManager.setSplitScreenResizing(false);
+ mWmService.mActivityTaskManager.setSplitScreenResizing(false);
} catch (RemoteException e) {
// Local call, shouldn't return RemoteException.
throw e.rethrowAsRuntimeException();
@@ -2392,11 +2395,11 @@
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mAppToken == null || mAppToken.windowsAreFocusable())
- && !canReceiveTouchInput();
+ && !cantReceiveTouchInput();
}
- /** @return true if this window desires touch events. */
- boolean canReceiveTouchInput() {
+ /** @return false if this window desires touch events. */
+ boolean cantReceiveTouchInput() {
return mAppToken != null && mAppToken.getTask() != null
&& mAppToken.getTask().mStack.shouldIgnoreInput();
}
@@ -2455,10 +2458,10 @@
mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true);
}
if (requestAnim) {
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
}
if ((mAttrs.flags & FLAG_NOT_FOCUSABLE) == 0) {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
}
return true;
}
@@ -2493,18 +2496,18 @@
// Window is no longer visible -- make sure if we were waiting
// for it to be displayed before enabling the display, that
// we allow the display to be enabled now.
- mService.enableScreenIfNeededLocked();
+ mWmService.enableScreenIfNeededLocked();
if (isFocused) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
"WindowState.hideLw: setting mFocusMayChange true");
- mService.mFocusMayChange = true;
+ mWmService.mFocusMayChange = true;
}
}
if (requestAnim) {
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
}
if (isFocused) {
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
}
return true;
}
@@ -2566,7 +2569,7 @@
// and add the window only if the permission was granted. Therefore, if
// the mode is MODE_DEFAULT we want the op to succeed as the window is
// shown.
- final int mode = mService.mAppOps.startOpNoThrow(mAppOp,
+ final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp,
getOwningUid(), getOwningPackage(), true);
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
setAppOpVisibilityLw(false);
@@ -2575,7 +2578,7 @@
void resetAppOpsState() {
if (mAppOp != OP_NONE && mAppOpVisibility) {
- mService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+ mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
}
}
@@ -2588,13 +2591,13 @@
if (mAppOpVisibility) {
// There is a race between the check and the finish calls but this is fine
// as this would mean we will get another change callback and will reconcile.
- int mode = mService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
+ int mode = mWmService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
- mService.mAppOps.finishOp(mAppOp, uid, packageName);
+ mWmService.mAppOps.finishOp(mAppOp, uid, packageName);
setAppOpVisibilityLw(false);
}
} else {
- final int mode = mService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
+ final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
setAppOpVisibilityLw(true);
}
@@ -2615,7 +2618,7 @@
// in wake lock statistics. So in particular, we don't want to include the
// window's hash code as in toString().
final CharSequence tag = getWindowTag();
- mDrawLock = mService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag);
+ mDrawLock = mWmService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag);
mDrawLock.setReferenceCounted(false);
mDrawLock.setWorkSource(new WorkSource(mOwnerUid, mAttrs.packageName));
}
@@ -2700,10 +2703,10 @@
mAppFreezing = false;
if (mHasSurface && !getOrientationChanging()
- && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
+ && mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
setOrientationChanging(true);
- mService.mRoot.mOrientationChangeComplete = false;
+ mWmService.mRoot.mOrientationChangeComplete = false;
}
mLastFreezeDuration = 0;
setDisplayLayoutNeeded();
@@ -2793,7 +2796,7 @@
}
return win.mShowToOwnerOnly
- && !mService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
+ && !mWmService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
}
private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
@@ -2803,25 +2806,32 @@
}
void getTouchableRegion(Region outRegion) {
+ if (inPinnedWindowingMode() && !isFocused()) {
+ outRegion.setEmpty();
+ return;
+ }
+
final Rect frame = mWindowFrames.mFrame;
switch (mTouchableInsets) {
default:
case TOUCHABLE_INSETS_FRAME:
outRegion.set(frame);
+ outRegion.translate(-frame.left, -frame.top);
break;
case TOUCHABLE_INSETS_CONTENT:
applyInsets(outRegion, frame, mGivenContentInsets);
+ outRegion.translate(-frame.left, -frame.top);
break;
case TOUCHABLE_INSETS_VISIBLE:
applyInsets(outRegion, frame, mGivenVisibleInsets);
+ outRegion.translate(-frame.left, -frame.top);
break;
case TOUCHABLE_INSETS_REGION: {
outRegion.set(mGivenTouchableRegion);
- outRegion.translate(frame.left, frame.top);
break;
}
}
- cropRegionToStackBoundsIfNeeded(outRegion);
+ outRegion.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
}
private void cropRegionToStackBoundsIfNeeded(Region region) {
@@ -2885,7 +2895,7 @@
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mWindowFrames.mCompatFrame);
final MergedConfiguration mergedConfiguration =
- new MergedConfiguration(mService.mRoot.getConfiguration(),
+ new MergedConfiguration(mWmService.mRoot.getConfiguration(),
getMergedOverrideConfiguration());
setLastReportedMergedConfiguration(mergedConfiguration);
@@ -2906,7 +2916,7 @@
if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& mClient instanceof IWindow.Stub) {
// To prevent deadlock simulate one-way call if win.mClient is a local object.
- mService.mH.post(new Runnable() {
+ mWmService.mH.post(new Runnable() {
@Override
public void run() {
try {
@@ -2925,8 +2935,8 @@
}
//TODO (multidisplay): Accessibility supported only for the default display.
- if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
mWindowFrames.resetInsetsChanged();
@@ -2935,16 +2945,39 @@
} catch (RemoteException e) {
setOrientationChanging(false);
mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mService.mDisplayFreezeTime);
+ - mWmService.mDisplayFreezeTime);
// We are assuming the hosting process is dead or in a zombie state.
Slog.w(TAG, "Failed to report 'resized' to the client of " + this
+ ", removing this window.");
- mService.mPendingRemove.add(this);
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mPendingRemove.add(this);
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ /**
+ * Called when the insets state changed.
+ */
+ void notifyInsetsChanged() {
+ try {
+ mClient.insetsChanged(
+ getDisplayContent().getInsetsStateController().getInsetsForDispatch(this));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver inset state change", e);
+ }
+ }
+
+ void notifyInsetsControlChanged() {
+ final InsetsStateController stateController =
+ getDisplayContent().getInsetsStateController();
+ try {
+ mClient.insetsControlChanged(stateController.getInsetsForDispatch(this),
+ stateController.getControlsForDispatch(this));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver inset state change", e);
+ }
+ }
+
Rect getBackdropFrame(Rect frame) {
// When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
// start even if we haven't received the relayout window, so that the client requests
@@ -2989,7 +3022,7 @@
}
public void registerFocusObserver(IWindowFocusObserver observer) {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (mFocusCallbacks == null) {
mFocusCallbacks = new RemoteCallbackList<IWindowFocusObserver>();
}
@@ -2998,7 +3031,7 @@
}
public void unregisterFocusObserver(IWindowFocusObserver observer) {
- synchronized (mService.mGlobalLock) {
+ synchronized (mWmService.mGlobalLock) {
if (mFocusCallbacks != null) {
mFocusCallbacks.unregister(observer);
}
@@ -3071,7 +3104,7 @@
@Override
void setWaitingForDrawnIfResizingChanged() {
if (isDragResizeChanged()) {
- mService.mWaitingForDrawn.add(this);
+ mWmService.mWaitingForDrawn.add(this);
}
super.setWaitingForDrawnIfResizingChanged();
}
@@ -3560,7 +3593,7 @@
if (dc != null && (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
dc.setLayoutNeeded();
- mService.mWindowPlacerLocked.requestTraversal();
+ mWmService.mWindowPlacerLocked.requestTraversal();
}
for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -3658,7 +3691,7 @@
logPerformShow("Showing ");
- mService.enableScreenIfNeededLocked();
+ mWmService.enableScreenIfNeededLocked();
mWinAnimator.applyEnterAnimationLocked();
// Force the show in the next prepareSurfaceLocked() call.
@@ -3666,7 +3699,7 @@
if (DEBUG_ANIM) Slog.v(TAG,
"performShowLocked: mDrawState=HAS_DRAWN in " + this);
mWinAnimator.mDrawState = HAS_DRAWN;
- mService.scheduleAnimationLocked();
+ mWmService.scheduleAnimationLocked();
if (mHidden) {
mHidden = false;
@@ -3945,7 +3978,7 @@
if (mWinAnimator.mEnteringAnimation) {
mWinAnimator.mEnteringAnimation = false;
- mService.requestTraversal();
+ mWmService.requestTraversal();
// System windows don't have an activity and an app token as a result, but need a way
// to be informed about their entrance animation end.
if (mAppToken == null) {
@@ -3961,8 +3994,8 @@
}
//TODO (multidisplay): Accessibility is supported only for the default display.
- if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
if (!isSelfOrAncestorWindowAnimatingExit()) {
@@ -3988,10 +4021,10 @@
mAppToken.destroySurfaces();
} else {
if (hasSurface) {
- mService.mDestroySurface.add(this);
+ mWmService.mDestroySurface.add(this);
}
if (mRemoveOnExit) {
- mService.mPendingRemove.add(this);
+ mWmService.mPendingRemove.add(this);
mRemoveOnExit = false;
}
}
@@ -4022,7 +4055,7 @@
}
if (mDestroying) {
mDestroying = false;
- mService.mDestroySurface.remove(this);
+ mWmService.mDestroySurface.remove(this);
didSomething = true;
}
}
@@ -4051,7 +4084,7 @@
if (displayContent != null) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
+ mWmService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
displayContent.pendingLayoutChanges);
}
}
@@ -4266,7 +4299,7 @@
}
if (mDestroying) {
mDestroying = false;
- mService.mDestroySurface.remove(this);
+ mWmService.mDestroySurface.remove(this);
}
if (oldVisibility == View.GONE) {
mWinAnimator.mEnterAnimationPending = true;
@@ -4334,10 +4367,10 @@
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
+ anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */),
- mService.mSurfaceAnimationRunner);
+ mWmService.mSurfaceAnimationRunner);
startAnimation(mPendingTransaction, adapter);
commitPendingTransaction();
}
@@ -4351,7 +4384,7 @@
transformFrameToSurfacePosition(left, top, newPosition);
final AnimationAdapter adapter = new LocalAnimationAdapter(
new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
- mService.mSurfaceAnimationRunner);
+ mWmService.mSurfaceAnimationRunner);
startAnimation(getPendingTransaction(), adapter);
}
@@ -4445,7 +4478,7 @@
public boolean isFocused() {
final WindowState outer = mOuter.get();
if (outer != null) {
- synchronized (outer.mService.mGlobalLock) {
+ synchronized (outer.mWmService.mGlobalLock) {
return outer.isFocused();
}
}
@@ -4576,7 +4609,7 @@
outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
-parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
} else if (parentWindowContainer != null) {
- final Rect parentBounds = parentWindowContainer.getBounds();
+ final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
@@ -4766,6 +4799,14 @@
mWindowFrames.setContentChanged(false);
}
+ void setInsetProvider(InsetsSourceProvider insetProvider) {
+ mInsetProvider = insetProvider;
+ }
+
+ InsetsSourceProvider getInsetProvider() {
+ return mInsetProvider;
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
@@ -4777,7 +4818,7 @@
final Animation anim = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.window_move_from_decor);
mDuration = (long)
- (anim.computeDurationHint() * mService.getWindowAnimationScaleLocked());
+ (anim.computeDurationHint() * mWmService.getWindowAnimationScaleLocked());
mInterpolator = anim.getInterpolator();
mFrom.set(fromX, fromY);
mTo.set(toX, toY);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e090cc5..fb5c556 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -234,7 +234,7 @@
private final Point mTmpPos = new Point();
WindowStateAnimator(final WindowState win) {
- final WindowManagerService service = win.mService;
+ final WindowManagerService service = win.mWmService;
mService = service;
mAnimator = service.mAnimator;
@@ -861,7 +861,7 @@
// to find the surface size changed underneath it.
final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
if (relayout) {
- mSurfaceResized = mSurfaceController.setSizeInTransaction(
+ mSurfaceResized = mSurfaceController.setBufferSizeInTransaction(
mTmpSize.width(), mTmpSize.height(), recoveringMemory);
} else {
mSurfaceResized = false;
@@ -1020,7 +1020,7 @@
mTmpPos.x = 0;
mTmpPos.y = 0;
if (stack != null) {
- stack.getRelativePosition(mTmpPos);
+ stack.getRelativeDisplayedPosition(mTmpPos);
}
xOffset = -mTmpPos.x;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 6821e94..ce627e2 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -19,34 +19,28 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
-import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.IBinder;
import android.os.Debug;
+import android.os.IBinder;
import android.os.Trace;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowContentFrameStats;
-import android.view.Surface.OutOfResourcesException;
-import android.util.Slog;
-
-import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
class WindowSurfaceController {
static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfaceController" : TAG_WM;
@@ -106,7 +100,7 @@
final SurfaceControl.Builder b = win.makeSurface()
.setParent(win.getSurfaceControl())
.setName(name)
- .setSize(w, h)
+ .setBufferSize(w, h)
.setFormat(format)
.setFlags(flags)
.setMetadata(windowType, ownerUid);
@@ -303,7 +297,7 @@
}
}
- boolean setSizeInTransaction(int width, int height, boolean recoveringMemory) {
+ boolean setBufferSizeInTransaction(int width, int height, boolean recoveringMemory) {
final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
if (surfaceResized) {
mSurfaceW = width;
@@ -312,7 +306,7 @@
try {
if (SHOW_TRANSACTIONS) logSurface(
"SIZE " + width + "x" + height, null);
- mSurfaceControl.setSize(width, height);
+ mSurfaceControl.setBufferSize(width, height);
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
// as running out of memory), don't take down the
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 0cf79b6..9c13782 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -172,8 +172,8 @@
setHidden(true);
if (changed) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
}
if (delayed) {
@@ -202,7 +202,7 @@
if (!mChildren.contains(win)) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
addChild(win, mWindowComparator);
- mService.mWindowsChanged = true;
+ mWmService.mWindowsChanged = true;
// TODO: Should we also be setting layout needed here and other places?
}
}
@@ -265,7 +265,6 @@
// to another display before the window behind
// it is ready.
- updateSurfaceSize(dc);
super.onDisplayChanged(dc);
}
@@ -327,9 +326,9 @@
* system bars, or in other words extend outside of the "Decor Frame"
*/
boolean canLayerAboveSystemBars() {
- int layer = mService.mPolicy.getWindowLayerFromTypeLw(windowType,
+ int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
mOwnerCanManageAppTokens);
- int navLayer = mService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
+ int navLayer = mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
mOwnerCanManageAppTokens);
return mOwnerCanManageAppTokens && (layer > navLayer);
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index bf83ac13..b85489a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -2,8 +2,6 @@
name: "libservices.core",
defaults: ["libservices.core-libs"],
- cpp_std: "c++17",
-
cflags: [
"-Wall",
"-Werror",
@@ -108,6 +106,7 @@
"android.hardware.contexthub@1.0",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
+ "android.hardware.gnss@2.0",
"android.hardware.ir@1.0",
"android.hardware.light@2.0",
"android.hardware.power@1.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index ee8a08b..43d2dcf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -32,6 +32,7 @@
#include <atomic>
#include <cinttypes>
#include <limits.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
@@ -71,6 +72,7 @@
#define INDENT " "
+using android::base::ParseUint;
using android::base::StringPrintf;
namespace android {
@@ -81,11 +83,13 @@
static const float POINTER_SPEED_EXPONENT = 1.0f / 4;
static struct {
+ jclass clazz;
jmethodID notifyConfigurationChanged;
jmethodID notifyInputDevicesChanged;
jmethodID notifySwitch;
jmethodID notifyInputChannelBroken;
jmethodID notifyANR;
+ jmethodID notifyFocusChanged;
jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptMotionBeforeQueueingNonInteractive;
@@ -94,6 +98,7 @@
jmethodID checkInjectEventsPermission;
jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames;
+ jmethodID getInputPortAssociations;
jmethodID getKeyRepeatTimeout;
jmethodID getKeyRepeatDelay;
jmethodID getHoverTapTimeout;
@@ -147,15 +152,6 @@
return value ? "true" : "false";
}
-static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
- const sp<InputApplicationHandle>& inputApplicationHandle) {
- if (inputApplicationHandle == nullptr) {
- return nullptr;
- }
- return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())->
- getInputApplicationHandleObjLocalRef(env);
-}
-
static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextObj, int32_t style,
PointerIcon* outPointerIcon, SpriteIcon* outSpriteIcon) {
status_t status = android_view_PointerIcon_loadSystemIcon(env,
@@ -191,6 +187,13 @@
WM_ACTION_PASS_TO_USER = 1,
};
+static std::string getStringElementFromJavaArray(JNIEnv* env, jobjectArray array, jsize index) {
+ jstring item = jstring(env->GetObjectArrayElement(array, index));
+ ScopedUtfChars chars(env, item);
+ std::string result(chars.c_str());
+ return result;
+}
+
// --- NativeInputManager ---
@@ -249,6 +252,7 @@
const sp<IBinder>& token,
const std::string& reason);
virtual void notifyInputChannelBroken(const sp<IBinder>& token);
+ virtual void notifyFocusChanged(const sp<IBinder>& token);
virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
@@ -342,6 +346,8 @@
mInteractive = true;
mInputManager = new InputManager(this, this);
+ defaultServiceManager()->addService(String16("inputflinger"),
+ mInputManager, false);
}
NativeInputManager::~NativeInputManager() {
@@ -457,20 +463,44 @@
}
outConfig->excludedDeviceNames.clear();
- jobjectArray excludedDeviceNames = jobjectArray(env->CallObjectMethod(mServiceObj,
- gServiceClassInfo.getExcludedDeviceNames));
+ jobjectArray excludedDeviceNames = jobjectArray(env->CallStaticObjectMethod(
+ gServiceClassInfo.clazz, gServiceClassInfo.getExcludedDeviceNames));
if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) {
jsize length = env->GetArrayLength(excludedDeviceNames);
for (jsize i = 0; i < length; i++) {
- jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i));
- const char* deviceNameChars = env->GetStringUTFChars(item, nullptr);
- outConfig->excludedDeviceNames.push_back(deviceNameChars);
- env->ReleaseStringUTFChars(item, deviceNameChars);
- env->DeleteLocalRef(item);
+ std::string deviceName = getStringElementFromJavaArray(env, excludedDeviceNames, i);
+ outConfig->excludedDeviceNames.push_back(deviceName);
}
env->DeleteLocalRef(excludedDeviceNames);
}
+ // Associations between input ports and display ports
+ // The java method packs the information in the following manner:
+ // Original data: [{'inputPort1': '1'}, {'inputPort2': '2'}]
+ // Received data: ['inputPort1', '1', 'inputPort2', '2']
+ // So we unpack accordingly here.
+ outConfig->portAssociations.clear();
+ jobjectArray portAssociations = jobjectArray(env->CallStaticObjectMethod(
+ gServiceClassInfo.clazz, gServiceClassInfo.getInputPortAssociations));
+ if (!checkAndClearExceptionFromCallback(env, "getInputPortAssociations") && portAssociations) {
+ jsize length = env->GetArrayLength(portAssociations);
+ for (jsize i = 0; i < length / 2; i++) {
+ std::string inputPort = getStringElementFromJavaArray(env, portAssociations, 2 * i);
+ std::string displayPortStr =
+ getStringElementFromJavaArray(env, portAssociations, 2 * i + 1);
+ uint8_t displayPort;
+ // Should already have been validated earlier, but do it here for safety.
+ bool success = ParseUint(displayPortStr, &displayPort);
+ if (!success) {
+ ALOGE("Could not parse entry in port configuration file, received: %s",
+ displayPortStr.c_str());
+ continue;
+ }
+ outConfig->portAssociations.insert({inputPort, displayPort});
+ }
+ env->DeleteLocalRef(portAssociations);
+ }
+
jint hoverTapTimeout = env->CallIntMethod(mServiceObj,
gServiceClassInfo.getHoverTapTimeout);
if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) {
@@ -656,13 +686,11 @@
JNIEnv* env = jniEnv();
- jobject inputApplicationHandleObj =
- getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
jobject tokenObj = javaObjectForIBinder(env, token);
jstring reasonObj = env->NewStringUTF(reason.c_str());
jlong newTimeout = env->CallLongMethod(mServiceObj,
- gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
+ gServiceClassInfo.notifyANR, tokenObj,
reasonObj);
if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
newTimeout = 0; // abort dispatch
@@ -671,7 +699,6 @@
}
env->DeleteLocalRef(reasonObj);
- env->DeleteLocalRef(inputApplicationHandleObj);
return newTimeout;
}
@@ -691,6 +718,22 @@
}
}
+void NativeInputManager::notifyFocusChanged(const sp<IBinder>& token) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ ALOGD("notifyFocusChanged");
+#endif
+ ATRACE_CALL();
+
+ JNIEnv* env = jniEnv();
+
+ jobject tokenObj = javaObjectForIBinder(env, token);
+ if (tokenObj) {
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyFocusChanged,
+ tokenObj);
+ checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
+ }
+}
+
void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -1689,6 +1732,10 @@
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! (var), "Unable to find method " methodName);
+#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! (var), "Unable to find static method " methodName);
+
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
@@ -1703,6 +1750,7 @@
jclass clazz;
FIND_CLASS(clazz, "com/android/server/input/InputManagerService");
+ gServiceClassInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz,
"notifyConfigurationChanged", "(J)V");
@@ -1715,10 +1763,13 @@
GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz,
"notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
+
+ GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
+ "notifyFocusChanged", "(Landroid/os/IBinder;)V");
GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
"notifyANR",
- "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
+ "(Landroid/os/IBinder;Ljava/lang/String;)J");
GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
"filterInputEvent", "(Landroid/view/InputEvent;I)Z");
@@ -1743,9 +1794,12 @@
GET_METHOD_ID(gServiceClassInfo.getVirtualKeyQuietTimeMillis, clazz,
"getVirtualKeyQuietTimeMillis", "()I");
- GET_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz,
+ GET_STATIC_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
+ GET_STATIC_METHOD_ID(gServiceClassInfo.getInputPortAssociations, clazz,
+ "getInputPortAssociations", "()[Ljava/lang/String;");
+
GET_METHOD_ID(gServiceClassInfo.getKeyRepeatTimeout, clazz,
"getKeyRepeatTimeout", "()I");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 9216005..4d0556c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -20,9 +20,11 @@
#include <android/hardware/gnss/1.0/IGnss.h>
#include <android/hardware/gnss/1.1/IGnss.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
#include <android/hardware/gnss/1.0/IGnssMeasurement.h>
#include <android/hardware/gnss/1.1/IGnssMeasurement.h>
+#include <android/hardware/gnss/2.0/IGnssMeasurement.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include "hardware_legacy/power.h"
@@ -110,13 +112,15 @@
using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
+using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
+using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
using IGnssMeasurementCallback_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
-
+using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
@@ -135,6 +139,7 @@
sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
sp<IGnss_V1_0> gnssHal = nullptr;
sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
+sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil> agnssRilIface = nullptr;
sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
@@ -146,6 +151,7 @@
sp<IGnssNi> gnssNiIface = nullptr;
sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
+sp<IGnssMeasurement_V2_0> gnssMeasurementIface_V2_0 = nullptr;
sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr;
#define WAKE_LOCK_NAME "GPS"
@@ -744,7 +750,9 @@
* GnssMeasurementCallback implements the callback methods required for the
* GnssMeasurement interface.
*/
-struct GnssMeasurementCallback : public IGnssMeasurementCallback_V1_1 {
+struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_0 {
+ Return<void> gnssMeasurementCb_2_0(const IGnssMeasurementCallback_V2_0::GnssData& data)
+ override;
Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_V1_1::GnssData& data) override;
Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override;
private:
@@ -761,6 +769,11 @@
void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray);
};
+Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0(
+ const IGnssMeasurementCallback_V2_0::GnssData& data) {
+ // TODO(b/119571122): implement gnssMeasurementCb_2_0
+ return Void();
+}
Return<void> GnssMeasurementCallback::gnssMeasurementCb(
const IGnssMeasurementCallback_V1_1::GnssData& data) {
@@ -1126,13 +1139,22 @@
}
static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
- gnssHal_V1_1 = IGnss_V1_1::getService();
- if (gnssHal_V1_1 == nullptr) {
- ALOGD("gnssHal 1.1 was null, trying 1.0");
- gnssHal = IGnss_V1_0::getService();
- } else {
- gnssHal = gnssHal_V1_1;
+ gnssHal_V2_0 = IGnss_V2_0::getService();
+ if (gnssHal_V2_0 != nullptr) {
+ gnssHal = gnssHal_V2_0;
+ gnssHal_V1_1 = gnssHal_V2_0;
+ return;
}
+
+ ALOGD("gnssHal 2.0 was null, trying 1.1");
+ gnssHal_V1_1 = IGnss_V1_1::getService();
+ if (gnssHal_V1_1 != nullptr) {
+ gnssHal = gnssHal_V1_1;
+ return;
+ }
+
+ ALOGD("gnssHal 1.1 was null, trying 1.0");
+ gnssHal = IGnss_V1_0::getService();
}
static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz) {
@@ -1187,110 +1209,120 @@
LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
}
- if (gnssHal != nullptr) {
- gnssHalDeathRecipient = new GnssDeathRecipient();
- hardware::Return<bool> linked = gnssHal->linkToDeath(
- gnssHalDeathRecipient, /*cookie*/ 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to GnssHAL death: %s",
- linked.description().c_str());
- } else if (!linked) {
- ALOGW("Unable to link to GnssHal death notifications");
- } else {
- ALOGD("Link to death notification successful");
- }
+ if (gnssHal == nullptr) {
+ ALOGE("Unable to get GPS service\n");
+ return;
+ }
- auto gnssXtra = gnssHal->getExtensionXtra();
- if (!gnssXtra.isOk()) {
- ALOGD("Unable to get a handle to Xtra");
- } else {
- gnssXtraIface = gnssXtra;
- }
+ gnssHalDeathRecipient = new GnssDeathRecipient();
+ hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to GnssHAL death: %s",
+ linked.description().c_str());
+ } else if (!linked) {
+ ALOGW("Unable to link to GnssHal death notifications");
+ } else {
+ ALOGD("Link to death notification successful");
+ }
- auto gnssRil = gnssHal->getExtensionAGnssRil();
- if (!gnssRil.isOk()) {
- ALOGD("Unable to get a handle to AGnssRil");
- } else {
- agnssRilIface = gnssRil;
- }
+ auto gnssXtra = gnssHal->getExtensionXtra();
+ if (!gnssXtra.isOk()) {
+ ALOGD("Unable to get a handle to Xtra");
+ } else {
+ gnssXtraIface = gnssXtra;
+ }
- auto gnssAgnss = gnssHal->getExtensionAGnss();
- if (!gnssAgnss.isOk()) {
- ALOGD("Unable to get a handle to AGnss");
- } else {
- agnssIface = gnssAgnss;
- }
+ auto gnssRil = gnssHal->getExtensionAGnssRil();
+ if (!gnssRil.isOk()) {
+ ALOGD("Unable to get a handle to AGnssRil");
+ } else {
+ agnssRilIface = gnssRil;
+ }
- auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
- if (!gnssNavigationMessage.isOk()) {
- ALOGD("Unable to get a handle to GnssNavigationMessage");
- } else {
- gnssNavigationMessageIface = gnssNavigationMessage;
- }
+ auto gnssAgnss = gnssHal->getExtensionAGnss();
+ if (!gnssAgnss.isOk()) {
+ ALOGD("Unable to get a handle to AGnss");
+ } else {
+ agnssIface = gnssAgnss;
+ }
- if (gnssHal_V1_1 != nullptr) {
- auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
- if (!gnssMeasurement.isOk()) {
- ALOGD("Unable to get a handle to GnssMeasurement");
- } else {
- gnssMeasurementIface_V1_1 = gnssMeasurement;
- gnssMeasurementIface = gnssMeasurementIface_V1_1;
- }
- } else {
- auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement();
- if (!gnssMeasurement_V1_0.isOk()) {
- ALOGD("Unable to get a handle to GnssMeasurement");
- } else {
- gnssMeasurementIface = gnssMeasurement_V1_0;
- }
- }
+ auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
+ if (!gnssNavigationMessage.isOk()) {
+ ALOGD("Unable to get a handle to GnssNavigationMessage");
+ } else {
+ gnssNavigationMessageIface = gnssNavigationMessage;
+ }
- auto gnssDebug = gnssHal->getExtensionGnssDebug();
- if (!gnssDebug.isOk()) {
- ALOGD("Unable to get a handle to GnssDebug");
+ if (gnssHal_V2_0 != nullptr) {
+ // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0
+ auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
+ if (!gnssMeasurement.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurement_V2_0");
} else {
- gnssDebugIface = gnssDebug;
+ gnssMeasurementIface_V2_0 = gnssMeasurement;
+ gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0;
+ gnssMeasurementIface = gnssMeasurementIface_V2_0;
}
+ } else if (gnssHal_V1_1 != nullptr) {
+ auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
+ if (!gnssMeasurement.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurement_V1_1");
+ } else {
+ gnssMeasurementIface_V1_1 = gnssMeasurement;
+ gnssMeasurementIface = gnssMeasurementIface_V1_1;
+ }
+ } else {
+ auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement();
+ if (!gnssMeasurement_V1_0.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurement");
+ } else {
+ gnssMeasurementIface = gnssMeasurement_V1_0;
+ }
+ }
- auto gnssNi = gnssHal->getExtensionGnssNi();
- if (!gnssNi.isOk()) {
- ALOGD("Unable to get a handle to GnssNi");
- } else {
- gnssNiIface = gnssNi;
- }
+ auto gnssDebug = gnssHal->getExtensionGnssDebug();
+ if (!gnssDebug.isOk()) {
+ ALOGD("Unable to get a handle to GnssDebug");
+ } else {
+ gnssDebugIface = gnssDebug;
+ }
- if (gnssHal_V1_1 != nullptr) {
- auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
- if (!gnssConfiguration.isOk()) {
- ALOGD("Unable to get a handle to GnssConfiguration");
- } else {
- gnssConfigurationIface_V1_1 = gnssConfiguration;
- gnssConfigurationIface = gnssConfigurationIface_V1_1;
- }
- } else {
- auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
- if (!gnssConfiguration_V1_0.isOk()) {
- ALOGD("Unable to get a handle to GnssConfiguration");
- } else {
- gnssConfigurationIface = gnssConfiguration_V1_0;
- }
- }
+ auto gnssNi = gnssHal->getExtensionGnssNi();
+ if (!gnssNi.isOk()) {
+ ALOGD("Unable to get a handle to GnssNi");
+ } else {
+ gnssNiIface = gnssNi;
+ }
- auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
- if (!gnssGeofencing.isOk()) {
- ALOGD("Unable to get a handle to GnssGeofencing");
+ if (gnssHal_V1_1 != nullptr) {
+ auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
+ if (!gnssConfiguration.isOk()) {
+ ALOGD("Unable to get a handle to GnssConfiguration");
} else {
- gnssGeofencingIface = gnssGeofencing;
- }
-
- auto gnssBatching = gnssHal->getExtensionGnssBatching();
- if (!gnssBatching.isOk()) {
- ALOGD("Unable to get a handle to gnssBatching");
- } else {
- gnssBatchingIface = gnssBatching;
+ gnssConfigurationIface_V1_1 = gnssConfiguration;
+ gnssConfigurationIface = gnssConfigurationIface_V1_1;
}
} else {
- ALOGE("Unable to get GPS service\n");
+ auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
+ if (!gnssConfiguration_V1_0.isOk()) {
+ ALOGD("Unable to get a handle to GnssConfiguration");
+ } else {
+ gnssConfigurationIface = gnssConfiguration_V1_0;
+ }
+ }
+
+ auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
+ if (!gnssGeofencing.isOk()) {
+ ALOGD("Unable to get a handle to GnssGeofencing");
+ } else {
+ gnssGeofencingIface = gnssGeofencing;
+ }
+
+ auto gnssBatching = gnssHal->getExtensionGnssBatching();
+ if (!gnssBatching.isOk()) {
+ ALOGD("Unable to get a handle to gnssBatching");
+ } else {
+ gnssBatchingIface = gnssBatching;
}
}
@@ -1820,10 +1852,11 @@
sp<GnssMeasurementCallback> cbIface = new GnssMeasurementCallback();
IGnssMeasurement_V1_0::GnssMeasurementStatus result =
- IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;;
- if (gnssMeasurementIface_V1_1 != nullptr) {
- result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface,
- enableFullTracking);
+ IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;
+ if (gnssMeasurementIface_V2_0 != nullptr) {
+ result = gnssMeasurementIface_V2_0->setCallback_2_0(cbIface, enableFullTracking);
+ } else if (gnssMeasurementIface_V1_1 != nullptr) {
+ result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface, enableFullTracking);
} else {
if (enableFullTracking == JNI_TRUE) {
// full tracking mode not supported in 1.0 HAL
@@ -1837,7 +1870,7 @@
static_cast<int32_t>(result));
return JNI_FALSE;
} else {
- ALOGD("gnss measurement infc has been enabled");
+ ALOGD("gnss measurement infc has been enabled");
}
return JNI_TRUE;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 6462d16..65d3245 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -23,6 +23,9 @@
import com.android.server.SystemService;
+import java.util.Collections;
+import java.util.List;
+
/**
* Defines the required interface for IDevicePolicyManager implemenation.
*
@@ -97,4 +100,29 @@
@Override
public void installUpdateFromFile(ComponentName admin,
ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {}
+
+ @Override
+ public void addCrossProfileCalendarPackage(ComponentName admin, String packageName) {
+ }
+
+ @Override
+ public boolean removeCrossProfileCalendarPackage(ComponentName admin, String packageName) {
+ return false;
+ }
+
+ @Override
+ public List<String> getCrossProfileCalendarPackages(ComponentName admin) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isPackageAllowedToAccessCalendarForUser(String packageName,
+ int userHandle) {
+ return false;
+ }
+
+ @Override
+ public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) {
+ return Collections.emptyList();
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7751b4a..f68f4d7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -74,8 +74,10 @@
.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+ .ADMIN_TYPE_DEVICE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+ .ADMIN_TYPE_PROFILE_OWNER;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -908,8 +910,12 @@
private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
- private static final String TAG_METERED_DATA_DISABLED_PACKAGES
- = "metered_data_disabled_packages";
+ private static final String TAG_METERED_DATA_DISABLED_PACKAGES =
+ "metered_data_disabled_packages";
+ private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES =
+ "cross-profile-calendar-packages";
+ private static final String TAG_PACKAGE = "package";
+
DeviceAdminInfo info;
@@ -1030,6 +1036,9 @@
String startUserSessionMessage = null;
String endUserSessionMessage = null;
+ // The whitelist of packages that can access cross profile calendar APIs.
+ final Set<String> mCrossProfileCalendarPackages = new ArraySet<>();
+
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
isParent = parent;
@@ -1299,6 +1308,12 @@
out.text(endUserSessionMessage);
out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
}
+ if (!mCrossProfileCalendarPackages.isEmpty()) {
+ out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+ writeAttributeValuesToXml(
+ out, TAG_PACKAGE, mCrossProfileCalendarPackages);
+ out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+ }
}
void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1491,6 +1506,9 @@
} else {
Log.w(LOG_TAG, "Missing text when loading end session message");
}
+ } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
+ readAttributeValues(
+ parser, TAG_PACKAGE, mCrossProfileCalendarPackages);
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1706,6 +1724,8 @@
pw.print(prefix); pw.println("parentAdmin:");
parentAdmin.dump(prefix + " ", pw);
}
+ pw.print(prefix); pw.print("mCrossProfileCalendarPackages=");
+ pw.println(mCrossProfileCalendarPackages);
}
}
@@ -13339,9 +13359,92 @@
}
}
-
private boolean isDeviceAB() {
return "true".equalsIgnoreCase(android.os.SystemProperties
.get(AB_DEVICE_KEY, ""));
}
+
+ @Override
+ public void addCrossProfileCalendarPackage(ComponentName who, String packageName) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (admin.mCrossProfileCalendarPackages.add(packageName)) {
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+ }
+ }
+
+ @Override
+ public boolean removeCrossProfileCalendarPackage(ComponentName who, String packageName) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+ boolean isRemoved = false;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ isRemoved = admin.mCrossProfileCalendarPackages.remove(packageName);
+ if (isRemoved) {
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+ }
+ return isRemoved;
+ }
+
+ @Override
+ public List<String> getCrossProfileCalendarPackages(ComponentName who) {
+ if (!mHasFeature) {
+ return Collections.emptyList();
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+ }
+ }
+
+ @Override
+ public boolean isPackageAllowedToAccessCalendarForUser(String packageName,
+ int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+ enforceCrossUsersPermission(userHandle);
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+ if (admin != null) {
+ return admin.mCrossProfileCalendarPackages.contains(packageName);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public List<String> getCrossProfileCalendarPackagesForUser(int userHandle) {
+ if (!mHasFeature) {
+ return Collections.emptyList();
+ }
+ enforceCrossUsersPermission(userHandle);
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+ if (admin != null) {
+ return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+ }
+ }
+ return Collections.emptyList();
+ }
}
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 08fbf55..14912c4 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -19,9 +19,9 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
-import android.service.intelligence.IntelligenceService;
import android.service.intelligence.InteractionContext;
import android.service.intelligence.InteractionSessionId;
+import android.service.intelligence.SmartSuggestionsService;
import android.service.intelligence.SnapshotData;
import android.util.Slog;
import android.view.autofill.AutofillId;
@@ -59,7 +59,7 @@
mService = service;
mId = Preconditions.checkNotNull(sessionId);
mRemoteService = new RemoteIntelligenceService(context,
- IntelligenceService.SERVICE_INTERFACE, serviceComponentName, userId, this,
+ SmartSuggestionsService.SERVICE_INTERFACE, serviceComponentName, userId, this,
bindInstantServiceAllowed, verbose);
mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
}
@@ -72,7 +72,7 @@
}
/**
- * Notifies the {@link IntelligenceService} that the service started.
+ * Notifies the {@link SmartSuggestionsService} that the service started.
*/
@GuardedBy("mLock")
public void notifySessionStartedLocked() {
@@ -80,14 +80,14 @@
}
/**
- * Notifies the {@link IntelligenceService} of a batch of events.
+ * Notifies the {@link SmartSuggestionsService} of a batch of events.
*/
public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
mRemoteService.onContentCaptureEventsRequest(mId, events);
}
/**
- * Notifies the {@link IntelligenceService} of a snapshot of an activity.
+ * Notifies the {@link SmartSuggestionsService} of a snapshot of an activity.
*/
@GuardedBy("mLock")
public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
@@ -110,7 +110,7 @@
* Cleans up the session and removes it from the service.
*
* @param notifyRemoteService whether it should trigger a {@link
- * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+ * SmartSuggestionsService#onDestroyInteractionSession(InteractionSessionId)}
* request.
*/
@GuardedBy("mLock")
@@ -126,7 +126,7 @@
* Cleans up the session, but not removes it from the service.
*
* @param notifyRemoteService whether it should trigger a {@link
- * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+ * SmartSuggestionsService#onDestroyInteractionSession(InteractionSessionId)}
* request.
*/
@GuardedBy("mLock")
@@ -146,7 +146,7 @@
}
@Override // from RemoteScreenObservationServiceCallbacks
- public void onServiceDied(AbstractRemoteService service) {
+ public void onServiceDied(AbstractRemoteService<?> service) {
// TODO(b/111276913): implement (remove session from PerUserSession?)
if (mService.isDebug()) {
Slog.d(TAG, "onServiceDied() for " + mId);
@@ -176,6 +176,10 @@
pw.println(mAutofillCallback != null);
}
+ String toShortString() {
+ return mId.getValue() + ":" + mActivityToken;
+ }
+
@Override
public String toString() {
return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index 38810dd..b8f2ad0 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -16,17 +16,24 @@
package com.android.server.intelligence;
-import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
+import static android.Manifest.permission.MANAGE_SMART_SUGGESTIONS;
+import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
import android.os.UserManager;
import android.service.intelligence.InteractionSessionId;
+import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
@@ -41,6 +48,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -49,11 +57,16 @@
* <p>The data collected by this service can be analyzed and combined with other sources to provide
* contextual data in other areas of the system such as Autofill.
*/
+//TODO(b/111276913): rename once the final name is defined
public final class IntelligenceManagerService extends
AbstractMasterSystemService<IntelligenceManagerService, IntelligencePerUserService> {
private static final String TAG = "IntelligenceManagerService";
+ static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
+
+ private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
@GuardedBy("mLock")
private ActivityManagerInternal mAm;
@@ -64,12 +77,6 @@
}
@Override // from AbstractMasterSystemService
- protected String getServiceSettingsProperty() {
- // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
- return "intel_service";
- }
-
- @Override // from AbstractMasterSystemService
protected IntelligencePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
return new IntelligencePerUserService(this, mLock, resolvedUserId);
@@ -77,7 +84,7 @@
@Override // from SystemService
public void onStart() {
- publishBinderService(INTELLIGENCE_MANAGER_SERVICE,
+ publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE,
new IntelligenceManagerServiceStub());
publishLocalService(IntelligenceManagerInternal.class, mLocalService);
}
@@ -88,6 +95,66 @@
service.destroyLocked();
}
+ @Override // from AbstractMasterSystemService
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_SMART_SUGGESTIONS, TAG);
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_DURATION_MS;
+ }
+
+ // Called by Shell command.
+ void destroySessions(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
+ Slog.i(TAG, "destroySessions() for userId " + userId);
+ enforceCallingPermissionForManagement();
+
+ synchronized (mLock) {
+ if (userId != UserHandle.USER_ALL) {
+ final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.destroySessionsLocked();
+ }
+ } else {
+ visitServicesLocked((s) -> s.destroySessionsLocked());
+ }
+ }
+
+ try {
+ receiver.send(0, new Bundle());
+ } catch (RemoteException e) {
+ // Just ignore it...
+ }
+ }
+
+ // Called by Shell command.
+ void listSessions(int userId, IResultReceiver receiver) {
+ Slog.i(TAG, "listSessions() for userId " + userId);
+ enforceCallingPermissionForManagement();
+
+ final Bundle resultData = new Bundle();
+ final ArrayList<String> sessions = new ArrayList<>();
+
+ synchronized (mLock) {
+ if (userId != UserHandle.USER_ALL) {
+ final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.listSessionsLocked(sessions);
+ }
+ } else {
+ visitServicesLocked((s) -> s.listSessionsLocked(sessions));
+ }
+ }
+
+ resultData.putStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS, sessions);
+ try {
+ receiver.send(0, resultData);
+ } catch (RemoteException e) {
+ // Just ignore it...
+ }
+ }
+
private ActivityManagerInternal getAmInternal() {
synchronized (mLock) {
if (mAm == null) {
@@ -117,7 +184,7 @@
synchronized (mLock) {
final IntelligencePerUserService service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, componentName, taskId, displayId,
- sessionId, flags, result);
+ sessionId, flags, mAllowInstantService, result);
}
}
@@ -134,12 +201,13 @@
}
@Override
- public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId) {
+ public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
+ @Nullable List<ContentCaptureEvent> events) {
Preconditions.checkNotNull(sessionId);
synchronized (mLock) {
final IntelligencePerUserService service = getServiceForUserLocked(userId);
- service.finishSessionLocked(sessionId);
+ service.finishSessionLocked(sessionId, events);
}
}
@@ -151,6 +219,14 @@
dumpLocked("", pw);
}
}
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+ throws RemoteException {
+ new IntelligenceServiceShellCommand(IntelligenceManagerService.this).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
}
private final class LocalService extends IntelligenceManagerInternal {
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index 051f0d6..6f047c5 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -22,6 +22,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.assist.AssistContent;
@@ -35,12 +36,13 @@
import android.os.RemoteException;
import android.service.intelligence.InteractionSessionId;
import android.service.intelligence.SnapshotData;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.IAutoFillManagerClient;
import android.view.intelligence.ContentCaptureEvent;
-import android.view.intelligence.IntelligenceManager;
+import android.view.intelligence.ContentCaptureManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -48,11 +50,13 @@
import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
* Per-user instance of {@link IntelligenceManagerService}.
*/
+//TODO(b/111276913): rename once the final name is defined
final class IntelligencePerUserService
extends AbstractPerUserSystemService<IntelligencePerUserService,
IntelligenceManagerService> {
@@ -74,35 +78,56 @@
protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
+ int flags = PackageManager.GET_META_DATA;
+ final boolean isTemp = isTemporaryServiceSetLocked();
+ if (!isTemp) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+
ServiceInfo si;
try {
- // TODO(b/111276913): must check that either the service is from a system component,
- // or it matches a service set by shell cmd (so it can be used on CTS tests and when
- // OEMs are implementing the real service
- si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
- PackageManager.GET_META_DATA, mUserId);
+ si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags, mUserId);
} catch (RemoteException e) {
Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
return null;
}
- if (!Manifest.permission.BIND_INTELLIGENCE_SERVICE.equals(si.permission)) {
- Slog.w(TAG, "IntelligenceService from '" + si.packageName
+ if (si == null) {
+ Slog.w(TAG, "Could not get serviceInfo for " + (isTemp ? " (temp)" : "(default system)")
+ + " " + serviceComponent.flattenToShortString());
+ return null;
+ }
+ if (!Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE.equals(si.permission)) {
+ Slog.w(TAG, "SmartSuggestionsService from '" + si.packageName
+ "' does not require permission "
- + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+ + Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE);
throw new SecurityException("Service does not require permission "
- + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+ + Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE);
}
return si;
}
+ @Override // from PerUserSystemService
+ @GuardedBy("mLock")
+ protected boolean updateLocked(boolean disabled) {
+ destroyLocked();
+ return super.updateLocked(disabled);
+ }
+
+ @Override // from PerUserSystemService
+ protected String getDefaultComponentName() {
+ final String name = getContext()
+ .getString(com.android.internal.R.string.config_defaultSmartSuggestionsService);
+ return TextUtils.isEmpty(name) ? null : name;
+ }
+
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
@NonNull ComponentName componentName, int taskId, int displayId,
- @NonNull InteractionSessionId sessionId, int flags,
+ @NonNull InteractionSessionId sessionId, int flags, boolean bindInstantServiceAllowed,
@NonNull IResultReceiver resultReceiver) {
if (!isEnabledLocked()) {
- sendToClient(resultReceiver, IntelligenceManager.STATE_DISABLED);
+ sendToClient(resultReceiver, ContentCaptureManager.STATE_DISABLED);
return;
}
final ComponentName serviceComponentName = getServiceComponentName();
@@ -125,13 +150,10 @@
// TODO(b/111276913): check if local ids match and decide what to do if they don't
// TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
// if not, move notifySessionStartedLocked() into session constructor
- sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+ sendToClient(resultReceiver, ContentCaptureManager.STATE_ACTIVE);
return;
}
- // TODO(b/117779333): get from mMaster once it's moved to superclass
- final boolean bindInstantServiceAllowed = false;
-
session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
bindInstantServiceAllowed, mMaster.verbose);
@@ -141,12 +163,13 @@
}
mSessions.put(sessionId, session);
session.notifySessionStartedLocked();
- sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+ sendToClient(resultReceiver, ContentCaptureManager.STATE_ACTIVE);
}
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
- public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+ public void finishSessionLocked(@NonNull InteractionSessionId sessionId,
+ @Nullable List<ContentCaptureEvent> events) {
if (!isEnabledLocked()) {
return;
}
@@ -158,8 +181,18 @@
}
return;
}
+ if (events != null && !events.isEmpty()) {
+ // TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
+ // calls because it's not clear yet whether we'll change the manager to send events
+ // to the service directly (i.e., without passing through system server). Once we
+ // decide, we might need to split IIntelligenceService.onSessionLifecycle() in 2
+ // methods, one for start and another for finish (and passing the events to finish),
+ // otherwise the service might receive the 2 calls out of order.
+ session.sendEventsLocked(events);
+ }
if (mMaster.verbose) {
- Slog.v(TAG, "finishSession(): " + session);
+ Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
+ + session);
}
session.removeSelfLocked(true);
}
@@ -180,7 +213,7 @@
return;
}
if (mMaster.verbose) {
- Slog.v(TAG, "sendEvents(): id=" + sessionId + "; events =" + events.size());
+ Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size());
}
session.sendEventsLocked(events);
}
@@ -233,6 +266,11 @@
@GuardedBy("mLock")
public void destroyLocked() {
if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
+ destroySessionsLocked();
+ }
+
+ @GuardedBy("mLock")
+ void destroySessionsLocked() {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
final ContentCaptureSession session = mSessions.valueAt(i);
@@ -241,6 +279,15 @@
mSessions.clear();
}
+ @GuardedBy("mLock")
+ void listSessionsLocked(ArrayList<String> output) {
+ final int numSessions = mSessions.size();
+ for (int i = 0; i < numSessions; i++) {
+ final ContentCaptureSession session = mSessions.valueAt(i);
+ output.add(session.toShortString());
+ }
+ }
+
public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
@NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
synchronized (mLock) {
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
new file mode 100644
index 0000000..0d92a972
--- /dev/null
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+package com.android.server.intelligence;
+
+import static com.android.server.intelligence.IntelligenceManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Shell Command implementation for {@link IntelligenceManagerService}.
+ */
+//TODO(b/111276913): rename once the final name is defined
+public final class IntelligenceServiceShellCommand extends ShellCommand {
+
+ private final IntelligenceManagerService mService;
+
+ public IntelligenceServiceShellCommand(@NonNull IntelligenceManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "list":
+ return requestList(pw);
+ case "destroy":
+ return requestDestroy(pw);
+ case "get":
+ return requestGet(pw);
+ case "set":
+ return requestSet(pw);
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter();) {
+ // TODO(b/111276913): rename "intelligence" once SELinux rule changed
+ pw.println("Intelligence Service (intelligence) commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" get bind-instant-service-allowed");
+ pw.println(" Gets whether binding to services provided by instant apps is allowed");
+ pw.println("");
+ pw.println(" set bind-instant-service-allowed [true | false]");
+ pw.println(" Sets whether binding to services provided by instant apps is allowed");
+ pw.println("");
+ pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println("");
+ pw.println(" list sessions [--user USER_ID]");
+ pw.println(" Lists all pending sessions.");
+ pw.println("");
+ pw.println(" destroy sessions [--user USER_ID]");
+ pw.println(" Destroys all pending sessions.");
+ pw.println("");
+ }
+ }
+
+ private int requestGet(PrintWriter pw) {
+ final String what = getNextArgRequired();
+ switch(what) {
+ case "bind-instant-service-allowed":
+ return getBindInstantService(pw);
+ default:
+ pw.println("Invalid set: " + what);
+ return -1;
+ }
+ }
+
+ private int requestSet(PrintWriter pw) {
+ final String what = getNextArgRequired();
+
+ switch(what) {
+ case "bind-instant-service-allowed":
+ return setBindInstantService(pw);
+ case "temporary-service":
+ return setTemporaryService();
+ default:
+ pw.println("Invalid set: " + what);
+ return -1;
+ }
+ }
+
+ private int getBindInstantService(PrintWriter pw) {
+ if (mService.getAllowInstantService()) {
+ pw.println("true");
+ } else {
+ pw.println("false");
+ }
+ return 0;
+ }
+
+ private int setBindInstantService(PrintWriter pw) {
+ final String mode = getNextArgRequired();
+ switch (mode.toLowerCase()) {
+ case "true":
+ mService.setAllowInstantService(true);
+ return 0;
+ case "false":
+ mService.setAllowInstantService(false);
+ return 0;
+ default:
+ pw.println("Invalid mode: " + mode);
+ return -1;
+ }
+ }
+
+ private int setTemporaryService() {
+ final int userId = getNextIntArgRequired();
+ final String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ return 0;
+ }
+ final int duration = getNextIntArgRequired();
+ mService.setTemporaryService(userId, serviceName, duration);
+ return 0;
+ }
+
+ private int requestDestroy(PrintWriter pw) {
+ if (!isNextArgSessions(pw)) {
+ return -1;
+ }
+
+ final int userId = getUserIdFromArgsOrAllUsers();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final IResultReceiver receiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ latch.countDown();
+ }
+ };
+ return requestSessionCommon(pw, latch, () -> mService.destroySessions(userId, receiver));
+ }
+
+ private int requestList(PrintWriter pw) {
+ if (!isNextArgSessions(pw)) {
+ return -1;
+ }
+
+ final int userId = getUserIdFromArgsOrAllUsers();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final IResultReceiver receiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ final ArrayList<String> sessions = resultData
+ .getStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS);
+ for (String session : sessions) {
+ pw.println(session);
+ }
+ latch.countDown();
+ }
+ };
+ return requestSessionCommon(pw, latch, () -> mService.listSessions(userId, receiver));
+ }
+
+ private boolean isNextArgSessions(PrintWriter pw) {
+ final String type = getNextArgRequired();
+ if (!type.equals("sessions")) {
+ pw.println("Error: invalid list type");
+ return false;
+ }
+ return true;
+ }
+
+ private int requestSessionCommon(PrintWriter pw, CountDownLatch latch,
+ Runnable command) {
+ command.run();
+ return waitForLatch(pw, latch);
+ }
+
+ private int waitForLatch(PrintWriter pw, CountDownLatch latch) {
+ try {
+ final boolean received = latch.await(5, TimeUnit.SECONDS);
+ if (!received) {
+ pw.println("Timed out after 5 seconds");
+ return -1;
+ }
+ } catch (InterruptedException e) {
+ pw.println("System call interrupted");
+ Thread.currentThread().interrupt();
+ return -1;
+ }
+ return 0;
+ }
+
+ private int getUserIdFromArgsOrAllUsers() {
+ if ("--user".equals(getNextArg())) {
+ return UserHandle.parseUserArg(getNextArgRequired());
+ }
+ return UserHandle.USER_ALL;
+ }
+
+ private int getNextIntArgRequired() {
+ return Integer.parseInt(getNextArgRequired());
+ }
+}
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
index 00c5b6a..d9f4f20 100644
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
+import android.service.intelligence.ContentCaptureEventsRequest;
import android.service.intelligence.IIntelligenceService;
import android.service.intelligence.InteractionContext;
import android.service.intelligence.InteractionSessionId;
@@ -35,11 +36,13 @@
import android.view.intelligence.ContentCaptureEvent;
import com.android.internal.os.IResultReceiver;
-import com.android.server.AbstractRemoteService;
+import com.android.server.AbstractMultiplePendingRequestsRemoteService;
import java.util.List;
-final class RemoteIntelligenceService extends AbstractRemoteService {
+//TODO(b/111276913): rename once the final name is defined
+final class RemoteIntelligenceService
+ extends AbstractMultiplePendingRequestsRemoteService<RemoteIntelligenceService> {
private static final String TAG = "RemoteIntelligenceService";
@@ -54,7 +57,7 @@
RemoteIntelligenceServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, serviceInterface, componentName, userId, callbacks,
- bindInstantServiceAllowed, verbose);
+ bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
mCallbacks = callbacks;
}
@@ -194,7 +197,8 @@
@Override // from MyPendingRequest
public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
- remoteService.mService.onContentCaptureEvents(mSessionId, mEvents);
+ remoteService.mService.onContentCaptureEventsRequest(mSessionId,
+ new ContentCaptureEventsRequest(mEvents));
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 56f7cff..05ff660 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -22,6 +22,7 @@
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.NonNull;
import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
@@ -55,6 +56,9 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.IStorageManager;
+import android.provider.Settings;
+import android.sysprop.VoldProperties;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
@@ -120,6 +124,7 @@
import com.android.server.role.RoleManagerService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
+import com.android.server.signedconfig.SignedConfigService;
import com.android.server.soundtrigger.SoundTriggerService;
import com.android.server.stats.StatsCompanionService;
import com.android.server.statusbar.StatusBarManagerService;
@@ -654,7 +659,7 @@
traceEnd();
// Only run "core" apps if we're encrypting the device.
- String cryptState = SystemProperties.get("vold.decrypt");
+ String cryptState = VoldProperties.decrypt().orElse("");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true;
@@ -796,10 +801,6 @@
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
- //TODO(b/111276913): temporarily disabled until the manager is properly implemented to
- // ignore events when disabled and buffer when enabled
- boolean disableIntelligence = SystemProperties.getBoolean(
- "config.disable_intelligence", true);
boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime",
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -994,6 +995,11 @@
traceBeginAndSlog("PinnerService");
mSystemServiceManager.startService(PinnerService.class);
traceEnd();
+
+ traceBeginAndSlog("SignedConfigService");
+ SignedConfigService.registerUpdateReceiver(mSystemContext);
+ traceEnd();
+
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
@@ -1130,13 +1136,7 @@
traceEnd();
}
- if (!disableIntelligence) {
- traceBeginAndSlog("StartIntelligenceService");
- mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
- traceEnd();
- } else {
- Slog.d(TAG, "IntelligenceService disabled");
- }
+ startIntelligenceService(context);
// NOTE: ClipboardService indirectly depends on IntelligenceService
traceBeginAndSlog("StartClipboardService");
@@ -1503,6 +1503,14 @@
}
traceEnd();
+ traceBeginAndSlog("RuntimeService");
+ try {
+ ServiceManager.addService("runtime", new RuntimeService(context));
+ } catch (Throwable e) {
+ reportWtf("starting RuntimeService", e);
+ }
+ traceEnd();
+
// timezone.RulesManagerService will prevent a device starting up if the chain of trust
// required for safe time zone updates might be broken. RuleManagerService cannot do
// this check when mOnlyCore == true, so we don't enable the service in this case.
@@ -2092,6 +2100,37 @@
}, BOOT_TIMINGS_TRACE_LOG);
}
+ private void startIntelligenceService(@NonNull Context context) {
+
+ // First check if it was explicitly enabled by Settings
+ boolean explicitlySupported = false;
+ final String settings = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED);
+ if (settings != null) {
+ explicitlySupported = Boolean.parseBoolean(settings);
+ if (explicitlySupported) {
+ Slog.d(TAG, "IntelligenceService explicitly enabled by Settings");
+ } else {
+ Slog.d(TAG, "IntelligenceService explicitly disabled by Settings");
+ return;
+ }
+ }
+
+ // Then check if OEM overlaid the resource that defines the service.
+ if (!explicitlySupported) {
+ final String serviceName = context
+ .getString(com.android.internal.R.string.config_defaultSmartSuggestionsService);
+ if (TextUtils.isEmpty(serviceName)) {
+ Slog.d(TAG, "IntelligenceService disabled because config resource is not overlaid");
+ return;
+ }
+ }
+
+ traceBeginAndSlog("StartIntelligenceService");
+ mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
static final void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index dc55179b..c9b9f3e 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -146,13 +146,14 @@
final long identity = Binder.clearCallingIdentity();
try {
disabledMessage = dpmi.getPrintingDisabledReasonForUser(callingUserId);
+
+ if (disabledMessage != null) {
+ Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
+ Toast.LENGTH_LONG).show();
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
- if (disabledMessage != null) {
- Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
- Toast.LENGTH_LONG).show();
- }
try {
adapter.start();
} catch (RemoteException re) {
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index f7bb68c..ba4caf44 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,49 +16,27 @@
package com.android.server.backup;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
import static com.android.server.backup.testing.TransportData.backupTransport;
-import static com.android.server.backup.testing.TransportData.d2dTransport;
-import static com.android.server.backup.testing.TransportData.localTransport;
-import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
-import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-import android.app.backup.BackupManager;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
-import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.PowerManager;
-import android.os.PowerSaveState;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TransportData;
-import com.android.server.backup.testing.TransportTestUtils.TransportMock;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.testing.shadows.ShadowAppBackupUtils;
-import com.android.server.testing.shadows.ShadowBinder;
-import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
-import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,977 +44,482 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowContextWrapper;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.shadows.ShadowSettings;
import java.io.File;
-import java.util.List;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
-/** Tests for the system service {@link BackupManagerService} that performs backup/restore. */
+/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAppBackupUtils.class})
@Presubmit
public class BackupManagerServiceTest {
- private static final String TAG = "BMSTest";
- private static final String PACKAGE_1 = "some.package.1";
- private static final String PACKAGE_2 = "some.package.2";
+ private static final String TEST_PACKAGE = "package";
+ private static final String TEST_TRANSPORT = "transport";
+ @Mock private UserBackupManagerService mUserBackupManagerService;
@Mock private TransportManager mTransportManager;
- private HandlerThread mBackupThread;
- private ShadowLooper mShadowBackupLooper;
- private File mBaseStateDir;
- private File mDataDir;
- private ShadowContextWrapper mShadowContext;
+ private BackupManagerService mBackupManagerService;
private Context mContext;
- private TransportData mTransport;
- private String mTransportName;
- private ShadowPackageManager mShadowPackageManager;
- /**
- * Initialize state that {@link BackupManagerService} operations interact with. This includes
- * setting up the transport, starting the backup thread, and creating backup data directories.
- */
+ /** Initialize {@link BackupManagerService}. */
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTransport = backupTransport();
- mTransportName = mTransport.transportName;
-
- // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
- // we should not fail tests because of this. This is not flakiness, the exceptions thrown
- // don't interfere with the tests.
- mBackupThread = startSilentBackupThread(TAG);
- mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
-
- ContextWrapper context = RuntimeEnvironment.application;
- mShadowPackageManager = shadowOf(context.getPackageManager());
- mContext = context;
- mShadowContext = shadowOf(context);
-
- File cacheDir = mContext.getCacheDir();
- // Corresponds to /data/backup
- mBaseStateDir = new File(cacheDir, "base_state");
- // Corresponds to /cache/backup_stage
- mDataDir = new File(cacheDir, "data");
+ Application application = RuntimeEnvironment.application;
+ mContext = application;
+ mBackupManagerService =
+ new BackupManagerService(
+ application,
+ new Trampoline(application),
+ BackupManagerServiceTestUtils.startBackupThread(null),
+ new File(application.getCacheDir(), "base_state"),
+ new File(application.getCacheDir(), "data"),
+ mTransportManager);
+ mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
}
/**
- * Clean up and reset state that was created for testing {@link BackupManagerService}
- * operations.
- */
- @After
- public void tearDown() throws Exception {
- mBackupThread.quit();
- ShadowAppBackupUtils.reset();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
- * specifically to prevent overloading the logs in production.
+ * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
+ * This is specifically to prevent overloading the logs in production.
*/
@Test
- public void testMoreDebug_isFalse() {
+ public void testMoreDebug_isFalse() throws Exception {
boolean moreDebug = BackupManagerService.MORE_DEBUG;
assertThat(moreDebug).isFalse();
}
- /**
- * Test verifying that {@link BackupManagerService#getDestinationString(String)} returns the
- * current destination string of inputted transport if the transport is registered.
- */
+ // TODO(b/118520567): Change the following tests to use the per-user instance of
+ // UserBackupManagerService once it's implemented. Currently these tests only test the straight
+ // forward redirection.
+
+ // ---------------------------------------------
+ // Backup agent tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testDestinationString() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
- .thenReturn("destinationString");
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testDataChanged_callsDataChangedForUser() throws Exception {
+ mBackupManagerService.dataChanged(TEST_PACKAGE);
- String destination = backupManagerService.getDestinationString(mTransportName);
-
- assertThat(destination).isEqualTo("destinationString");
+ verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
}
- /**
- * Test verifying that {@link BackupManagerService#getDestinationString(String)} returns {@code
- * null} if the inputted transport is not registered.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testDestinationString_whenTransportNotRegistered() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
- .thenThrow(TransportNotRegisteredException.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
+ IBinder agentBinder = mock(IBinder.class);
- String destination = backupManagerService.getDestinationString(mTransportName);
+ mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
- assertThat(destination).isNull();
+ verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
}
- /**
- * Test verifying that {@link BackupManagerService#getDestinationString(String)} throws a {@link
- * SecurityException} if the caller does not have backup permission.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testDestinationString_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
- .thenThrow(TransportNotRegisteredException.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
+ mBackupManagerService.agentDisconnected(TEST_PACKAGE);
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.getDestinationString(mTransportName));
+ verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
}
- /**
- * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} returns
- * {@code false} when the given app is not eligible for backup.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- setUpCurrentTransport(mTransportManager, mTransport);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testOpComplete_callsOpCompleteForUser() throws Exception {
+ mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
- boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
-
- assertThat(result).isFalse();
+ verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
}
- /**
- * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} returns
- * {@code true} when the given app is eligible for backup.
- */
+ // ---------------------------------------------
+ // Transport tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
+ String[] transports = {TEST_TRANSPORT};
- boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+ mBackupManagerService.initializeTransports(transports, /* observer */ null);
- assertThat(result).isTrue();
- verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
}
- /**
- * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} throws a
- * {@link SecurityException} if the caller does not have backup permission.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- setUpCurrentTransport(mTransportManager, mTransport);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
+ mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
+ verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
}
- /**
- * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])}
- * returns an {@code array} of only apps that are eligible for backup from an {@array} of
- * inputted apps.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testFilterAppsEligibleForBackup() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
+ mBackupManagerService.getCurrentTransport();
- String[] filtered =
- backupManagerService.filterAppsEligibleForBackup(
- new String[] {PACKAGE_1, PACKAGE_2});
-
- assertThat(filtered).asList().containsExactly(PACKAGE_1);
- verify(mTransportManager)
- .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ verify(mUserBackupManagerService).getCurrentTransport();
}
- /**
- * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])}
- * returns an empty {@code array} if no inputted apps are eligible for backup.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
+ throws Exception {
+ mBackupManagerService.getCurrentTransportComponent();
- String[] filtered =
- backupManagerService.filterAppsEligibleForBackup(
- new String[] {PACKAGE_1, PACKAGE_2});
-
- assertThat(filtered).isEmpty();
+ verify(mUserBackupManagerService).getCurrentTransportComponent();
}
- /**
- * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])} throws
- * a {@link SecurityException} if the caller does not have backup permission.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- setUpCurrentTransport(mTransportManager, mTransport);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
+ mBackupManagerService.listAllTransports();
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.filterAppsEligibleForBackup(
- new String[] {PACKAGE_1, PACKAGE_2}));
+ verify(mUserBackupManagerService).listAllTransports();
}
- /* Tests for select transport */
-
- private ComponentName mNewTransportComponent;
- private TransportData mNewTransport;
- private TransportMock mNewTransportMock;
- private TransportData mOldTransport;
- private TransportMock mOldTransportMock;
-
- private void setUpForSelectTransport() throws Exception {
- mNewTransport = backupTransport();
- mNewTransportComponent = mNewTransport.getTransportComponent();
- mOldTransport = d2dTransport();
- List<TransportMock> transportMocks =
- setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
- mNewTransportMock = transportMocks.get(0);
- mOldTransportMock = transportMocks.get(1);
- when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
- .thenReturn(mOldTransport.transportName);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransport(String)} successfully
- * switches the current transport to the inputted transport, returns the name of the old
- * transport, and disposes of the transport client after the operation.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSelectBackupTransport() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
+ throws Exception {
+ mBackupManagerService.listAllTransportComponents();
- String oldTransport =
- backupManagerService.selectBackupTransport(mNewTransport.transportName);
-
- assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
- assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
- verify(mTransportManager)
- .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ verify(mUserBackupManagerService).listAllTransportComponents();
}
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransport(String)} throws a
- * {@link SecurityException} if the caller does not have backup permission.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSelectBackupTransport_withoutPermission() throws Exception {
- setUpForSelectTransport();
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
+ mBackupManagerService.getTransportWhitelist();
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+ verify(mUserBackupManagerService).getTransportWhitelist();
}
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
- * transport and disposes of the transport client after the operation.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testSelectBackupTransportAsync() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
- verify(callback).onSuccess(eq(mNewTransport.transportName));
- verify(mTransportManager)
- .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
- }
-
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
- * transport and notifies the inputted callback of failure when it fails to register the
- * transport.
- */
- @Test
- public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
- setUpForSelectTransport();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
- .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
- verify(callback).onFailure(anyInt());
- }
-
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
- * transport and notifies the inputted callback of failure when the transport gets unregistered.
- */
- @Test
- public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
- setUpTransports(mTransportManager, mTransport.unregistered());
- ComponentName newTransportComponent = mTransport.getTransportComponent();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
- .thenReturn(BackupManager.SUCCESS);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
- verify(callback).onFailure(anyInt());
- }
-
- /**
- * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
- * have backup permission.
- */
- @Test
- public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
- setUpForSelectTransport();
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- ComponentName newTransportComponent = mNewTransport.getTransportComponent();
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.selectBackupTransportAsync(
- newTransportComponent, mock(ISelectBackupTransportCallback.class)));
- }
-
- private String getSettingsTransport() {
- return ShadowSettings.ShadowSecure.getString(
- mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns the
- * {@link ComponentName} of the currently selected transport.
- */
- @Test
- public void testGetCurrentTransportComponent() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getCurrentTransportComponent())
- .thenReturn(mTransport.getTransportComponent());
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
- assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
- }
-
- /**
- * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns
- * {@code null} if there is no currently selected transport.
- */
- @Test
- public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
- assertThat(transportComponent).isNull();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns
- * {@code null} if the currently selected transport is not registered.
- */
- @Test
- public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- when(mTransportManager.getCurrentTransportComponent())
- .thenThrow(TransportNotRegisteredException.class);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
- assertThat(transportComponent).isNull();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} throws a
- * {@link SecurityException} if the caller does not have backup permission.
- */
- @Test
- public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
- }
-
- /* Tests for updating transport attributes */
-
- private static final int PACKAGE_UID = 10;
- private ComponentName mTransportComponent;
- private int mTransportUid;
-
- private void setUpForUpdateTransportAttributes() throws Exception {
- mTransportComponent = mTransport.getTransportComponent();
- String transportPackage = mTransportComponent.getPackageName();
-
- ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
- shadowPackageManager.addPackage(transportPackage);
- shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
-
- mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} succeeds if the uid of the transport is same as the
- * uid of the caller.
- */
- @Test
- public void
- testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
- throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
+ throws Exception {
+ TransportData transport = backupTransport();
Intent configurationIntent = new Intent();
Intent dataManagementIntent = new Intent();
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
+ mBackupManagerService.updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
configurationIntent,
"currentDestinationString",
dataManagementIntent,
"dataManagementLabel");
- verify(mTransportManager)
+ verify(mUserBackupManagerService)
.updateTransportAttributes(
- eq(mTransportComponent),
- eq(mTransportName),
- eq(configurationIntent),
- eq("currentDestinationString"),
- eq(dataManagementIntent),
- eq("dataManagementLabel"));
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link SecurityException} if the uid of the
- * transport is not equal to the uid of the caller.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+ public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
+ mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ mBackupManagerService.selectBackupTransportAsync(
+ transport.getTransportComponent(), callback);
+
+ verify(mUserBackupManagerService)
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
+ mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
+ mBackupManagerService.getDestinationString(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
+ mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
+ mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ // ---------------------------------------------
+ // Settings tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
+ mBackupManagerService.setBackupEnabled(true);
+
+ verify(mUserBackupManagerService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
+ mBackupManagerService.setAutoRestore(true);
+
+ verify(mUserBackupManagerService).setAutoRestore(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
+ mBackupManagerService.setBackupProvisioned(true);
+
+ verify(mUserBackupManagerService).setBackupProvisioned(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
+ mBackupManagerService.isBackupEnabled();
+
+ verify(mUserBackupManagerService).isBackupEnabled();
+ }
+
+ // ---------------------------------------------
+ // Backup tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
+ mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+ verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ String[] packages = {TEST_PACKAGE};
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid + 1,
- mTransportComponent,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
+ mBackupManagerService.filterAppsEligibleForBackup(packages);
+
+ verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
- * null} transport component.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+ public void testBackupNow_callsBackupNowForUser() throws Exception {
+ mBackupManagerService.backupNow();
+
+ verify(mUserBackupManagerService).backupNow();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_callsRequestBackupForUser() throws Exception {
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+ mBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
+ mBackupManagerService.cancelBackups();
+
+ verify(mUserBackupManagerService).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
+ FullBackupJob job = new FullBackupJob();
+
+ mBackupManagerService.beginFullBackup(job);
+
+ verify(mUserBackupManagerService).beginFullBackup(job);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
+ mBackupManagerService.endFullBackup();
+
+ verify(mUserBackupManagerService).endFullBackup();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
+ String[] packages = {TEST_PACKAGE};
+
+ mBackupManagerService.fullTransportBackup(packages);
+
+ verify(mUserBackupManagerService).fullTransportBackup(packages);
+ }
+
+ // ---------------------------------------------
+ // Restore tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
+ mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
+ mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- null,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
+ verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
- * null} transport name.
- */
- @Test
- public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ // ---------------------------------------------
+ // Adb backup/restore tests
+ // ---------------------------------------------
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- null,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
+ mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
- * null} destination string.
- */
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+ public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
+ mBackupManagerService.hasBackupPassword();
+
+ verify(mUserBackupManagerService).hasBackupPassword();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbBackup_callsAdbBackupForUser() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ ParcelFileDescriptor parcelFileDescriptor =
+ ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+ String[] packages = {TEST_PACKAGE};
+
+ mBackupManagerService.adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ packages);
+
+ verify(mUserBackupManagerService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ packages);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ ParcelFileDescriptor parcelFileDescriptor =
+ ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+
+ mBackupManagerService.adbRestore(parcelFileDescriptor);
+
+ verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- new Intent(),
- null,
- new Intent(),
- "dataManagementLabel"));
+ mBackupManagerService.acknowledgeAdbBackupOrRestore(
+ /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+ verify(mUserBackupManagerService)
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
}
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given either a
- * {@code null} data management label or {@code null} data management intent, but not both.
- */
+ // ---------------------------------------------
+ // Service tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
@Test
- public void
- testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
- throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
+ public void testDump_callsDumpForUser() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- null,
- "dataManagementLabel"));
+ mBackupManagerService.dump(fileDescriptor, printWriter, args);
- expectThrows(
- RuntimeException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- null));
- }
-
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} succeeds if the caller has backup permission.
- */
- @Test
- public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
- throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- Intent configurationIntent = new Intent();
- Intent dataManagementIntent = new Intent();
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
-
- verify(mTransportManager)
- .updateTransportAttributes(
- eq(mTransportComponent),
- eq(mTransportName),
- eq(configurationIntent),
- eq("currentDestinationString"),
- eq(dataManagementIntent),
- eq("dataManagementLabel"));
- }
-
- /**
- * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
- * String, Intent, String, Intent, String)} throws a {@link SecurityException} if the caller
- * does not have backup permission.
- */
- @Test
- public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
- throws Exception {
- setUpForUpdateTransportAttributes();
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.updateTransportAttributes(
- mTransportUid,
- mTransportComponent,
- mTransportName,
- new Intent(),
- "currentDestinationString",
- new Intent(),
- "dataManagementLabel"));
- }
-
- /* Tests for request backup */
-
- @Mock private IBackupObserver mObserver;
-
- private void setUpForRequestBackup(String... packages) throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- for (String packageName : packages) {
- mShadowPackageManager.addPackage(packageName);
- ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
- }
- setUpCurrentTransport(mTransportManager, mTransport);
- }
-
- private void tearDownForRequestBackup() {
- ShadowKeyValueBackupTask.reset();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} throws a {@link SecurityException} if the caller does not have backup permission.
- */
- @Test
- public void testRequestBackup_whenPermissionDenied() throws Exception {
- mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
- */
- @Test
- public void testRequestBackup_whenPackagesNull() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(
- IllegalArgumentException.class,
- () -> backupManagerService.requestBackup(null, mObserver, 0));
- verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
- * packages.
- */
- @Test
- public void testRequestBackup_whenPackagesEmpty() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
- expectThrows(
- IllegalArgumentException.class,
- () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
- verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
- */
- @Test
- public void testRequestBackup_whenBackupDisabled() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setEnabled(false);
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
- * through SUW.
- */
- @Test
- public void testRequestBackup_whenNotProvisioned() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setProvisioned(false);
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
- * registered.
- */
- @Test
- public void testRequestBackup_whenTransportNotRegistered() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- setUpCurrentTransport(mTransportManager, mTransport.unregistered());
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setEnabled(true);
- backupManagerService.setProvisioned(true);
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
- verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
- * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
- */
- @Test
- public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- mShadowPackageManager.addPackage(PACKAGE_1);
- setUpCurrentTransport(mTransportManager, mTransport);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setEnabled(true);
- backupManagerService.setProvisioned(true);
- // Haven't set PACKAGE_1 as eligible
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- assertThat(result).isEqualTo(BackupManager.SUCCESS);
- verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
- tearDownForRequestBackup();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
- * package succeeds.
- */
- @Test
- @Config(shadows = ShadowKeyValueBackupTask.class)
- public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
- setUpForRequestBackup(PACKAGE_1);
- BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(result).isEqualTo(BackupManager.SUCCESS);
- ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
- assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
- assertThat(shadowTask.getPendingFullBackups()).isEmpty();
- // TODO: Assert more about KeyValueBackupTask
- tearDownForRequestBackup();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
- * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
- * backup package succeeds.
- */
- @Test
- @Config(shadows = ShadowKeyValueBackupTask.class)
- public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
- setUpForRequestBackup(PACKAGE_1);
- ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
- BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
-
- int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
- mShadowBackupLooper.runToEndOfTasks();
- assertThat(result).isEqualTo(BackupManager.SUCCESS);
- ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
- assertThat(shadowTask.getQueue()).isEmpty();
- assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
- // TODO: Assert more about KeyValueBackupTask
- tearDownForRequestBackup();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#backupNow()} clears the calling identity
- * for scheduling a job and then restores the original calling identity after the operation.
- */
- @Test
- @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
- public void testBackupNow_clearsCallingIdentityForJobScheduler() {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- setUpPowerManager(backupManagerService);
- ShadowBinder.setCallingUid(1);
-
- backupManagerService.backupNow();
-
- assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
- assertThat(Binder.getCallingUid()).isEqualTo(1);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#backupNow()} restores the original calling
- * identity if an exception is thrown during execution.
- */
- @Test
- @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
- public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- setUpPowerManager(backupManagerService);
- ShadowBinder.setCallingUid(1);
-
- expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
- assertThat(ShadowKeyValueBackupJobException.getCallingUid())
- .isEqualTo(ShadowBinder.LOCAL_UID);
- assertThat(Binder.getCallingUid()).isEqualTo(1);
- }
-
- private BackupManagerService createBackupManagerServiceForRequestBackup() {
- BackupManagerService backupManagerService = createInitializedBackupManagerService();
- backupManagerService.setEnabled(true);
- backupManagerService.setProvisioned(true);
- return backupManagerService;
- }
-
- /**
- * Test verifying that {@link BackupManagerService#BackupManagerService(Context, Trampoline,
- * HandlerThread, File, File, TransportManager)} posts a transport registration task to the
- * backup handler thread.
- */
- @Test
- public void testConstructor_postRegisterTransports() {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
- createBackupManagerService();
-
- mShadowBackupLooper.runToEndOfTasks();
- verify(mTransportManager).registerTransports();
- }
-
- /**
- * Test verifying that the {@link BackupManagerService#BackupManagerService(Context, Trampoline,
- * HandlerThread, File, File, TransportManager)} does not directly register transports in its
- * own thread.
- */
- @Test
- public void testConstructor_doesNotRegisterTransportsSynchronously() {
- mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
- createBackupManagerService();
-
- // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
- verify(mTransportManager, never()).registerTransports();
- }
-
- private BackupManagerService createBackupManagerService() {
- return new BackupManagerService(
- mContext,
- new Trampoline(mContext),
- mBackupThread,
- mBaseStateDir,
- mDataDir,
- mTransportManager);
- }
-
- private BackupManagerService createInitializedBackupManagerService() {
- return BackupManagerServiceTestUtils.createInitializedBackupManagerService(
- mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
- }
-
- private void setUpPowerManager(BackupManagerService backupManagerService) {
- PowerManager powerManagerMock = mock(PowerManager.class);
- when(powerManagerMock.getPowerSaveState(anyInt()))
- .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
- backupManagerService.setPowerManager(powerManagerMock);
- }
-
- /**
- * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
- * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
- */
- @Implements(KeyValueBackupJob.class)
- public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
- /**
- * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
- * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
- */
- public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
- ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
- throw new IllegalArgumentException();
- }
+ verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
}
}
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
new file mode 100644
index 0000000..9d43819
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -0,0 +1,1039 @@
+/*
+ * 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.
+ */
+
+package com.android.server.backup;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+import static com.android.server.backup.testing.TransportData.localTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.app.backup.BackupManager;
+import android.app.backup.IBackupObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.PowerManager;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.testing.shadows.ShadowAppBackupUtils;
+import com.android.server.testing.shadows.ShadowBinder;
+import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
+import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowContextWrapper;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.shadows.ShadowSettings;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Tests for the per-user instance of the backup/restore system service {@link
+ * UserBackupManagerService} that performs operations for its target user.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowAppBackupUtils.class})
+@Presubmit
+public class UserBackupManagerServiceTest {
+ private static final String TAG = "BMSTest";
+ private static final String PACKAGE_1 = "some.package.1";
+ private static final String PACKAGE_2 = "some.package.2";
+
+ @Mock private TransportManager mTransportManager;
+ private HandlerThread mBackupThread;
+ private ShadowLooper mShadowBackupLooper;
+ private File mBaseStateDir;
+ private File mDataDir;
+ private ShadowContextWrapper mShadowContext;
+ private Context mContext;
+ private TransportData mTransport;
+ private String mTransportName;
+ private ShadowPackageManager mShadowPackageManager;
+
+ /**
+ * Initialize state that {@link UserBackupManagerService} operations interact with. This
+ * includes setting up the transport, starting the backup thread, and creating backup data
+ * directories.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTransport = backupTransport();
+ mTransportName = mTransport.transportName;
+
+ // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
+ // we should not fail tests because of this. This is not flakiness, the exceptions thrown
+ // don't interfere with the tests.
+ mBackupThread = startSilentBackupThread(TAG);
+ mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
+
+ ContextWrapper context = RuntimeEnvironment.application;
+ mShadowPackageManager = shadowOf(context.getPackageManager());
+ mContext = context;
+ mShadowContext = shadowOf(context);
+
+ File cacheDir = mContext.getCacheDir();
+ // Corresponds to /data/backup
+ mBaseStateDir = new File(cacheDir, "base_state");
+ // Corresponds to /cache/backup_stage
+ mDataDir = new File(cacheDir, "data");
+ }
+
+ /**
+ * Clean up and reset state that was created for testing {@link UserBackupManagerService}
+ * operations.
+ */
+ @After
+ public void tearDown() throws Exception {
+ mBackupThread.quit();
+ ShadowAppBackupUtils.reset();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns the
+ * current destination string of inputted transport if the transport is registered.
+ */
+ @Test
+ public void testDestinationString() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenReturn("destinationString");
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String destination = backupManagerService.getDestinationString(mTransportName);
+
+ assertThat(destination).isEqualTo("destinationString");
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns
+ * {@code null} if the inputted transport is not registered.
+ */
+ @Test
+ public void testDestinationString_whenTransportNotRegistered() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenThrow(TransportNotRegisteredException.class);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String destination = backupManagerService.getDestinationString(mTransportName);
+
+ assertThat(destination).isNull();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} throws a
+ * {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testDestinationString_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+ .thenThrow(TransportNotRegisteredException.class);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.getDestinationString(mTransportName));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
+ * {@code false} when the given app is not eligible for backup.
+ */
+ @Test
+ public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+
+ assertThat(result).isFalse();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
+ * {@code true} when the given app is eligible for backup.
+ */
+ @Test
+ public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
+ ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+
+ assertThat(result).isTrue();
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} throws a
+ * {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+ * returns an {@code array} of only apps that are eligible for backup from an {@array} of
+ * inputted apps.
+ */
+ @Test
+ public void testFilterAppsEligibleForBackup() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
+ ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String[] filtered =
+ backupManagerService.filterAppsEligibleForBackup(
+ new String[] {PACKAGE_1, PACKAGE_2});
+
+ assertThat(filtered).asList().containsExactly(PACKAGE_1);
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(transportMock.transportClient), any());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+ * returns an empty {@code array} if no inputted apps are eligible for backup.
+ */
+ @Test
+ public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String[] filtered =
+ backupManagerService.filterAppsEligibleForBackup(
+ new String[] {PACKAGE_1, PACKAGE_2});
+
+ assertThat(filtered).isEmpty();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+ * throws a {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.filterAppsEligibleForBackup(
+ new String[] {PACKAGE_1, PACKAGE_2}));
+ }
+
+ /* Tests for select transport */
+
+ private ComponentName mNewTransportComponent;
+ private TransportData mNewTransport;
+ private TransportMock mNewTransportMock;
+ private TransportData mOldTransport;
+ private TransportMock mOldTransportMock;
+
+ private void setUpForSelectTransport() throws Exception {
+ mNewTransport = backupTransport();
+ mNewTransportComponent = mNewTransport.getTransportComponent();
+ mOldTransport = d2dTransport();
+ List<TransportMock> transportMocks =
+ setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
+ mNewTransportMock = transportMocks.get(0);
+ mOldTransportMock = transportMocks.get(1);
+ when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
+ .thenReturn(mOldTransport.transportName);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)}
+ * successfully switches the current transport to the inputted transport, returns the name of
+ * the old transport, and disposes of the transport client after the operation.
+ */
+ @Test
+ public void testSelectBackupTransport() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ String oldTransport =
+ backupManagerService.selectBackupTransport(mNewTransport.transportName);
+
+ assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+ assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)} throws a
+ * {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testSelectBackupTransport_withoutPermission() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
+ * transport and disposes of the transport client after the operation.
+ */
+ @Test
+ public void testSelectBackupTransportAsync() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+ verify(callback).onSuccess(eq(mNewTransport.transportName));
+ verify(mTransportManager)
+ .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
+ * transport and notifies the inputted callback of failure when it fails to register the
+ * transport.
+ */
+ @Test
+ public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+ .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
+ verify(callback).onFailure(anyInt());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
+ * transport and notifies the inputted callback of failure when the transport gets unregistered.
+ */
+ @Test
+ public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
+ setUpTransports(mTransportManager, mTransport.unregistered());
+ ComponentName newTransportComponent = mTransport.getTransportComponent();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
+ .thenReturn(BackupManager.SUCCESS);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
+ verify(callback).onFailure(anyInt());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
+ * have backup permission.
+ */
+ @Test
+ public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
+ setUpForSelectTransport();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ ComponentName newTransportComponent = mNewTransport.getTransportComponent();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.selectBackupTransportAsync(
+ newTransportComponent, mock(ISelectBackupTransportCallback.class)));
+ }
+
+ private String getSettingsTransport() {
+ return ShadowSettings.ShadowSecure.getString(
+ mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+ * the {@link ComponentName} of the currently selected transport.
+ */
+ @Test
+ public void testGetCurrentTransportComponent() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getCurrentTransportComponent())
+ .thenReturn(mTransport.getTransportComponent());
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+ assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+ * {@code null} if there is no currently selected transport.
+ */
+ @Test
+ public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+ assertThat(transportComponent).isNull();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+ * {@code null} if the currently selected transport is not registered.
+ */
+ @Test
+ public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ when(mTransportManager.getCurrentTransportComponent())
+ .thenThrow(TransportNotRegisteredException.class);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+ assertThat(transportComponent).isNull();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} throws a
+ * {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
+ }
+
+ /* Tests for updating transport attributes */
+
+ private static final int PACKAGE_UID = 10;
+ private ComponentName mTransportComponent;
+ private int mTransportUid;
+
+ private void setUpForUpdateTransportAttributes() throws Exception {
+ mTransportComponent = mTransport.getTransportComponent();
+ String transportPackage = mTransportComponent.getPackageName();
+
+ ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
+ shadowPackageManager.addPackage(transportPackage);
+ shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
+
+ mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} succeeds if the uid of the transport
+ * is same as the uid of the caller.
+ */
+ @Test
+ public void
+ testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mTransportManager)
+ .updateTransportAttributes(
+ eq(mTransportComponent),
+ eq(mTransportName),
+ eq(configurationIntent),
+ eq("currentDestinationString"),
+ eq(dataManagementIntent),
+ eq("dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
+ * the uid of the transport is not equal to the uid of the caller.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid + 1,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+ * given a {@code null} transport component.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ null,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+ * given a {@code null} transport name.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ null,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+ * given a {@code null} destination string.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ null,
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+ * given either a {@code null} data management label or {@code null} data management intent, but
+ * not both.
+ */
+ @Test
+ public void
+ testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ null,
+ "dataManagementLabel"));
+
+ expectThrows(
+ RuntimeException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ null));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} succeeds if the caller has backup
+ * permission.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mTransportManager)
+ .updateTransportAttributes(
+ eq(mTransportComponent),
+ eq(mTransportName),
+ eq(configurationIntent),
+ eq("currentDestinationString"),
+ eq(dataManagementIntent),
+ eq("dataManagementLabel"));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+ * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
+ * the caller does not have backup permission.
+ */
+ @Test
+ public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
+ throws Exception {
+ setUpForUpdateTransportAttributes();
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.updateTransportAttributes(
+ mTransportUid,
+ mTransportComponent,
+ mTransportName,
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel"));
+ }
+
+ /* Tests for request backup */
+
+ @Mock private IBackupObserver mObserver;
+
+ private void setUpForRequestBackup(String... packages) throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ for (String packageName : packages) {
+ mShadowPackageManager.addPackage(packageName);
+ ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
+ }
+ setUpCurrentTransport(mTransportManager, mTransport);
+ }
+
+ private void tearDownForRequestBackup() {
+ ShadowKeyValueBackupTask.reset();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} throws a {@link SecurityException} if the caller does not have backup permission.
+ */
+ @Test
+ public void testRequestBackup_whenPermissionDenied() throws Exception {
+ mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
+ */
+ @Test
+ public void testRequestBackup_whenPackagesNull() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> backupManagerService.requestBackup(null, mObserver, 0));
+ verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
+ * packages.
+ */
+ @Test
+ public void testRequestBackup_whenPackagesEmpty() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
+ verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
+ */
+ @Test
+ public void testRequestBackup_whenBackupDisabled() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setEnabled(false);
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
+ * through SUW.
+ */
+ @Test
+ public void testRequestBackup_whenNotProvisioned() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setProvisioned(false);
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
+ * registered.
+ */
+ @Test
+ public void testRequestBackup_whenTransportNotRegistered() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ setUpCurrentTransport(mTransportManager, mTransport.unregistered());
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setEnabled(true);
+ backupManagerService.setProvisioned(true);
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
+ verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
+ * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
+ */
+ @Test
+ public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ mShadowPackageManager.addPackage(PACKAGE_1);
+ setUpCurrentTransport(mTransportManager, mTransport);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setEnabled(true);
+ backupManagerService.setProvisioned(true);
+ // Haven't set PACKAGE_1 as eligible
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ assertThat(result).isEqualTo(BackupManager.SUCCESS);
+ verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
+ tearDownForRequestBackup();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
+ * package succeeds.
+ */
+ @Test
+ @Config(shadows = ShadowKeyValueBackupTask.class)
+ public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
+ setUpForRequestBackup(PACKAGE_1);
+ UserBackupManagerService backupManagerService =
+ createBackupManagerServiceForRequestBackup();
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(result).isEqualTo(BackupManager.SUCCESS);
+ ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
+ assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
+ assertThat(shadowTask.getPendingFullBackups()).isEmpty();
+ // TODO: Assert more about KeyValueBackupTask
+ tearDownForRequestBackup();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+ * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
+ * backup package succeeds.
+ */
+ @Test
+ @Config(shadows = ShadowKeyValueBackupTask.class)
+ public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
+ setUpForRequestBackup(PACKAGE_1);
+ ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
+ UserBackupManagerService backupManagerService =
+ createBackupManagerServiceForRequestBackup();
+
+ int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+ mShadowBackupLooper.runToEndOfTasks();
+ assertThat(result).isEqualTo(BackupManager.SUCCESS);
+ ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
+ assertThat(shadowTask.getQueue()).isEmpty();
+ assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
+ // TODO: Assert more about KeyValueBackupTask
+ tearDownForRequestBackup();
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#backupNow()} clears the calling identity
+ * for scheduling a job and then restores the original calling identity after the operation.
+ */
+ @Test
+ @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
+ public void testBackupNow_clearsCallingIdentityForJobScheduler() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ setUpPowerManager(backupManagerService);
+ ShadowBinder.setCallingUid(1);
+
+ backupManagerService.backupNow();
+
+ assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
+ assertThat(Binder.getCallingUid()).isEqualTo(1);
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#backupNow()} restores the original
+ * calling identity if an exception is thrown during execution.
+ */
+ @Test
+ @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
+ public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ setUpPowerManager(backupManagerService);
+ ShadowBinder.setCallingUid(1);
+
+ expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
+ assertThat(ShadowKeyValueBackupJobException.getCallingUid())
+ .isEqualTo(ShadowBinder.LOCAL_UID);
+ assertThat(Binder.getCallingUid()).isEqualTo(1);
+ }
+
+ private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
+ UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+ backupManagerService.setEnabled(true);
+ backupManagerService.setProvisioned(true);
+ return backupManagerService;
+ }
+
+ /**
+ * Test verifying that {@link UserBackupManagerService#UserBackupManagerService(Context,
+ * Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
+ * to the backup handler thread.
+ */
+ @Test
+ public void testConstructor_postRegisterTransports() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+ createBackupManagerService();
+
+ mShadowBackupLooper.runToEndOfTasks();
+ verify(mTransportManager).registerTransports();
+ }
+
+ /**
+ * Test verifying that the {@link UserBackupManagerService#UserBackupManagerService(Context,
+ * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
+ * transports in its own thread.
+ */
+ @Test
+ public void testConstructor_doesNotRegisterTransportsSynchronously() {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+ createBackupManagerService();
+
+ // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
+ verify(mTransportManager, never()).registerTransports();
+ }
+
+ private UserBackupManagerService createBackupManagerService() {
+ return new UserBackupManagerService(
+ mContext,
+ new Trampoline(mContext),
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+ }
+
+ private UserBackupManagerService createInitializedBackupManagerService() {
+ return BackupManagerServiceTestUtils.createInitializedUserBackupManagerService(
+ mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+ }
+
+ private void setUpPowerManager(UserBackupManagerService backupManagerService) {
+ PowerManager powerManagerMock = mock(PowerManager.class);
+ when(powerManagerMock.getPowerSaveState(anyInt()))
+ .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+ backupManagerService.setPowerManager(powerManagerMock);
+ }
+
+ /**
+ * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
+ * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
+ */
+ @Implements(KeyValueBackupJob.class)
+ public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
+ /**
+ * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
+ * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
+ */
+ public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+ ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+ throw new IllegalArgumentException();
+ }
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index fd7ced2..423512c 100644
--- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -1,10 +1,10 @@
package com.android.server.backup.fullbackup;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index 6ee6eb6..a14cc51 100644
--- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -46,6 +46,7 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
@@ -71,7 +72,7 @@
@Config(shadows = ShadowSlog.class)
@Presubmit
public class PerformInitializeTaskTest {
- @Mock private BackupManagerService mBackupManagerService;
+ @Mock private UserBackupManagerService mBackupManagerService;
@Mock private TransportManager mTransportManager;
@Mock private OnTaskFinishedListener mListener;
@Mock private IBackupTransport mTransportBinder;
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
index a0afb5e..a1b8a95 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
@@ -26,7 +26,7 @@
import android.platform.test.annotations.Presubmit;
import android.util.Log;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.testing.shadows.ShadowEventLog;
import com.android.server.testing.shadows.ShadowSlog;
@@ -41,7 +41,7 @@
@Config(shadows = {ShadowEventLog.class, ShadowSlog.class})
@Presubmit
public class KeyValueBackupReporterTest {
- @Mock private BackupManagerService mBackupManagerService;
+ @Mock private UserBackupManagerService mBackupManagerService;
@Mock private IBackupObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index a69f007..1aa4999 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -26,7 +26,7 @@
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils
- .createInitializedBackupManagerService;
+ .createInitializedUserBackupManagerService;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils
.setUpBackupManagerServiceBasics;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils
@@ -103,12 +103,12 @@
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
import com.android.server.backup.PackageManagerBackupAgent;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.remote.RemoteCall;
@@ -178,7 +178,7 @@
@Mock private IBackupObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
@Mock private OnTaskFinishedListener mListener;
- private BackupManagerService mBackupManagerService;
+ private UserBackupManagerService mBackupManagerService;
private TransportData mTransport;
private ShadowLooper mShadowBackupLooper;
private Handler mBackupHandler;
@@ -227,7 +227,7 @@
setUpBinderCallerAndApplicationAsSystem(mApplication);
mBackupManagerService =
spy(
- createInitializedBackupManagerService(
+ createInitializedUserBackupManagerService(
mContext, mBaseStateDir, mDataDir, mTransportManager));
setUpBackupManagerServiceBasics(
mBackupManagerService,
@@ -720,7 +720,7 @@
}
/**
- * Agent unavailable means {@link BackupManagerService#bindToAgentSynchronous(ApplicationInfo,
+ * Agent unavailable means {@link UserBackupManagerService#bindToAgentSynchronous(ApplicationInfo,
* int)} returns {@code null}.
*
* @see #setUpAgent(PackageData)
@@ -2597,7 +2597,7 @@
*
* <ul>
* <li>The transport being initialized with {@link IBackupTransport#initializeDevice()}
- * <li>{@link BackupManagerService#resetBackupState(File)} being called, which will:
+ * <li>{@link UserBackupManagerService#resetBackupState(File)} being called, which will:
* <ul>
* <li>Reset processed packages journal.
* <li>Reset current token to 0.
@@ -2617,7 +2617,7 @@
/**
* Forces transport initialization and call to {@link
- * BackupManagerService#resetBackupState(File)}
+ * UserBackupManagerService#resetBackupState(File)}
*/
private void deletePmStateFile() throws IOException {
Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE));
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 0e2b95b..859392d 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -51,8 +51,8 @@
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils;
@@ -85,7 +85,7 @@
private static final long TOKEN_1 = 1L;
private static final long TOKEN_2 = 2L;
- @Mock private BackupManagerService mBackupManagerService;
+ @Mock private UserBackupManagerService mBackupManagerService;
@Mock private TransportManager mTransportManager;
@Mock private IRestoreObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index f307730..bacc44e 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -37,9 +37,9 @@
import android.util.Log;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.Trampoline;
import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
import org.mockito.stubbing.Answer;
import org.robolectric.shadows.ShadowApplication;
@@ -49,26 +49,26 @@
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.atomic.AtomicReference;
-/** Test utils for {@link BackupManagerService} and friends. */
+/** Test utils for {@link UserBackupManagerService} and friends. */
public class BackupManagerServiceTestUtils {
/**
* If the class-under-test is going to execute methods as the system, it's a good idea to also
* call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
*/
- public static BackupManagerService createInitializedBackupManagerService(
+ public static UserBackupManagerService createInitializedUserBackupManagerService(
Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
- return createInitializedBackupManagerService(
+ return createInitializedUserBackupManagerService(
context, startBackupThread(null), baseStateDir, dataDir, transportManager);
}
- public static BackupManagerService createInitializedBackupManagerService(
+ public static UserBackupManagerService createInitializedUserBackupManagerService(
Context context,
HandlerThread backupThread,
File baseStateDir,
File dataDir,
TransportManager transportManager) {
- BackupManagerService backupManagerService =
- new BackupManagerService(
+ UserBackupManagerService backupManagerService =
+ new UserBackupManagerService(
context,
new Trampoline(context),
backupThread,
@@ -80,15 +80,16 @@
}
/**
- * Sets up basic mocks for {@link BackupManagerService} mock. If {@code backupManagerService} is
- * a spy, make sure you provide in the arguments the same objects that the original object uses.
+ * Sets up basic mocks for {@link UserBackupManagerService} mock. If {@code
+ * backupManagerService} is a spy, make sure you provide in the arguments the same objects that
+ * the original object uses.
*
* <p>If the class-under-test is going to execute methods as the system, it's a good idea to
* also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)}.
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
public static void setUpBackupManagerServiceBasics(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
Application application,
TransportManager transportManager,
PackageManager packageManager,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
index ca80664..ac5d2da 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -18,8 +18,8 @@
import android.annotation.Nullable;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.keyvalue.KeyValueBackupReporter;
import com.android.server.backup.keyvalue.KeyValueBackupTask;
@@ -54,7 +54,7 @@
@Implementation
protected void __constructor__(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
String transportDirName,
List<String> queue,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 228d4eb..2cebbeb 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -21,7 +21,7 @@
import android.app.backup.IRestoreObserver;
import android.content.pm.PackageInfo;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
import com.android.server.backup.transport.TransportClient;
@@ -47,7 +47,7 @@
sLastShadow = null;
}
- private BackupManagerService mBackupManagerService;
+ private UserBackupManagerService mBackupManagerService;
@Nullable private PackageInfo mPackage;
private boolean mIsFullSystemRestore;
@Nullable private String[] mFilterSet;
@@ -55,7 +55,7 @@
@Implementation
protected void __constructor__(
- BackupManagerService backupManagerService,
+ UserBackupManagerService backupManagerService,
TransportClient transportClient,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 42f75f0..cf4d3a8 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -144,9 +144,6 @@
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
- <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
- android:showWhenLocked="true"/>
-
<activity android:name="com.android.server.pm.ShortcutTestActivity"
android:enabled="true" android:exported="true" />
@@ -207,12 +204,6 @@
</intent-filter>
</activity-alias>
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
- <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
-
<receiver android:name="com.android.server.appwidget.DummyAppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/services/tests/servicestests/res/raw/input_port_associations.xml b/services/tests/servicestests/res/raw/input_port_associations.xml
new file mode 100644
index 0000000..b10d541
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations.xml
@@ -0,0 +1,4 @@
+<ports>
+ <port display="0" input="USB1" />
+ <port display="1" input="USB2" />
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml b/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml
new file mode 100644
index 0000000..8eeb1f5
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml
@@ -0,0 +1,3 @@
+<ports>
+ <port display="a" input="USB1" />
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml b/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml
new file mode 100644
index 0000000..cf6e124
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml
@@ -0,0 +1,3 @@
+<ports>
+ <port Garbage data inside xml>
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/shortcut_share_targets.xml b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
new file mode 100644
index 0000000..ec696e9
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 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.
+ -->
+
+<!-- Test XML resource to read share-targets from, used in ShortcutManagerTest1.java -->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+ <shortcut
+ android:shortcutId="dummy_shortcut1"
+ android:enabled="true"
+ android:shortcutShortLabel="@string/shortcut_title1">
+ <intent
+ android:action="android.intent.action.VIEW"
+ android:targetPackage="com.test.somepackage"
+ android:targetClass="com.test.somepackage.someclass" />
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+
+ <!-- Valid share target definition -->
+ <share-target android:targetClass="com.test.directshare.TestActivity1">
+ <data
+ android:scheme="http"
+ android:host="www.google.com"
+ android:port="1234"
+ android:path="somePath"
+ android:pathPrefix="somePathPrefix"
+ android:pathPattern="somePathPattern"
+ android:mimeType="text/plain"/>
+ <category android:name="com.test.category.CATEGORY1"/>
+ <category android:name="com.test.category.CATEGORY2"/>
+ </share-target>
+
+ <!-- Share target missing data tag, will be dropped -->
+ <share-target android:targetClass="com.test.directshare.TestActivity">
+ <category android:name="com.test.category.CATEGORY2"/>
+ </share-target>
+
+ <!-- Share target missing target class, will be dropped -->
+ <share-target>
+ <data
+ android:scheme="file"
+ android:host="www.somehost.com"
+ android:port="1234"
+ android:mimeType="video/*"/>
+ <category android:name="com.test.category.CATEGORY3"/>
+ </share-target>
+
+ <shortcut
+ android:shortcutId="dummy_shortcut2"
+ android:enabled="true"
+ android:shortcutShortLabel="@string/shortcut_title1">
+ <intent
+ android:action="android.intent.action.VIEW"
+ android:targetPackage="com.test.somepackage"
+ android:targetClass="com.test.somepackage.someclass" />
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+
+ <!-- Share target missing category, will be dropped -->
+ <share-target android:targetClass="com.test.directshare.TestActivity">
+ <data
+ android:scheme="content"
+ android:mimeType="text/plain"/>
+ </share-target>
+
+ <!-- Valid share target definition -->
+ <share-target android:targetClass="com.test.directshare.TestActivity5">
+ <category android:name="com.test.category.CATEGORY5"/>
+ <category android:name="com.test.category.CATEGORY6"/>
+ <data android:mimeType="video/mp4"/>
+ <data
+ android:scheme="content"
+ android:mimeType="video/*"/>
+ </share-target>
+</shortcuts>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
index 81107cf..2a78b6f 100644
--- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -17,9 +17,10 @@
package com.android.server;
-import static org.mockito.Mockito.when;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
@@ -134,4 +135,44 @@
mContext.sendBroadcast(intentUnplugged);
assertThat(deviceState.isCharging()).isFalse();
}
+
+ @Test
+ public void correctlyTracksTimeOnBattery() throws Exception {
+ CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+ when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+
+ service.onStart();
+ CachedDeviceState.Readonly deviceState =
+ LocalServices.getService(CachedDeviceState.Readonly.class);
+
+ CachedDeviceState.TimeInStateStopwatch stopwatch =
+ deviceState.createTimeOnBatteryStopwatch();
+
+ // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+ assertThat(stopwatch.isRunning()).isFalse();
+ service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ assertThat(stopwatch.isRunning()).isTrue();
+ stopwatch.reset();
+
+ Thread.sleep(100);
+ assertThat(stopwatch.isRunning()).isTrue();
+ assertThat(stopwatch.getMillis()).isAtLeast(100L);
+
+ long timeOnBatteryBeforePluggedIn = stopwatch.getMillis();
+ Intent intentPluggedIn = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intentPluggedIn.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+ mContext.sendBroadcast(intentPluggedIn);
+
+ assertThat(stopwatch.getMillis()).isAtLeast(timeOnBatteryBeforePluggedIn);
+ assertThat(stopwatch.isRunning()).isFalse();
+
+ long timeOnBatteryAfterPluggedIn = stopwatch.getMillis();
+ Thread.sleep(20);
+ assertThat(stopwatch.getMillis()).isEqualTo(timeOnBatteryAfterPluggedIn);
+
+ stopwatch.reset();
+ assertThat(stopwatch.getMillis()).isEqualTo(0L);
+ assertThat(stopwatch.isRunning()).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index c7409d7..89c7b71 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -20,7 +20,6 @@
import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
-import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
@@ -197,23 +196,6 @@
}
@Test
- public void testParseMemoryMaxUsageFromMemCg_parsesCorrectValue() {
- assertEquals(1234, parseMemoryMaxUsageFromMemCg("1234"));
- }
-
- @Test
- public void testParseMemoryMaxUsageFromMemCg_emptyContents() {
- assertEquals(0, parseMemoryMaxUsageFromMemCg(""));
-
- assertEquals(0, parseMemoryMaxUsageFromMemCg(null));
- }
-
- @Test
- public void testParseMemoryMaxUsageFromMemCg_incorrectValue() {
- assertEquals(0, parseMemoryMaxUsageFromMemCg("memory"));
- }
-
- @Test
public void testParseMemoryStatFromProcfs_parsesCorrectValues() {
MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS);
assertEquals(1, stat.pgfault);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index d52051eec..479a19b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -34,7 +34,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.testutils.PackageManagerStub;
import org.junit.Before;
@@ -97,7 +97,7 @@
applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
applicationInfo.uid = Process.SYSTEM_UID;
applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
- applicationInfo.packageName = BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
+ applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
mPackageManagerStub);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 4774985..d43b677 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -51,8 +51,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.FileMetadata;
+import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.restore.PerformAdbRestoreTask;
import com.android.server.backup.restore.RestorePolicy;
import com.android.server.backup.testutils.PackageManagerStub;
@@ -150,7 +150,7 @@
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
- assertThat(fileMetadata.path).isEqualTo(BackupManagerService.BACKUP_MANIFEST_FILENAME);
+ assertThat(fileMetadata.path).isEqualTo(UserBackupManagerService.BACKUP_MANIFEST_FILENAME);
tarBackupReader.skipTarPadding(fileMetadata.size);
diff --git a/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java b/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java
new file mode 100644
index 0000000..636aa37
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+package com.android.server.input;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * atest ConfigurationProcessorTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ConfigurationProcessorTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ }
+
+ @Test
+ public void testGetInputPortAssociations() {
+ final int res = com.android.frameworks.servicestests.R.raw.input_port_associations;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ List<Pair<String, String>> associations = null;
+ try {
+ associations = ConfigurationProcessor.processInputPortAssociations(xml);
+ } catch (Exception e) {
+ fail("Could not process xml file for input associations");
+ }
+ assertNotNull(associations);
+ assertEquals(2, associations.size());
+ assertTrue(associations.contains(Pair.create("USB1", "0")));
+ assertTrue(associations.contains(Pair.create("USB2", "1")));
+ }
+
+ @Test
+ public void testGetInputPortAssociationsBadDisplayport() {
+ final int res =
+ com.android.frameworks.servicestests.R.raw.input_port_associations_bad_displayport;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ List<Pair<String, String>> associations = null;
+ try {
+ associations = ConfigurationProcessor.processInputPortAssociations(xml);
+ } catch (Exception e) {
+ fail("Could not process xml file for input associations");
+ }
+ assertNotNull(associations);
+ assertEquals(0, associations.size());
+ }
+
+ @Test
+ public void testGetInputPortAssociationsEmptyConfig() {
+ final int res = com.android.frameworks.servicestests.R.raw.input_port_associations_bad_xml;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ try {
+ ConfigurationProcessor.processInputPortAssociations(xml);
+ fail("Parsing should fail, because xml contains bad data");
+ } catch (Exception e) {
+ // This is expected
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java
new file mode 100644
index 0000000..fc2dcb9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.test.AndroidTestCase;
+
+import com.android.internal.util.HexDump;
+
+public class SP800DeriveTests extends AndroidTestCase {
+ public void testFixedInput() throws Exception {
+ // CAVP: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/key-derivation
+ byte[] keyBytes = HexDump.hexStringToByteArray(
+ "e204d6d466aad507ffaf6d6dab0a5b26"
+ + "152c9e21e764370464e360c8fbc765c6");
+ SP800Derive sk = new SP800Derive(keyBytes);
+ byte[] fixedInput = HexDump.hexStringToByteArray(
+ "7b03b98d9f94b899e591f3ef264b71b1"
+ + "93fba7043c7e953cde23bc5384bc1a62"
+ + "93580115fae3495fd845dadbd02bd645"
+ + "5cf48d0f62b33e62364a3a80");
+ byte[] res = sk.fixedInput(fixedInput);
+ assertEquals((
+ "770dfab6a6a4a4bee0257ff335213f78"
+ + "d8287b4fd537d5c1fffa956910e7c779").toUpperCase(), HexDump.toHexString(res));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 113ee2d..99b827c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.net;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
@@ -72,7 +72,6 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -142,12 +141,14 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
-import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkPolicyManagerService;
-import com.android.server.net.NetworkStatsManagerInternal;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
import com.google.common.util.concurrent.AbstractFuture;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -162,9 +163,6 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
@@ -195,15 +193,6 @@
/**
* Tests for {@link NetworkPolicyManagerService}.
- *
- * <p>Typical usage:
- *
- * <pre><code>
- m -j32 FrameworksServicesTests && adb install -r -g \
- ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \
- adb shell am instrument -e class "com.android.server.NetworkPolicyManagerServiceTest" -w \
- "com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner"
- * </code></pre>
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -376,7 +365,7 @@
return null;
}
}).when(mActivityManager).registerUidObserver(any(), anyInt(),
- eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), isNull(String.class));
+ eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
@@ -425,7 +414,7 @@
// catch INetworkManagementEventObserver during systemReady()
final ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
- ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
+ ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
verify(mNetworkManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
@@ -843,6 +832,18 @@
assertTrue(mService.isUidForeground(UID_B));
}
+ @Test
+ public void testAppIdleTempWhitelisting() throws Exception {
+ mService.setAppIdleWhitelist(UID_A, true);
+ mService.setAppIdleWhitelist(UID_B, false);
+ int[] whitelistedIds = mService.getAppIdleWhitelist();
+ assertTrue(Arrays.binarySearch(whitelistedIds, UID_A) >= 0);
+ assertTrue(Arrays.binarySearch(whitelistedIds, UID_B) < 0);
+ assertFalse(mService.isUidIdle(UID_A));
+ // Can't currently guarantee UID_B's app idle state.
+ // TODO: expand with multiple app idle states.
+ }
+
private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
ZoneId.systemDefault());
@@ -1770,7 +1771,7 @@
}
private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes,
- long limitBytes, boolean inferred){
+ long limitBytes, boolean inferred) {
final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID);
return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes,
limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
@@ -1868,7 +1869,7 @@
}
private static void assertNotificationType(int expected, String actualTag) {
- assertEquals("notification type mismatch for '" + actualTag +"'",
+ assertEquals("notification type mismatch for '" + actualTag + "'",
Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
}
@@ -1902,7 +1903,8 @@
final String action = ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
final Intent intent = future.get(5, TimeUnit.SECONDS);
assertNotNull("Didn't get a " + action + "intent in 5 seconds");
- assertEquals("Wrong package on " + action + " intent", expectedPackage, intent.getPackage());
+ assertEquals("Wrong package on " + action + " intent",
+ expectedPackage, intent.getPackage());
}
// TODO: replace by Truth, Hamcrest, or a similar tool.
@@ -1923,7 +1925,7 @@
}
if (errors.length() > 0) {
fail("assertContainsInAnyOrder(expected=" + Arrays.toString(expected)
- + ", actual=" + Arrays.toString(actual) +") failed: \n" + errors);
+ + ", actual=" + Arrays.toString(actual) + ") failed: \n" + errors);
}
}
@@ -1986,7 +1988,7 @@
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Log.d(TAG,"counting down on answer: " + invocation);
+ Log.d(TAG, "counting down on answer: " + invocation);
latch.countDown();
return null;
}
@@ -2024,8 +2026,8 @@
final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml;
final File netConfigFile = new File(mPolicyDir, "netpolicy.xml");
Log.d(TAG, "Creating " + netConfigFile + " from asset " + assetPath);
- try (final InputStream in = context.getResources().getAssets().open(assetPath);
- final OutputStream out = new FileOutputStream(netConfigFile)) {
+ try (InputStream in = context.getResources().getAssets().open(assetPath);
+ OutputStream out = new FileOutputStream(netConfigFile)) {
Streams.copy(in, out);
}
}
@@ -2037,9 +2039,7 @@
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NetPolicyXml {
-
- public String value() default "";
-
+ String value() default "";
}
/**
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 a3348c2..1f5c64e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -91,8 +91,8 @@
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutUser.PackageWithUser;
-
import com.android.server.wm.ActivityTaskManagerInternal;
+
import org.junit.Assert;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
@@ -105,8 +105,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -762,6 +760,7 @@
LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
@@ -963,6 +962,10 @@
return getInstrumentation().getContext();
}
+ protected Context getClientContext() {
+ return mClientContext;
+ }
+
protected ShortcutManager getManager() {
return mManager;
}
@@ -1792,6 +1795,15 @@
}
/**
+ * @return all share targets stored internally for the caller.
+ */
+ protected List<ShareTargetInfo> getCallerShareTargets() {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ return p == null ? null : p.getAllShareTargetsForTest();
+ }
+
+ /**
* @return all shortcuts owned by caller that are actually visible via ShortcutManager.
* See also {@link #getCallerShortcuts}.
*/
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index d798865..ce59e6e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -31,6 +31,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
+import android.content.pm.UsesPermissionInfo;
import android.os.Bundle;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -464,6 +465,7 @@
pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
pkg.requestedPermissions.add("foo7");
+ pkg.usesPermissionInfos.add(new UsesPermissionInfo("foo7"));
pkg.implicitPermissions.add("foo25");
pkg.protectedBroadcasts = new ArrayList<>();
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index fa73447..3172afb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -106,6 +106,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.BiConsumer;
@@ -398,7 +399,7 @@
assertEquals(3, mManager.getRemainingCallCount());
}
- public void testPublishWithNoActivity() {
+ public void testPublishWithNoActivity() {
// If activity is not explicitly set, use the default one.
mRunningUsers.put(USER_10, true);
@@ -8015,4 +8016,56 @@
assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
}
+
+ public void testParseShareTargetsFromManifest() {
+ // These values must exactly match the content of shortcuts_share_targets.xml resource
+ List<ShareTargetInfo> expectedValues = new ArrayList<>();
+ expectedValues.add(new ShareTargetInfo(
+ new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
+ "http", "www.google.com", "1234", "somePath", "somePathPattern",
+ "somePathPrefix", "text/plain")}, "com.test.directshare.TestActivity1",
+ new String[]{"com.test.category.CATEGORY1", "com.test.category.CATEGORY2"}));
+ expectedValues.add(new ShareTargetInfo(new ShareTargetInfo.TargetData[]{
+ new ShareTargetInfo.TargetData(null, null, null, null, null, null, "video/mp4"),
+ new ShareTargetInfo.TargetData("content", null, null, null, null, null, "video/*")},
+ "com.test.directshare.TestActivity5",
+ new String[]{"com.test.category.CATEGORY5", "com.test.category.CATEGORY6"}));
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_share_targets);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ List<ShareTargetInfo> shareTargets = getCallerShareTargets();
+
+ assertNotNull(shareTargets);
+ assertEquals(expectedValues.size(), shareTargets.size());
+
+ for (int i = 0; i < expectedValues.size(); i++) {
+ ShareTargetInfo expected = expectedValues.get(i);
+ ShareTargetInfo actual = shareTargets.get(i);
+
+ assertEquals(expected.mTargetData.length, actual.mTargetData.length);
+ for (int j = 0; j < expected.mTargetData.length; j++) {
+ assertEquals(expected.mTargetData[j].mScheme, actual.mTargetData[j].mScheme);
+ assertEquals(expected.mTargetData[j].mHost, actual.mTargetData[j].mHost);
+ assertEquals(expected.mTargetData[j].mPort, actual.mTargetData[j].mPort);
+ assertEquals(expected.mTargetData[j].mPath, actual.mTargetData[j].mPath);
+ assertEquals(expected.mTargetData[j].mPathPrefix,
+ actual.mTargetData[j].mPathPrefix);
+ assertEquals(expected.mTargetData[j].mPathPattern,
+ actual.mTargetData[j].mPathPattern);
+ assertEquals(expected.mTargetData[j].mMimeType, actual.mTargetData[j].mMimeType);
+ }
+
+ assertEquals(expected.mTargetClass, actual.mTargetClass);
+
+ assertEquals(expected.mCategories.length, actual.mCategories.length);
+ for (int j = 0; j < expected.mCategories.length; j++) {
+ assertEquals(expected.mCategories[j], actual.mCategories[j]);
+ }
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 76d52fd..9b59f91 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -144,8 +144,8 @@
assertExpectException(
IllegalArgumentException.class, "Short label must be provided", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
.build();
assertTrue(getManager().setDynamicShortcuts(list(si)));
});
@@ -153,15 +153,15 @@
// same for add.
assertExpectException(
IllegalArgumentException.class, "Short label must be provided", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
.build();
assertTrue(getManager().addDynamicShortcuts(list(si)));
});
assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
.setShortLabel("x")
.build();
assertTrue(getManager().setDynamicShortcuts(list(si)));
@@ -169,8 +169,8 @@
// same for add.
assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
.setShortLabel("x")
.build();
assertTrue(getManager().addDynamicShortcuts(list(si)));
@@ -178,7 +178,7 @@
assertExpectException(
IllegalStateException.class, "does not belong to package", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
.setActivity(new ComponentName("xxx", "s"))
.build();
assertTrue(getManager().setDynamicShortcuts(list(si)));
@@ -187,7 +187,7 @@
// same for add.
assertExpectException(
IllegalStateException.class, "does not belong to package", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
.setActivity(new ComponentName("xxx", "s"))
.build();
assertTrue(getManager().addDynamicShortcuts(list(si)));
@@ -198,24 +198,24 @@
assertExpectException(
IllegalStateException.class, "is not main", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext(), "s"))
.build();
assertTrue(getManager().setDynamicShortcuts(list(si)));
});
// For add
assertExpectException(
IllegalStateException.class, "is not main", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext(), "s"))
.build();
assertTrue(getManager().addDynamicShortcuts(list(si)));
});
// For update
assertExpectException(
IllegalStateException.class, "is not main", () -> {
- ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
- .setActivity(new ComponentName(getTestContext(), "s"))
+ ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+ .setActivity(new ComponentName(getClientContext(), "s"))
.build();
assertTrue(getManager().updateShortcuts(list(si)));
});
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 7cf7df13..c1963da 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -268,10 +268,10 @@
@Test
public void testGetCurrentStatus() throws RemoteException {
- int status = Temperature.THROTTLING_WARNING;
+ int status = Temperature.THROTTLING_EMERGENCY;
Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
mFakeHal.mCallback.onValues(newSkin);
- assertEquals(status, mService.mService.getCurrentStatus());
+ assertEquals(status, mService.mService.getCurrentThermalStatus());
}
@Test
@@ -294,6 +294,6 @@
assertEquals(0, mService.mService.getCurrentTemperatures().size());
assertEquals(0,
mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
- assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
+ assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
deleted file mode 100644
index 415b5d9..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.res.Configuration.EMPTY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link AppWindowContainerController}.
- *
- * atest FrameworksServicesTests:AppWindowContainerControllerTests
- */
-@FlakyTest(bugId = 74078662)
-@SmallTest
-@Presubmit
-public class AppWindowContainerControllerTests extends WindowTestsBase {
-
- private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
-
- @Test
- public void testRemoveContainer() {
- final WindowTestUtils.TestAppWindowContainerController controller =
- createAppWindowController();
-
- // Assert token was added to display.
- assertNotNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
- // Assert that the container was created and linked.
- assertNotNull(controller.mContainer);
-
- controller.removeContainer(mDisplayContent.getDisplayId());
-
- // Assert token was remove from display.
- assertNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
- // Assert that the container was removed.
- assertNull(controller.mContainer);
- }
-
- @Test
- public void testSetOrientation() {
- final WindowTestUtils.TestAppWindowContainerController controller =
- createAppWindowController();
-
- // Assert orientation is unspecified to start.
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
- controller.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getDisplayId(),
- EMPTY /* displayConfig */, false /* freezeScreenIfNeeded */);
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, controller.getOrientation());
-
- controller.removeContainer(mDisplayContent.getDisplayId());
- // Assert orientation is unspecified to after container is removed.
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
- // Reset display frozen state
- mWm.mDisplayFrozen = false;
- }
-
- private void assertHasStartingWindow(AppWindowToken atoken) {
- assertNotNull(atoken.startingSurface);
- assertNotNull(atoken.startingData);
- assertNotNull(atoken.startingWindow);
- }
-
- private void assertNoStartingWindow(AppWindowToken atoken) {
- assertNull(atoken.startingSurface);
- assertNull(atoken.startingWindow);
- assertNull(atoken.startingData);
- atoken.forAllWindows(windowState -> {
- assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
- }, true);
- }
-
- @Test
- public void testCreateRemoveStartingWindow() {
- final WindowTestUtils.TestAppWindowContainerController controller =
- createAppWindowController();
- controller.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- waitUntilHandlersIdle();
- final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent);
- assertHasStartingWindow(atoken);
- controller.removeStartingWindow();
- waitUntilHandlersIdle();
- assertNoStartingWindow(atoken);
- }
-
- @Test
- public void testAddRemoveRace() {
- // There was once a race condition between adding and removing starting windows
- for (int i = 0; i < 1000; i++) {
- final WindowTestUtils.TestAppWindowContainerController controller =
- createAppWindowController();
- controller.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- controller.removeStartingWindow();
- waitUntilHandlersIdle();
- assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
-
- controller.getAppWindowToken(
- mDisplayContent).getParent().getParent().removeImmediately();
- }
- }
-
- @Test
- public void testTransferStartingWindow() {
- final WindowTestUtils.TestAppWindowContainerController controller1 =
- createAppWindowController();
- final WindowTestUtils.TestAppWindowContainerController controller2 =
- createAppWindowController();
- controller1.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- waitUntilHandlersIdle();
- controller2.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
- true, true, false, true, false, false);
- waitUntilHandlersIdle();
- assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
- assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
- }
-
- @Test
- public void testTransferStartingWindowWhileCreating() {
- final WindowTestUtils.TestAppWindowContainerController controller1 =
- createAppWindowController();
- final WindowTestUtils.TestAppWindowContainerController controller2 =
- createAppWindowController();
- ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
-
- // Surprise, ...! Transfer window in the middle of the creation flow.
- controller2.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
- true, true, false, true, false, false);
- });
- controller1.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- waitUntilHandlersIdle();
- assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
- assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
- }
-
- @Test
- public void testTryTransferStartingWindowFromHiddenAboveToken() {
-
- // Add two tasks on top of each other.
- TestTaskWindowContainerController taskController =
- new WindowTestUtils.TestTaskWindowContainerController(this);
- final WindowTestUtils.TestAppWindowContainerController controllerTop =
- createAppWindowController(taskController);
- final WindowTestUtils.TestAppWindowContainerController controllerBottom =
- createAppWindowController(taskController);
-
- // Add a starting window.
- controllerTop.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- waitUntilHandlersIdle();
-
- // Make the top one invisible, and try transfering the starting window from the top to the
- // bottom one.
- controllerTop.setVisibility(false, false);
- controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
-
- // Assert that the bottom window now has the starting window.
- assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent));
- assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent));
- }
-
- @Test
- public void testReparent() {
- final StackWindowController stackController =
- createStackControllerOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTaskWindowContainerController taskController1 =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
- final WindowTestUtils.TestAppWindowContainerController appWindowController1 =
- createAppWindowController(taskController1);
- final WindowTestUtils.TestTaskWindowContainerController taskController2 =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
- final WindowTestUtils.TestAppWindowContainerController appWindowController2 =
- createAppWindowController(taskController2);
- final WindowTestUtils.TestTaskWindowContainerController taskController3 =
- new WindowTestUtils.TestTaskWindowContainerController(stackController);
-
- try {
- appWindowController1.reparent(taskController1, 0);
- fail("Should not be able to reparent to the same parent");
- } catch (IllegalArgumentException e) {
- // Expected
- }
-
- try {
- taskController3.setContainer(null);
- appWindowController1.reparent(taskController3, 0);
- fail("Should not be able to reparent to a task that doesn't have a container");
- } catch (IllegalArgumentException e) {
- // Expected
- }
-
- // Reparent the app window and ensure that it is moved
- appWindowController1.reparent(taskController2, 0);
- assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent());
- assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer)
- .positionInParent());
- assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer)
- .positionInParent());
- }
-
- private WindowTestUtils.TestAppWindowContainerController createAppWindowController() {
- return createAppWindowController(
- new WindowTestUtils.TestTaskWindowContainerController(this));
- }
-
- private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
- WindowTestUtils.TestTaskWindowContainerController taskController) {
- return new WindowTestUtils.TestAppWindowContainerController(taskController);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
deleted file mode 100644
index a91c5e7..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.wm;
-
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.wm.utils.WmDisplayCutout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
-
- private DisplayFrames mFrames;
- private WindowState mWindow;
- private int mRotation = ROTATION_0;
- private boolean mHasDisplayCutout;
-
- @Before
- public void setUp() throws Exception {
- updateDisplayFrames();
-
- mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
- // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
- // changing those frames.
- doNothing().when(mWindow).computeFrameLw();
-
- final WindowManager.LayoutParams attrs = mWindow.mAttrs;
- attrs.width = MATCH_PARENT;
- attrs.height = MATCH_PARENT;
- attrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- attrs.format = PixelFormat.TRANSLUCENT;
- }
-
- public void setRotation(int rotation) {
- mRotation = rotation;
- updateDisplayFrames();
- }
-
- public void addDisplayCutout() {
- mHasDisplayCutout = true;
- updateDisplayFrames();
- }
-
- private void updateDisplayFrames() {
- final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
- mHasDisplayCutout);
- mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
- }
-
- @Test
- public void layoutWindowLw_appDrawsBars() {
- mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_appWontDrawBars() {
- mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
- }
-
- @Test
- public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
- mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
- }
-
- @Test
- public void addingWindow_doesNotTamperWithSysuiFlags() {
- mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- assertEquals(0, mWindow.mAttrs.systemUiVisibility);
- assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout() {
- addDisplayCutout();
-
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_never() {
- addDisplayCutout();
-
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
- addDisplayCutout();
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreen() {
- addDisplayCutout();
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
- addDisplayCutout();
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
-
-
- @Test
- public void layoutWindowLw_withDisplayCutout_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_seascape() {
- addDisplayCutout();
- setRotation(ROTATION_270);
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
- addDisplayCutout();
-
- mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
- mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
- mWindow.mAttrs.width = DISPLAY_WIDTH;
- mWindow.mAttrs.height = DISPLAY_HEIGHT;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- }
-
- @Test
- public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
- addDisplayCutout();
- setRotation(ROTATION_90);
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- }
-
- @Test
- public void layoutHint_appWindow() {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
-
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
- false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
- outDisplayCutout);
-
- assertThat(outFrame, is(mFrames.mUnrestricted));
- assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
- assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- }
-
- @Test
- public void layoutHint_appWindowInTask() {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
- final Rect taskBounds = new Rect(100, 100, 200, 200);
-
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
-
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
- false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
- outDisplayCutout);
-
- assertThat(outFrame, is(taskBounds));
- assertThat(outContentInsets, is(new Rect()));
- assertThat(outStableInsets, is(new Rect()));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- }
-
- @Test
- public void layoutHint_appWindowInTask_outsideContentFrame() {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
- // Task is in the nav bar area (usually does not happen, but this is similar enough to the
- // possible overlap with the IME)
- final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
- 200, mFrames.mContent.bottom + 10);
-
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
-
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
- true /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
- outDisplayCutout);
-
- assertThat(outFrame, is(taskBounds));
- assertThat(outContentInsets, is(new Rect()));
- assertThat(outStableInsets, is(new Rect()));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- }
-
- /**
- * Asserts that {@code actual} is inset by the given amounts from the full display rect.
- *
- * Convenience wrapper for when only the top and bottom inset are non-zero.
- */
- private void assertInsetByTopBottom(Rect actual, int expectedInsetTop,
- int expectedInsetBottom) {
- assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
- }
-
- /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
- private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
- int expectedInsetRight, int expectedInsetBottom) {
- assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
- mFrames.mDisplayWidth - expectedInsetRight,
- mFrames.mDisplayHeight - expectedInsetBottom), actual);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
deleted file mode 100644
index 60c0459..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.AppOpsManager.OP_NONE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.IBinder;
-import android.view.IApplicationToken;
-import android.view.IWindow;
-import android.view.SurfaceControl.Transaction;
-import android.view.WindowManager;
-
-import org.mockito.invocation.InvocationOnMock;
-
-/**
- * A collection of static functions that can be referenced by other test packages to provide access
- * to WindowManager related test functionality.
- */
-public class WindowTestUtils {
- private static int sNextTaskId = 0;
-
- /**
- * Creates a mock instance of {@link StackWindowController}.
- */
- public static StackWindowController createMockStackWindowContainerController() {
- StackWindowController controller = mock(StackWindowController.class);
- controller.mContainer = mock(TestTaskStack.class);
-
- // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
- // to properly set bounds values in the configuration. We must mimick those actions here.
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final Configuration config = invocationOnMock.<Configuration>getArgument(7);
- final Rect bounds = invocationOnMock.<Rect>getArgument(0);
- config.windowConfiguration.setBounds(bounds);
- return null;
- }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
- anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
-
- return controller;
- }
-
- /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
- public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
- int userId) {
- synchronized (service.mGlobalLock) {
- final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
- new ActivityManager.TaskDescription(), null);
- stack.addTask(newTask, POSITION_TOP);
- return newTask;
- }
- }
-
- /**
- * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
- * normally be mocked out.
- */
- public static class TestTaskStack extends TaskStack {
- TestTaskStack(WindowManagerService service, int stackId) {
- super(service, stackId, null);
- }
-
- @Override
- void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) {
- // Do nothing.
- }
- }
-
- static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
- synchronized (dc.mService.mGlobalLock) {
- return new TestAppWindowToken(dc);
- }
- }
-
- /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
- public static class TestAppWindowToken extends AppWindowToken {
- boolean mOnTop = false;
- private Transaction mPendingTransactionOverride;
-
- private TestAppWindowToken(DisplayContent dc) {
- super(dc.mService, new IApplicationToken.Stub() {
- public String getName() {return null;}
- }, new ComponentName("", ""), false, dc, true /* fillsParent */);
- }
-
- TestAppWindowToken(WindowManagerService service, IApplicationToken token,
- ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
- long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
- int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
- boolean launchTaskBehind, boolean alwaysFocusable,
- AppWindowContainerController controller) {
- super(service, token, activityComponent, voiceInteraction, dc,
- inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
- orientation, rotationAnimationHint, configChanges, launchTaskBehind,
- alwaysFocusable, controller);
- }
-
- int getWindowsCount() {
- return mChildren.size();
- }
-
- boolean hasWindow(WindowState w) {
- return mChildren.contains(w);
- }
-
- WindowState getFirstChild() {
- return mChildren.peekFirst();
- }
-
- WindowState getLastChild() {
- return mChildren.peekLast();
- }
-
- int positionInParent() {
- return getParent().mChildren.indexOf(this);
- }
-
- void setIsOnTop(boolean onTop) {
- mOnTop = onTop;
- }
-
- @Override
- boolean isOnTop() {
- return mOnTop;
- }
-
- void setPendingTransaction(Transaction transaction) {
- mPendingTransactionOverride = transaction;
- }
-
- @Override
- public Transaction getPendingTransaction() {
- return mPendingTransactionOverride == null
- ? super.getPendingTransaction()
- : mPendingTransactionOverride;
- }
- }
-
- static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
- return createTestWindowToken(type, dc, false /* persistOnEmpty */);
- }
-
- static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
- boolean persistOnEmpty) {
- synchronized (dc.mService.mGlobalLock) {
- return new TestWindowToken(type, dc, persistOnEmpty);
- }
- }
-
- /* Used so we can gain access to some protected members of the {@link WindowToken} class */
- public static class TestWindowToken extends WindowToken {
-
- private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
- super(dc.mService, mock(IBinder.class), type, persistOnEmpty, dc,
- false /* ownerCanManageAppTokens */);
- }
-
- int getWindowsCount() {
- return mChildren.size();
- }
-
- boolean hasWindow(WindowState w) {
- return mChildren.contains(w);
- }
- }
-
- /* Used so we can gain access to some protected members of the {@link Task} class */
- public static class TestTask extends Task {
- boolean mShouldDeferRemoval = false;
- boolean mOnDisplayChangedCalled = false;
- private boolean mIsAnimating = false;
-
- TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
- int resizeMode, boolean supportsPictureInPicture,
- TaskWindowContainerController controller) {
- super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
- new ActivityManager.TaskDescription(), controller);
- }
-
- boolean shouldDeferRemoval() {
- return mShouldDeferRemoval;
- }
-
- int positionInParent() {
- return getParent().mChildren.indexOf(this);
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- super.onDisplayChanged(dc);
- mOnDisplayChangedCalled = true;
- }
-
- @Override
- boolean isSelfAnimating() {
- return mIsAnimating;
- }
-
- void setLocalIsAnimating(boolean isAnimating) {
- mIsAnimating = isAnimating;
- }
- }
-
- /**
- * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
- * class.
- */
- public static class TestTaskWindowContainerController extends TaskWindowContainerController {
-
- static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() {
- @Override
- public void registerConfigurationChangeListener(
- ConfigurationContainerListener listener) {
- }
-
- @Override
- public void unregisterConfigurationChangeListener(
- ConfigurationContainerListener listener) {
- }
-
- @Override
- public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
- }
-
- @Override
- public void requestResize(Rect bounds, int resizeMode) {
- }
- };
-
- TestTaskWindowContainerController(WindowTestsBase testsBase) {
- this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
- }
-
- TestTaskWindowContainerController(StackWindowController stackController) {
- super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */,
- RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/,
- true /* showForAllUsers */, new ActivityManager.TaskDescription(),
- stackController.mService);
- }
-
- @Override
- TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode,
- boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
- return new TestTask(taskId, stack, userId, mService, resizeMode,
- supportsPictureInPicture, this);
- }
- }
-
- public static class TestAppWindowContainerController extends AppWindowContainerController {
-
- final IApplicationToken mToken;
-
- TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
- this(taskController, new TestIApplicationToken());
- }
-
- TestAppWindowContainerController(TestTaskWindowContainerController taskController,
- IApplicationToken token) {
- super(taskController, token, new ComponentName("", "") /* activityComponent */,
- null /* listener */, 0 /* index */, SCREEN_ORIENTATION_UNSPECIFIED,
- true /* fullscreen */, true /* showForAllUsers */, 0 /* configChanges */,
- false /* voiceInteraction */, false /* launchTaskBehind */,
- false /* alwaysFocusable */, 0 /* targetSdkVersion */,
- 0 /* rotationAnimationHint */, 0 /* inputDispatchingTimeoutNanos */,
- taskController.mService);
- mToken = token;
- }
-
- @Override
- AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
- ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
- long inputDispatchingTimeoutNanos,
- boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
- int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
- boolean alwaysFocusable, AppWindowContainerController controller) {
- return new TestAppWindowToken(service, token, activityComponent, voiceInteraction, dc,
- inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
- orientation,
- rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
- controller);
- }
-
- AppWindowToken getAppWindowToken(DisplayContent dc) {
- return (AppWindowToken) dc.getWindowToken(mToken.asBinder());
- }
- }
-
- public static class TestIApplicationToken implements IApplicationToken {
-
- private final Binder mBinder = new Binder();
- @Override
- public IBinder asBinder() {
- return mBinder;
- }
- @Override
- public String getName() {
- return null;
- }
- }
-
- /** Used to track resize reports. */
- public static class TestWindowState extends WindowState {
- boolean resizeReported;
-
- TestWindowState(WindowManagerService service, Session session, IWindow window,
- WindowManager.LayoutParams attrs, WindowToken token) {
- super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0,
- false /* ownerCanAddInternalSystemWindow */);
- }
-
- @Override
- void reportResized() {
- super.reportResized();
- resizeReported = true;
- }
-
- @Override
- public boolean isGoneForLayoutLw() {
- return false;
- }
-
- @Override
- void updateResizingWindowIfNeeded() {
- // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
- // the system that it can actually update the window.
- boolean hadSurface = mHasSurface;
- mHasSurface = true;
-
- super.updateResizingWindowIfNeeded();
-
- mHasSurface = hadSurface;
- }
- }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9da204f..41d5a1c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -456,6 +456,31 @@
}
@Test
+ public void testNoBeepForImportanceDefaultInAutomotive() throws Exception {
+ mService.setIsAutomotive(true);
+
+ NotificationRecord r = getBeepyNotification();
+ r.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testBeepForImportanceHighInAutomotive() throws Exception {
+ mService.setIsAutomotive(true);
+
+ NotificationRecord r = getBeepyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyBeepLooped();
+ assertTrue(r.isInterruptive());
+ }
+
+ @Test
public void testNoInterruptionForMin() throws Exception {
NotificationRecord r = getBeepyNotification();
r.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index fcd29e1..d950360 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3711,4 +3711,23 @@
verify(mAssistants).notifyAssistantSuggestedReplySent(
eq(r.sbn), eq(reply), eq(generatedByAssistant));
}
+
+ @Test
+ public void testOnNotificationActionClick() {
+ final int actionIndex = 2;
+ final Notification.Action action =
+ new Notification.Action.Builder(null, "text", null).build();
+ final boolean generatedByAssistant = false;
+
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ NotificationVisibility notificationVisibility =
+ NotificationVisibility.obtain(r.getKey(), 1, 2, true);
+ mService.mNotificationDelegate.onNotificationActionClick(
+ 10, 10, r.getKey(), actionIndex, action, notificationVisibility,
+ generatedByAssistant);
+ verify(mAssistants).notifyAssistantActionClicked(
+ eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 38d8e39..6c7ede3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -41,6 +41,7 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AutomaticZenRule;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
@@ -1097,6 +1098,25 @@
assertFalse(Objects.equals(defaultRuleName, ruleAfterUpdating.name)); // update name
}
+ @Test
+ public void testAddAutomaticZenRule() {
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ new ComponentName("android", "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+
+ assertTrue(id != null);
+ ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id);
+ assertTrue(ruleInConfig != null);
+ assertEquals(zenRule.isEnabled(), ruleInConfig.enabled);
+ assertEquals(zenRule.isModified(), ruleInConfig.modified);
+ assertEquals(zenRule.getConditionId(), ruleInConfig.conditionId);
+ assertEquals(NotificationManager.zenModeFromInterruptionFilter(
+ zenRule.getInterruptionFilter(), -1), ruleInConfig.zenMode);
+ assertEquals(zenRule.getName(), ruleInConfig.name);
+ }
+
private void setupZenConfig() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f128b4e..3f3b996 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -34,9 +34,13 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.REORDER_TASKS" />
<application android:debuggable="true"
android:testOnly="true">
+ <uses-library android:name="android.test.mock" android:required="true" />
+
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index cb2a8ec..5bf3d2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -62,8 +62,9 @@
@Test
public void testLastFocusedStackIsUpdatedWhenMovingStack() {
// Create a stack at bottom.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack stack =
+ new StackBuilder(mRootActivityContainer).setOnTop(!ON_TOP).build();
final ActivityStack prevFocusedStack = display.getFocusedStack();
stack.moveToFront("moveStackToFront");
@@ -83,7 +84,7 @@
@Test
public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
// Create a pinned stack and move to front.
- final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
.setStack(pinnedStack).build();
@@ -96,7 +97,7 @@
// Create a fullscreen stack and move to front.
final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
- mSupervisor.getDefaultDisplay());
+ mRootActivityContainer.getDefaultDisplay());
fullscreenStack.moveToFront("moveFullscreenStackToFront");
// The focused stack should be the fullscreen stack.
@@ -138,7 +139,7 @@
final ActivityDisplay display = spy(createNewActivityDisplay());
doReturn(false).when(display).shouldDestroyContentOnRemove();
doReturn(true).when(display).supportsSystemDecorations();
- mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
// Put home stack on the display.
final ActivityStack homeStack = display.createStack(
@@ -175,14 +176,14 @@
*/
@Test
public void testTopRunningActivity() {
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = new StackBuilder(mSupervisor).build();
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
final ActivityRecord activity = stack.getTopActivity();
// Create empty stack on top.
final ActivityStack emptyStack =
- new StackBuilder(mSupervisor).setCreateActivity(false).build();
+ new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
// Make sure the top running activity is not affected when keyguard is not locked.
assertTopRunningActivity(activity, display);
@@ -225,7 +226,7 @@
*/
@Test
public void testAlwaysOnTopStackLocation() {
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
final ActivityStack alwaysOnTopStack = display.createStack(WINDOWING_MODE_FREEFORM,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index c7f0521..cac9cf6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -24,20 +24,30 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.timeout;
import android.content.Intent;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+
+import java.util.Arrays;
/**
* Tests for the {@link ActivityMetricsLaunchObserver} class.
@@ -51,6 +61,7 @@
public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
private ActivityMetricsLogger mActivityMetricsLogger;
private ActivityMetricsLaunchObserver mLaunchObserver;
+ private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
private TestActivityStack mStack;
private TaskRecord mTask;
@@ -61,33 +72,62 @@
public void setUpAMLO() throws Exception {
setupActivityTaskManagerService();
- mActivityMetricsLogger =
- new ActivityMetricsLogger(mSupervisor, mService.mContext, mService.mH.getLooper());
-
mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
- // TODO: Use ActivityMetricsLaunchObserverRegistry .
- java.lang.reflect.Field f =
- mActivityMetricsLogger.getClass().getDeclaredField("mLaunchObserver");
- f.setAccessible(true);
- f.set(mActivityMetricsLogger, mLaunchObserver);
+ // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
+ mActivityMetricsLogger = mSupervisor.getActivityMetricsLogger();
+
+ mLaunchObserverRegistry = mActivityMetricsLogger.getLaunchObserverRegistry();
+ mLaunchObserverRegistry.registerLaunchObserver(mLaunchObserver);
// Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
// This seems to be the easiest way to create an ActivityRecord.
- mStack = mSupervisor.getDefaultDisplay().createStack(
+ mStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
}
+ @After
+ public void tearDownAMLO() throws Exception {
+ if (mLaunchObserverRegistry != null) { // Don't NPE if setUp failed.
+ mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
+ }
+ }
+
+ static class ActivityRecordMatcher implements ArgumentMatcher</*@ActivityRecordProto*/ byte[]> {
+ private final @ActivityRecordProto byte[] mExpected;
+
+ public ActivityRecordMatcher(ActivityRecord activityRecord) {
+ mExpected = activityRecordToProto(activityRecord);
+ }
+
+ public boolean matches(@ActivityRecordProto byte[] actual) {
+ return Arrays.equals(mExpected, actual);
+ }
+ }
+
+ static @ActivityRecordProto byte[] activityRecordToProto(ActivityRecord record) {
+ return ActivityMetricsLogger.convertActivityRecordToProto(record);
+ }
+
+ static @ActivityRecordProto byte[] eqProto(ActivityRecord record) {
+ return argThat(new ActivityRecordMatcher(record));
+ }
+
+ static <T> T verifyAsync(T mock) {
+ // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
+ return verify(mock, timeout(100));
+ }
+
@Test
public void testOnIntentStarted() throws Exception {
Intent intent = new Intent("action 1");
mActivityMetricsLogger.notifyActivityLaunching(intent);
- verify(mLaunchObserver).onIntentStarted(eq(intent));
+ verifyAsync(mLaunchObserver).onIntentStarted(eq(intent));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -102,7 +142,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
activityRecord);
- verify(mLaunchObserver).onIntentFailed();
+ verifyAsync(mLaunchObserver).onIntentFailed();
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -113,7 +153,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
mActivityRecord);
- verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -127,7 +167,7 @@
mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
SystemClock.uptimeMillis());
- verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecord));
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -135,12 +175,12 @@
public void testOnActivityLaunchCancelled() throws Exception {
testOnActivityLaunched();
- mActivityRecord.nowVisible = true;
+ mActivityRecord.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord);
- verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecord));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecord));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -151,7 +191,7 @@
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
mActivityRecord);
- verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
// A second, distinct, activity launch is coalesced into the the current app launch sequence
mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
@@ -170,7 +210,7 @@
mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
SystemClock.uptimeMillis());
- verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecordTrampoline));
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -178,13 +218,26 @@
public void testOnActivityLaunchCancelledTrampoline() throws Exception {
testOnActivityLaunchedTrampoline();
- mActivityRecordTrampoline.nowVisible = true;
+ mActivityRecordTrampoline.mDrawn = true;
// Cannot time already-visible activities.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
mActivityRecordTrampoline);
- verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecordTrampoline));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecordTrampoline));
verifyNoMoreInteractions(mLaunchObserver);
}
+
+ @Test
+ public void testActivityRecordProtoIsNotTooBig() throws Exception {
+ // The ActivityRecordProto must not be too big, otherwise converting it at runtime
+ // will become prohibitively expensive.
+ assertWithMessage("mActivityRecord: %s", mActivityRecord).
+ that(activityRecordToProto(mActivityRecord).length).
+ isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+
+ assertWithMessage("mActivityRecordTrampoline: %s", mActivityRecordTrampoline).
+ that(activityRecordToProto(mActivityRecordTrampoline).length).
+ isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 170bd33..b6f1817 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -29,10 +29,9 @@
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static junit.framework.TestCase.assertNotNull;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -65,7 +64,7 @@
@Before
public void setUp() throws Exception {
setupActivityTaskManagerService();
- mStack = new StackBuilder(mSupervisor).build();
+ mStack = new StackBuilder(mRootActivityContainer).build();
mTask = mStack.getChildAt(0);
mActivity = mTask.getTopActivity();
}
@@ -86,7 +85,7 @@
public void testStackCleanupOnTaskRemoval() {
mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING);
// Stack should be gone on task removal.
- assertNull(mService.mStackSupervisor.getStack(mStack.mStackId));
+ assertNull(mService.mRootActivityContainer.getStack(mStack.mStackId));
}
@Test
@@ -116,7 +115,7 @@
assertFalse(pauseFound.value);
// Clear focused stack
- final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
when(display.getFocusedStack()).thenReturn(null);
// In the unfocused stack, the activity should move to paused.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 8a6d587..f7b5d26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -18,15 +18,8 @@
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
-import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -36,10 +29,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
-import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.wm.ActivityStackSupervisor
- .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static com.google.common.truth.Truth.assertThat;
@@ -54,12 +43,7 @@
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
-import android.app.ActivityOptions;
import android.app.WaitResult;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.graphics.Rect;
-import android.os.Build;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.MediumTest;
@@ -67,8 +51,6 @@
import org.junit.Before;
import org.junit.Test;
-import java.util.ArrayList;
-
/**
* Tests for the {@link ActivityStackSupervisor} class.
*
@@ -83,78 +65,11 @@
@Before
public void setUp() throws Exception {
setupActivityTaskManagerService();
- mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
/**
- * This test ensures that we do not try to restore a task based off an invalid task id. We
- * should expect {@code null} to be returned in this case.
- */
- @Test
- public void testRestoringInvalidTask() {
- ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
- TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
- assertNull(task);
- }
-
- /**
- * This test ensures that an existing task in the pinned stack is moved to the fullscreen
- * activity stack when a new task is added.
- */
- @Test
- public void testReplacingTaskInPinnedStack() {
- final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(mFullscreenStack).build();
- final TaskRecord firstTask = firstActivity.getTask();
-
- final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(mFullscreenStack).build();
- final TaskRecord secondTask = secondActivity.getTask();
-
- mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
-
- // Ensure full screen stack has both tasks.
- ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
-
- // Move first activity to pinned stack.
- final Rect sourceBounds = new Rect();
- mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
- 0f /*aspectRatio*/, "initialMove");
-
- final ActivityDisplay display = mFullscreenStack.getDisplay();
- ActivityStack pinnedStack = display.getPinnedStack();
- // Ensure a task has moved over.
- ensureStackPlacement(pinnedStack, firstTask);
- ensureStackPlacement(mFullscreenStack, secondTask);
-
- // Move second activity to pinned stack.
- mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
- 0f /*aspectRatio*/, "secondMove");
-
- // Need to get stacks again as a new instance might have been created.
- pinnedStack = display.getPinnedStack();
- mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- // Ensure stacks have swapped tasks.
- ensureStackPlacement(pinnedStack, secondTask);
- ensureStackPlacement(mFullscreenStack, firstTask);
- }
-
- private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
- final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
- assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
-
- if (tasks == null) {
- return;
- }
-
- for (TaskRecord task : tasks) {
- assertTrue(stackTasks.contains(task));
- }
- }
-
- /**
* Ensures that an activity is removed from the stopping activities list once it is resumed.
*/
@Test
@@ -179,7 +94,7 @@
// #notifyAll will be called on the ActivityManagerService. we must hold the object lock
// when this happens.
- synchronized (mSupervisor.mService.mGlobalLock) {
+ synchronized (mService.mGlobalLock) {
final WaitResult taskToFrontWait = new WaitResult();
mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
@@ -195,337 +110,7 @@
assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
- assertEquals(deliverToTopWait.who, firstActivity.realActivity);
+ assertEquals(deliverToTopWait.who, firstActivity.mActivityComponent);
}
}
-
- @Test
- public void testApplySleepTokensLocked() {
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = mock(ActivityStack.class);
- display.addChild(stack, 0 /* position */);
-
- // Make sure we wake and resume in the case the display is turning on and the keyguard is
- // not showing.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- false /* keyguardShowing */, true /* expectWakeFromSleep */,
- true /* expectResumeTopActivity */);
-
- // Make sure we wake and don't resume when the display is turning on and the keyguard is
- // showing.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- true /* keyguardShowing */, true /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
-
- // Make sure we wake and don't resume when the display is turning on and the keyguard is
- // not showing as unfocused.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, false /* isFocusedStack */,
- false /* keyguardShowing */, true /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
-
- // Should not do anything if the display state hasn't changed.
- verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- false /* keyguardShowing */, false /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
- }
-
- private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
- ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
- boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
- boolean expectResumeTopActivity) {
- reset(stack);
-
- doReturn(displayShouldSleep).when(display).shouldSleep();
- doReturn(displaySleeping).when(display).isSleeping();
- doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
-
- doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
- doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
- mSupervisor.applySleepTokensLocked(true);
- verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
- verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
- null /* target */, null /* targetOptions */);
- }
-
- /**
- * Verifies that removal of activity with task and stack is done correctly.
- */
- @Test
- public void testRemovingStackOnAppCrash() {
- final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
- final int originalStackCount = defaultDisplay.getChildCount();
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(stack).build();
-
- assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
-
- // Let's pretend that the app has crashed.
- firstActivity.app.setThread(null);
- mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
-
- // Verify that the stack was removed.
- assertEquals(originalStackCount, defaultDisplay.getChildCount());
- }
-
- @Test
- public void testFocusability() {
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(stack).build();
-
- // Under split screen primary we should be focusable when not minimized
- mService.mStackSupervisor.setDockedStackMinimized(false);
- assertTrue(stack.isFocusable());
- assertTrue(activity.isFocusable());
-
- // Under split screen primary we should not be focusable when minimized
- mService.mStackSupervisor.setDockedStackMinimized(true);
- assertFalse(stack.isFocusable());
- assertFalse(activity.isFocusable());
-
- final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(pinnedStack).build();
-
- // We should not be focusable when in pinned mode
- assertFalse(pinnedStack.isFocusable());
- assertFalse(pinnedActivity.isFocusable());
-
- // Add flag forcing focusability.
- pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
-
- // We should not be focusable when in pinned mode
- assertTrue(pinnedStack.isFocusable());
- assertTrue(pinnedActivity.isFocusable());
-
- // Without the overridding activity, stack should not be focusable.
- pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
- REMOVE_TASK_MODE_DESTROYING);
- assertFalse(pinnedStack.isFocusable());
- }
-
- /**
- * Verify that split-screen primary stack will be chosen if activity is launched that targets
- * split-screen secondary, but a matching existing instance is found on top of split-screen
- * primary stack.
- */
- @Test
- public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
- // Create primary split-screen stack with a task and an activity.
- final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
- .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
- final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
-
- // Find a launch stack for the top activity in split-screen primary, while requesting
- // split-screen secondary.
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
- final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
-
- // Assert that the primary stack is returned.
- assertEquals(primaryStack, result);
- }
-
- /**
- * Verify split-screen primary stack & task can resized by
- * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
- */
- @Test
- public void testResizeDockedStackForSplitScreenPrimary() {
- final Rect taskSize = new Rect(0, 0, 600, 600);
- final Rect stackSize = new Rect(0, 0, 300, 300);
-
- // Create primary split-screen stack with a task.
- final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
- .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
-
- // Resize dock stack.
- mService.resizeDockedStack(stackSize, taskSize, null, null, null);
-
- // Verify dock stack & its task bounds if is equal as resized result.
- assertEquals(primaryStack.getBounds(), stackSize);
- assertEquals(task.getBounds(), taskSize);
- }
-
- /**
- * Verify that home stack would be moved to front when the top activity is Recents.
- */
- @Test
- public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
- // Create stack/task on default display.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build();
- final TaskRecord targetTask = targetStack.getChildAt(0);
-
- // Create Recents on top of the display.
- final ActivityStack stack =
- new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build();
-
- final String reason = "findTaskToMoveToFront";
- mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
- false);
-
- verify(display).moveHomeStackToFront(contains(reason));
- }
-
- /**
- * Verify that home stack won't be moved to front if the top activity on other display is
- * Recents.
- */
- @Test
- public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
- // Create stack/task on default display.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-
- // Create Recents on secondary display.
- final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
- ActivityDisplay.POSITION_TOP);
- final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_RECENTS, true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
- new ActivityBuilder(mService).setTask(task).build();
-
- final String reason = "findTaskToMoveToFront";
- mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
- false);
-
- verify(display, never()).moveHomeStackToFront(contains(reason));
- }
-
- /**
- * Verify if a stack is not at the topmost position, it should be able to resume its activity if
- * the stack is the top focused.
- */
- @Test
- public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
- // Create a stack at bottom.
- final ActivityDisplay display = mSupervisor.getDefaultDisplay();
- final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */));
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
- final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
- display.positionChildAtBottom(targetStack);
-
- // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
- // is the current top focused stack.
- assertFalse(targetStack.isTopStackOnDisplay());
- doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
-
- // Use the stack as target to resume.
- mSupervisor.resumeFocusedStacksTopActivitiesLocked(
- targetStack, activity, null /* targetOptions */);
-
- // Verify the target stack should resume its activity.
- verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
- eq(activity), eq(null /* targetOptions */));
- }
-
- /**
- * Tests home activities that targeted sdk before Q cannot start on secondary display.
- */
- @Test
- public void testStartHomeTargetSdkBeforeQ() throws Exception {
- final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
- mSupervisor.addChild(secondDisplay, POSITION_TOP);
- doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
- final ActivityInfo info = new ActivityInfo();
- info.launchMode = LAUNCH_MULTIPLE;
- info.applicationInfo = new ApplicationInfo();
- info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- assertTrue(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
- false /* allowInstrumenting */));
-
- info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
- assertFalse(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
- false /* allowInstrumenting */));
- }
-
- /**
- * Tests that home activities can be started on the displays that supports system decorations.
- */
- @Test
- public void testStartHomeOnAllDisplays() {
- // Create secondary displays.
- final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
- mSupervisor.addChild(secondDisplay, POSITION_TOP);
- doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
- // Create mock tasks and other necessary mocks.
- TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
- final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
- TaskRecord.setTaskRecordFactory(factory);
- doAnswer(i -> taskBuilder.build()).when(factory)
- .create(any(), anyInt(), any(), any(), any(), any());
- doReturn(true).when(mService.mStackSupervisor)
- .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
- doReturn(true).when(mSupervisor).canStartHomeOnDisplay(any(), anyInt(), anyBoolean());
-
- mSupervisor.startHomeOnAllDisplays(0, "testStartHome");
-
- assertTrue(mSupervisor.getDefaultDisplay().getTopStack().isActivityTypeHome());
- assertNotNull(secondDisplay.getTopStack());
- assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
- }
-
- /**
- * Tests that home activities won't be started before booting when display added.
- */
- @Test
- public void testNotStartHomeBeforeBoot() {
- final int displayId = 1;
- final boolean isBooting = mService.mAmInternal.isBooting();
- final boolean isBooted = mService.mAmInternal.isBooted();
- try {
- mService.mAmInternal.setBooting(false);
- mService.mAmInternal.setBooted(false);
- mSupervisor.onDisplayAdded(displayId);
- verify(mSupervisor, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
- } finally {
- mService.mAmInternal.setBooting(isBooting);
- mService.mAmInternal.setBooted(isBooted);
- }
- }
-
- /**
- * Tests whether home can be started if being instrumented.
- */
- @Test
- public void testCanStartHomeWhenInstrumented() {
- final ActivityInfo info = new ActivityInfo();
- info.applicationInfo = new ApplicationInfo();
- final WindowProcessController app = mock(WindowProcessController.class);
- doReturn(app).when(mService).getProcessController(any(), anyInt());
-
- // Can not start home if we don't want to start home while home is being instrumented.
- doReturn(true).when(app).isInstrumenting();
- assertFalse(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
- false /* allowInstrumenting*/));
-
- // Can start home for other cases.
- assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
- true /* allowInstrumenting*/));
-
- doReturn(false).when(app).isInstrumenting();
- assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
- false /* allowInstrumenting*/));
- assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
- true /* allowInstrumenting*/));
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 2fe45b8..048384e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -74,7 +74,7 @@
@Before
public void setUp() throws Exception {
setupActivityTaskManagerService();
- mDefaultDisplay = mSupervisor.getDefaultDisplay();
+ mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
true /* onTop */));
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
@@ -112,7 +112,7 @@
r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
assertEquals(r, mStack.getResumedActivity());
- final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
@@ -130,7 +130,7 @@
r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
assertEquals(r, mStack.getResumedActivity());
- final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
@@ -233,14 +233,14 @@
.setStack(mStack)
.setUid(0)
.build();
- final TaskRecord task = r.getTask();
+ final TaskRecord task = r.getTaskRecord();
// Overlay must be for a different user to prevent recognizing a matching top activity
final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task)
.setUid(UserHandle.PER_USER_RANGE * 2).build();
taskOverlay.mTaskOverlay = true;
- final ActivityStackSupervisor.FindTaskResult result =
- new ActivityStackSupervisor.FindTaskResult();
+ final RootActivityContainer.FindTaskResult result =
+ new RootActivityContainer.FindTaskResult();
mStack.findTaskLocked(r, result);
assertEquals(r, task.getTopActivity(false /* includeOverlays */));
@@ -700,7 +700,7 @@
// should be destroyed immediately with updating configuration to restore original state.
final ActivityRecord activity1 = finishCurrentActivity(stack1);
assertEquals(DESTROYING, activity1.getState());
- verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+ verify(mRootActivityContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
eq(display.mDisplayId), anyBoolean(), anyBoolean());
}
@@ -778,7 +778,7 @@
final ActivityDisplay display = mock(ActivityDisplay.class);
final KeyguardController keyguardController = mSupervisor.getKeyguardController();
- doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
+ doReturn(display).when(mRootActivityContainer).getActivityDisplay(anyInt());
doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index 9d93c85..2ba2fdb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -74,7 +74,7 @@
final ActivityRecord activity = new ActivityBuilder(mService).build();
final ActivityRecord source = new ActivityBuilder(mService).build();
final int startFlags = random.nextInt();
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack stack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final WindowProcessController wpc = new WindowProcessController(mService,
mService.mContext.getApplicationInfo(), "name", 12345,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 27fa20b..350114c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -90,6 +90,8 @@
@Mock
private ActivityTaskManagerService mService;
@Mock
+ private RootActivityContainer mRootActivityContainer;
+ @Mock
private ActivityStackSupervisor mSupervisor;
@Mock
private DevicePolicyManagerInternal mDevicePolicyManager;
@@ -111,7 +113,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mService.mAmInternal = mAmInternal;
- mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
+ mInterceptor = new ActivityStartInterceptor(
+ mService, mSupervisor, mRootActivityContainer, mContext);
mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
TEST_START_FLAGS, TEST_CALLING_PACKAGE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 50aa541..5c918b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.START_ABORTED;
import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
@@ -69,6 +70,7 @@
import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
@@ -110,6 +112,8 @@
private static final int FAKE_CALLING_UID = 666;
private static final int FAKE_REAL_CALLING_UID = 667;
private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
+ private static final int UNIMPORTANT_UID = 12345;
+ private static final int UNIMPORTANT_UID2 = 12346;
@Before
public void setUp() throws Exception {
@@ -125,7 +129,7 @@
public void testUpdateLaunchBounds() {
// When in a non-resizeable stack, the task bounds should be updated.
final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
- .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+ .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
.build();
final Rect bounds = new Rect(10, 10, 100, 100);
@@ -136,7 +140,7 @@
// When in a resizeable stack, the stack bounds should be updated as well.
final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
- .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+ .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
.build();
assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
@@ -240,7 +244,7 @@
final ActivityRecord source = builder.build();
if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
- intent.setComponent(source.realActivity);
+ intent.setComponent(source.mActivityComponent);
}
if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
@@ -259,11 +263,11 @@
PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
doAnswer((inv) -> {
throw new RemoteException();
- }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
- any());
+ }).when(packageManager).activitySupportsIntent(
+ eq(source.mActivityComponent), eq(intent), any());
} else {
doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
- .when(packageManager).activitySupportsIntent(eq(source.realActivity),
+ .when(packageManager).activitySupportsIntent(eq(source.mActivityComponent),
eq(intent), any());
}
} catch (RemoteException e) {
@@ -314,7 +318,7 @@
* Creates a {@link ActivityStarter} with default parameters and necessary mocks.
*
* @param launchFlags The intent flags to launch activity.
- * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
+ * @param mockGetLaunchStack Whether to mock {@link RootActivityContainer#getLaunchStack} for
* always launching to the testing stack. Set to false when allowing
* the activity can be launched to any stack that is decided by real
* implementation.
@@ -323,14 +327,14 @@
private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
boolean mockGetLaunchStack) {
// always allow test to start activity.
- doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
+ doReturn(true).when(mSupervisor).checkStartAnyActivityPermission(
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
anyBoolean(), anyBoolean(), any(), any(), any());
// instrument the stack and task used.
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+ final TaskRecord task = new TaskBuilder(mSupervisor)
.setCreateStack(false)
.build();
@@ -343,9 +347,9 @@
if (mockGetLaunchStack) {
// Direct starter to use spy stack.
- doReturn(stack).when(mService.mStackSupervisor)
+ doReturn(stack).when(mRootActivityContainer)
.getLaunchStack(any(), any(), any(), anyBoolean());
- doReturn(stack).when(mService.mStackSupervisor)
+ doReturn(stack).when(mRootActivityContainer)
.getLaunchStack(any(), any(), any(), anyBoolean(), any());
}
@@ -427,7 +431,7 @@
.setCreateTask(true)
.build();
- focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ActivityRecord reusableActivity = new ActivityBuilder(mService)
.setCreateTask(true)
@@ -435,13 +439,13 @@
// Create reusable activity after entering split-screen so that it is the top secondary
// stack.
- reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
// Set focus back to primary.
- final ActivityStack focusStack = focusActivity.getStack();
+ final ActivityStack focusStack = focusActivity.getActivityStack();
focusStack.moveToFront("testSplitScreenDeliverToTop");
- doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+ doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt());
final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
@@ -464,16 +468,16 @@
.setCreateTask(true)
.build();
- reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
final ActivityRecord focusActivity = new ActivityBuilder(mService)
.setCreateTask(true)
.build();
// Enter split-screen. Primary stack should have focus.
- focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+ doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt());
final int result = starter.setReason("testSplitScreenMoveToFront").execute();
@@ -486,7 +490,7 @@
*/
@Test
public void testTaskModeViolation() {
- final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
((TestActivityDisplay) display).removeAllTasks();
assertNoTasks(display);
@@ -551,6 +555,123 @@
}
/**
+ * This test ensures that unsupported usecases aren't aborted when background starts are
+ * allowed.
+ */
+ @Test
+ public void testBackgroundActivityStartsAllowed_noStartsAborted() {
+ doReturn(true).when(mService).isBackgroundActivityStartsEnabled();
+
+ runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ }
+
+ /**
+ * This test ensures that unsupported usecases are aborted when background starts are
+ * disallowed.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() {
+ doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
+
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_unsupportedUsecase_aborted", true,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ }
+
+ /**
+ * This test ensures that supported usecases aren't aborted when background starts are
+ * disallowed.
+ * The scenarios each have only one condidion that makes them supported.
+ */
+ @Test
+ public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() {
+ doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
+
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false,
+ Process.ROOT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false,
+ Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_callingUidHasVisibleWindow_notAborted", false,
+ UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_callingUidProcessStateTop_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_realCallingUidHasVisibleWindow_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, true, PROCESS_STATE_TOP + 1,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_realCallingUidProcessStateTop_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP,
+ false, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_hasForegroundActivities_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ true, false);
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_callerIsRecents_notAborted", false,
+ UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, true);
+ }
+
+ private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
+ int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
+ int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
+ boolean hasForegroundActivities, boolean callerIsRecents) {
+ // window visibility
+ doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
+ callingUid);
+ doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager)
+ .isAnyWindowVisibleForUid(realCallingUid);
+ // process importance
+ doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid);
+ doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid);
+ // foreground activities
+ final IApplicationThread caller = mock(IApplicationThread.class);
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.uid = callingUid;
+ final WindowProcessController callerApp =
+ new WindowProcessController(mService, ai, null, callingUid, -1, null, null);
+ callerApp.setHasForegroundActivities(hasForegroundActivities);
+ doReturn(callerApp).when(mService).getProcessController(caller);
+ // caller is recents
+ RecentTasks recentTasks = mock(RecentTasks.class);
+ mService.mStackSupervisor.setRecentTasks(recentTasks);
+ doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
+
+ final ActivityOptions options = spy(ActivityOptions.makeBasic());
+ ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
+ .setCaller(caller)
+ .setCallingUid(callingUid)
+ .setRealCallingUid(realCallingUid)
+ .setActivityOptions(new SafeActivityOptions(options));
+
+ final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
+
+ assertEquals(ActivityStarter.getExternalResult(
+ shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
+ verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+ }
+
+ /**
* This test ensures that when starting an existing single task activity on secondary display
* which is not the top focused display, it should deliver new intent to the activity and not
* create a new stack.
@@ -562,7 +683,7 @@
// Create a secondary display at bottom.
final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
- mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
+ mRootActivityContainer.addChild(secondaryDisplay, POSITION_BOTTOM);
final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -600,7 +721,7 @@
// Create a secondary display with an activity.
final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
- mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
+ mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP);
final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index c2ab3ac..3a56419 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -34,7 +34,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -94,6 +93,7 @@
final TestInjector mTestInjector = new TestInjector();
ActivityTaskManagerService mService;
+ RootActivityContainer mRootActivityContainer;
ActivityStackSupervisor mSupervisor;
// Default package name
@@ -120,6 +120,7 @@
ActivityTaskManagerService createActivityTaskManagerService() {
mService = new TestActivityTaskManagerService(mContext);
mSupervisor = mService.mStackSupervisor;
+ mRootActivityContainer = mService.mRootActivityContainer;
return mService;
}
@@ -139,7 +140,7 @@
/** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
TestActivityDisplay addNewActivityDisplayAt(int position) {
final TestActivityDisplay display = createNewActivityDisplay();
- mSupervisor.addChild(display, position);
+ mRootActivityContainer.addChild(display, position);
return display;
}
@@ -231,7 +232,9 @@
aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
- activity.mWindowContainerController = mock(AppWindowContainerController.class);
+ spyOn(activity);
+ activity.mAppWindowToken = mock(AppWindowToken.class);
+ doNothing().when(activity).removeWindowContainer();
if (mTaskRecord != null) {
mTaskRecord.addActivityToTop(activity);
@@ -317,7 +320,7 @@
TaskRecord build() {
if (mStack == null && mCreateStack) {
- mStack = mSupervisor.getDefaultDisplay().createStack(
+ mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
@@ -375,6 +378,8 @@
// We keep the reference in order to prevent creating it twice.
ActivityStackSupervisor mTestStackSupervisor;
+ ActivityDisplay mDefaultDisplay;
+
TestActivityTaskManagerService(Context context) {
super(context);
spyOn(this);
@@ -390,28 +395,49 @@
final TestActivityManagerService am =
new TestActivityManagerService(mTestInjector, this);
- // Put a home stack on the default display, so that we'll always have something
- // focusable.
- final TestActivityStackSupervisor supervisor =
- (TestActivityStackSupervisor) mStackSupervisor;
- supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final TaskRecord task = new TaskBuilder(mStackSupervisor)
- .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
- new ActivityBuilder(this).setTask(task).build();
-
spyOn(getLifecycleManager());
spyOn(getLockTaskController());
doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+ // allow background activity starts by default
+ doReturn(true).when(this).isBackgroundActivityStartsEnabled();
}
- void setActivityManagerService(IntentFirewall intentFirewall,
- PendingIntentController intentController, ActivityManagerInternal amInternal,
- WindowManagerService wm) {
+ void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
+ ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) {
mAmInternal = amInternal;
- setActivityManagerService(intentFirewall, intentController);
+ initialize(intentFirewall, intentController, looper);
+ initRootActivityContainerMocks(wm);
setWindowManager(wm);
}
+ void initRootActivityContainerMocks(WindowManagerService wm) {
+ spyOn(mRootActivityContainer);
+ mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class));
+ mRootActivityContainer.mWindowManager = wm;
+ mRootActivityContainer.mDisplayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ doNothing().when(mRootActivityContainer).setWindowManager(any());
+ // Invoked during {@link ActivityStack} creation.
+ doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
+ // Always keep things awake.
+ doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
+ // Called when moving activity to pinned stack.
+ doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
+ anyBoolean());
+
+ // Create a default display and put a home stack on it so that we'll always have
+ // something focusable.
+ mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY);
+ spyOn(mDefaultDisplay);
+ mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP);
+ mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final TaskRecord task = new TaskBuilder(mStackSupervisor)
+ .setStack(mDefaultDisplay.getHomeStack()).build();
+ new ActivityBuilder(this).setTask(task).build();
+
+ doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay();
+ }
+
@Override
int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
return userId;
@@ -489,8 +515,8 @@
mWindowManager = prepareMockWindowManager();
mUgmInternal = mock(UriGrantsManagerInternal.class);
- atm.setActivityManagerService(mIntentFirewall, mPendingIntentController,
- new LocalService(), mWindowManager);
+ atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager,
+ testInjector.mHandlerThread.getLooper());
mActivityTaskManager = atm;
mAtmInternal = atm.mInternal;
@@ -508,25 +534,14 @@
* setup not available in the test environment. Also specifies an injector for
*/
protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
- private ActivityDisplay mDisplay;
private KeyguardController mKeyguardController;
TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
super(service, looper);
spyOn(this);
- mDisplayManager =
- (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
mWindowManager = prepareMockWindowManager();
mKeyguardController = mock(KeyguardController.class);
- setWindowContainerController(mock(RootWindowContainerController.class));
- // Invoked during {@link ActivityStack} creation.
- doNothing().when(this).updateUIDsPresentOnDisplay();
- // Always keep things awake.
- doReturn(true).when(this).hasAwakeDisplay();
- // Called when moving activity to pinned stack.
- doNothing().when(this).ensureActivitiesVisibleLocked(any(), anyInt(),
- anyBoolean());
// Do not schedule idle timeouts
doNothing().when(this).scheduleIdleTimeoutLocked(any());
// unit test version does not handle launch wake lock
@@ -537,24 +552,11 @@
}
@Override
- public void initialize() {
- super.initialize();
- mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
- spyOn(mDisplay);
- addChild(mDisplay, ActivityDisplay.POSITION_TOP);
- }
-
- @Override
public KeyguardController getKeyguardController() {
return mKeyguardController;
}
@Override
- ActivityDisplay getDefaultDisplay() {
- return mDisplay;
- }
-
- @Override
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
}
@@ -571,7 +573,7 @@
DisplayInfo info) {
if (displayId == DEFAULT_DISPLAY) {
return new TestActivityDisplay(supervisor,
- supervisor.mDisplayManager.getDisplay(displayId));
+ supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
}
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
info, DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -579,7 +581,7 @@
}
TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
- super(supervisor, display);
+ super(supervisor.mService.mRootActivityContainer, display);
// Normally this comes from display-properties as exposed by WM. Without that, just
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -590,7 +592,7 @@
@Override
<T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop) {
- return new StackBuilder(mSupervisor).setDisplay(this)
+ return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
.setWindowingMode(windowingMode).setActivityType(activityType)
.setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
}
@@ -732,8 +734,8 @@
}
}
- protected static class StackBuilder {
- private final ActivityStackSupervisor mSupervisor;
+ static class StackBuilder {
+ private final RootActivityContainer mRootActivityContainer;
private ActivityDisplay mDisplay;
private int mStackId = -1;
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -741,9 +743,9 @@
private boolean mOnTop = true;
private boolean mCreateActivity = true;
- StackBuilder(ActivityStackSupervisor supervisor) {
- mSupervisor = supervisor;
- mDisplay = mSupervisor.getDefaultDisplay();
+ StackBuilder(RootActivityContainer root) {
+ mRootActivityContainer = root;
+ mDisplay = mRootActivityContainer.getDefaultDisplay();
}
StackBuilder setWindowingMode(int windowingMode) {
@@ -780,7 +782,8 @@
<T extends ActivityStack> T build() {
final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
if (mWindowingMode == WINDOWING_MODE_PINNED) {
- return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) {
+ return (T) new PinnedActivityStack(mDisplay, stackId,
+ mRootActivityContainer.mStackSupervisor, mOnTop) {
@Override
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
return new Rect(50, 50, 100, 100);
@@ -796,7 +799,8 @@
}
};
} else {
- return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode,
+ return (T) new TestActivityStack(mDisplay, stackId,
+ mRootActivityContainer.mStackSupervisor, mWindowingMode,
mActivityType, mOnTop, mCreateActivity);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
rename to services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index a907161..5556a15 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -19,12 +19,12 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.FlakyTest;
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
similarity index 84%
rename from services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f12619c..577859c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -24,18 +24,18 @@
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
-import android.view.IApplicationToken;
import androidx.test.filters.SmallTest;
@@ -111,16 +111,9 @@
final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- // Set TestAppWindowContainerController & assign first app token state to be good to go.
- final WindowTestUtils.TestAppWindowContainerController controller1 =
- createAppWindowController(dc1, token1.appToken);
- final WindowTestUtils.TestAppWindowContainerController controller2 =
- createAppWindowController(dc1, token2.appToken);
- controller1.setContainer(token1);
token1.allDrawn = true;
token1.startingDisplayed = true;
token1.startingMoved = true;
- controller2.setContainer(token2);
// Simulate activity resume / finish flows to prepare app transition & set visibility,
// make sure transition is set as expected for each display.
@@ -132,8 +125,8 @@
assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
// One activity window is visible for resuming & the other activity window is invisible
// for finishing in different display.
- controller1.setVisibility(true, false);
- controller2.setVisibility(false, false);
+ token1.setVisibility(true, false);
+ token2.setVisibility(false, false);
// Make sure each display is in animating stage.
assertTrue(dc1.mOpeningApps.size() > 0);
@@ -174,16 +167,4 @@
assertFalse(dc1.mOpeningApps.contains(token1));
}
- private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
- DisplayContent dc, IApplicationToken token) {
- return createAppWindowController(
- new WindowTestUtils.TestTaskWindowContainerController(
- createStackControllerOnDisplay(dc)), token);
- }
-
- private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
- WindowTestUtils.TestTaskWindowContainerController taskController,
- IApplicationToken token) {
- return new WindowTestUtils.TestAppWindowContainerController(taskController, token);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index 4522494..dcfb879 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -20,11 +20,12 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.SurfaceControl.Transaction;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
import android.view.SurfaceControl;
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
similarity index 64%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 552390d..0ee532d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -31,13 +32,17 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_UNSET;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
import android.graphics.Point;
import android.graphics.Rect;
@@ -66,6 +71,8 @@
Task mTask;
WindowTestUtils.TestAppWindowToken mToken;
+ private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+
@Before
public void setUp() throws Exception {
mStack = createTaskStackOnDisplay(mDisplayContent);
@@ -112,7 +119,8 @@
assertEquals(window1, mToken.findMainWindow());
window1.mAnimatingExit = true;
assertEquals(window1, mToken.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
+ "window2");
assertEquals(window2, mToken.findMainWindow());
mToken.removeImmediately();
}
@@ -150,7 +158,7 @@
mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
- appWindow.resizeReported = false;
+ appWindow.mResizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
@@ -158,7 +166,7 @@
mDisplayContent.getDisplayId());
mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
- assertTrue(appWindow.resizeReported);
+ assertTrue(appWindow.mResizeReported);
appWindow.removeImmediately();
}
@@ -179,11 +187,11 @@
// Set initial orientation and update.
performRotation(spiedRotation, Surface.ROTATION_90);
- appWindow.resizeReported = false;
+ appWindow.mResizeReported = false;
// Update the rotation to perform 180 degree rotation and check that resize was reported.
performRotation(spiedRotation, Surface.ROTATION_270);
- assertTrue(appWindow.resizeReported);
+ assertTrue(appWindow.mResizeReported);
appWindow.removeImmediately();
}
@@ -215,7 +223,8 @@
// Can not specify orientation if app isn't visible even though it fills parent.
assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
+ mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
}
@Test
@@ -241,7 +250,8 @@
// Finish relaunching and ensure flag is now not reported
mToken.finishRelaunching();
- assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
+ assertFalse(
+ mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
}
@Test
@@ -251,7 +261,7 @@
"closingWindow");
closingWindow.mAnimatingExit = true;
closingWindow.mRemoveOnExit = true;
- closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
// We pretended that we were running an exit animation, but that should have been cleared up
@@ -261,6 +271,124 @@
}
@Test
+ public void testSetOrientation() {
+ // Assert orientation is unspecified to start.
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation());
+
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
+
+ mDisplayContent.removeAppToken(mToken.token);
+ // Assert orientation is unset to after container is removed.
+ assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
+
+ // Reset display frozen state
+ mWm.mDisplayFrozen = false;
+ }
+
+ @Test
+ public void testCreateRemoveStartingWindow() {
+ mToken.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+ assertHasStartingWindow(mToken);
+ mToken.removeStartingWindow();
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(mToken);
+ }
+
+ @Test
+ public void testAddRemoveRace() {
+ // There was once a race condition between adding and removing starting windows
+ for (int i = 0; i < 1000; i++) {
+ final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken();
+
+ appToken.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ appToken.removeStartingWindow();
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(appToken);
+
+ appToken.getParent().getParent().removeImmediately();
+ }
+ }
+
+ @Test
+ public void testTransferStartingWindow() {
+ final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+ final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+ token1.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+ token2.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(),
+ true, true, false, true, false, false);
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(token1);
+ assertHasStartingWindow(token2);
+ }
+
+ @Test
+ public void testTransferStartingWindowWhileCreating() {
+ final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+ final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+ ((TestWindowManagerPolicy) token1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
+ () -> {
+ // Surprise, ...! Transfer window in the middle of the creation flow.
+ token2.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0,
+ token1.appToken.asBinder(), true, true, false,
+ true, false, false);
+ });
+ token1.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(token1);
+ assertHasStartingWindow(token2);
+ }
+
+ private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() {
+ final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(taskStack, 0 /* userId */);
+ return createTestAppWindowTokenForGivenTask(task);
+ }
+
+ private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) {
+ final WindowTestUtils.TestAppWindowToken appToken =
+ WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+ task.addChild(appToken, 0);
+ waitUntilHandlersIdle();
+ return appToken;
+ }
+
+ @Test
+ public void testTryTransferStartingWindowFromHiddenAboveToken() {
+ // Add two tasks on top of each other.
+ final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken();
+ final WindowTestUtils.TestAppWindowToken tokenBottom =
+ createTestAppWindowTokenForGivenTask(tokenTop.getTask());
+
+ // Add a starting window.
+ tokenTop.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+
+ // Make the top one invisible, and try transferring the starting window from the top to the
+ // bottom one.
+ tokenTop.setVisibility(false, false);
+ tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
+
+ // Assert that the bottom window now has the starting window.
+ assertNoStartingWindow(tokenTop);
+ assertHasStartingWindow(tokenBottom);
+ }
+
+ @Test
public void testTransitionAnimationPositionAndBounds() {
final Rect stackBounds = new Rect(
0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */);
@@ -285,4 +413,19 @@
assertEquals(expectedY, outPosition.y);
assertEquals(expectedBounds, outBounds);
}
+
+ private void assertHasStartingWindow(AppWindowToken atoken) {
+ assertNotNull(atoken.startingSurface);
+ assertNotNull(atoken.startingData);
+ assertNotNull(atoken.startingWindow);
+ }
+
+ private void assertNoStartingWindow(AppWindowToken atoken) {
+ assertNull(atoken.startingSurface);
+ assertNull(atoken.startingWindow);
+ assertNull(atoken.startingData);
+ atoken.forAllWindows(windowState -> {
+ assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
+ }, true);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 991981f..ee1c8df 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -16,16 +16,17 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -145,7 +146,7 @@
}
@Test
- public void testUpdateDimsAppliesSize() {
+ public void testUpdateDimsAppliesCrop() {
mDimmer.dimAbove(mTransaction, 0.8f);
int width = 100;
@@ -153,7 +154,7 @@
Rect bounds = new Rect(0, 0, width, height);
mDimmer.updateDims(mTransaction, bounds);
- verify(mTransaction).setSize(getDimLayer(), width, height);
+ verify(mTransaction).setWindowCrop(getDimLayer(), width, height);
verify(mTransaction).show(getDimLayer());
}
@@ -242,13 +243,13 @@
SurfaceControl dimLayer = getDimLayer();
bounds.set(0, 0, 10, 10);
mDimmer.updateDims(mTransaction, bounds);
+ verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
verify(mTransaction, times(1)).show(dimLayer);
- verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
verify(mTransaction).setPosition(dimLayer, 0, 0);
bounds.set(10, 10, 30, 30);
mDimmer.updateDims(mTransaction, bounds);
- verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
+ verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
verify(mTransaction).setPosition(dimLayer, 10, 10);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 43e10f0..3b8d71d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -29,6 +29,10 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -39,10 +43,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 18bd2e4..6767465 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -28,14 +28,11 @@
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
-import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
new file mode 100644
index 0000000..845a09f
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -0,0 +1,451 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wm;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.utils.WmDisplayCutout;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+@Presubmit
+public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
+
+ private DisplayFrames mFrames;
+ private WindowState mWindow;
+ private int mRotation = ROTATION_0;
+ private boolean mHasDisplayCutout;
+
+ @Before
+ public void setUp() throws Exception {
+ updateDisplayFrames();
+
+ mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
+ // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
+ // changing those frames.
+ doNothing().when(mWindow).computeFrameLw();
+
+ final WindowManager.LayoutParams attrs = mWindow.mAttrs;
+ attrs.width = MATCH_PARENT;
+ attrs.height = MATCH_PARENT;
+ attrs.flags =
+ FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ attrs.format = PixelFormat.TRANSLUCENT;
+ }
+
+ public void setRotation(int rotation) {
+ mRotation = rotation;
+ updateDisplayFrames();
+ }
+
+ public void addDisplayCutout() {
+ mHasDisplayCutout = true;
+ updateDisplayFrames();
+ }
+
+ private void updateDisplayFrames() {
+ final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
+ mHasDisplayCutout);
+ mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
+ }
+
+ @Test
+ public void addingWindow_doesNotTamperWithSysuiFlags() {
+ mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ assertEquals(0, mWindow.mAttrs.systemUiVisibility);
+ assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
+ }
+
+ @Test
+ public void layoutWindowLw_appDrawsBars() {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_appWontDrawBars() {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_never() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreen() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+ }
+ }
+
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_landscape() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_seascape() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_270);
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+
+ mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
+ mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
+ mWindow.mAttrs.width = DISPLAY_WIDTH;
+ mWindow.mAttrs.height = DISPLAY_HEIGHT;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ }
+ }
+
+ @Test
+ public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
+ synchronized (mWm.mGlobalLock) {
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ }
+ }
+
+ @Test
+ public void layoutHint_appWindow() {
+ synchronized (mWm.mGlobalLock) {
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
+
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
+ false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
+
+ assertThat(outFrame, is(mFrames.mUnrestricted));
+ assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+ assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ }
+ }
+
+ @Test
+ public void layoutHint_appWindowInTask() {
+ synchronized (mWm.mGlobalLock) {
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ final Rect taskBounds = new Rect(100, 100, 200, 200);
+
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
+
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+ false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
+
+ assertThat(outFrame, is(taskBounds));
+ assertThat(outContentInsets, is(new Rect()));
+ assertThat(outStableInsets, is(new Rect()));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ }
+ }
+
+ @Test
+ public void layoutHint_appWindowInTask_outsideContentFrame() {
+ synchronized (mWm.mGlobalLock) {
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ // Task is in the nav bar area (usually does not happen, but this is similar enough to
+ // the possible overlap with the IME)
+ final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
+ 200, mFrames.mContent.bottom + 10);
+
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
+
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+ true /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
+
+ assertThat(outFrame, is(taskBounds));
+ assertThat(outContentInsets, is(new Rect()));
+ assertThat(outStableInsets, is(new Rect()));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ }
+ }
+
+ /**
+ * Asserts that {@code actual} is inset by the given amounts from the full display rect.
+ *
+ * Convenience wrapper for when only the top and bottom inset are non-zero.
+ */
+ private void assertInsetByTopBottom(Rect actual, int expectedInsetTop,
+ int expectedInsetBottom) {
+ assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
+ }
+
+ /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
+ private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
+ int expectedInsetRight, int expectedInsetBottom) {
+ assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
+ mFrames.mDisplayWidth - expectedInsetRight,
+ mFrames.mDisplayHeight - expectedInsetBottom), actual);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 07d5fea..8349ac7f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -42,12 +42,9 @@
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
-import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class DisplayPolicyTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
new file mode 100644
index 0000000..e988994
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -0,0 +1,823 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.PowerManagerInternal;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.view.Surface;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class for {@link DisplayRotation}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayRotationTests
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+public class DisplayRotationTests {
+ private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
+
+ private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
+
+ private WindowManagerService mMockWm;
+ private DisplayContent mMockDisplayContent;
+ private DisplayPolicy mMockDisplayPolicy;
+ private Context mMockContext;
+ private Resources mMockRes;
+ private SensorManager mMockSensorManager;
+ private Sensor mFakeSensor;
+ private DisplayWindowSettings mMockDisplayWindowSettings;
+ private ContentResolver mMockResolver;
+ private FakeSettingsProvider mFakeSettingsProvider;
+ private StatusBarManagerInternal mMockStatusBarManagerInternal;
+
+ // Fields below are callbacks captured from test target.
+ private ContentObserver mShowRotationSuggestionsObserver;
+ private ContentObserver mAccelerometerRotationObserver;
+ private ContentObserver mUserRotationObserver;
+ private SensorEventListener mOrientationSensorListener;
+
+ private DisplayRotationBuilder mBuilder;
+
+ private DisplayRotation mTarget;
+
+ @Before
+ public void setUp() {
+ FakeSettingsProvider.clearSettingsProvider();
+
+ mMockWm = mock(WindowManagerService.class);
+ mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+
+ mPreviousStatusBarManagerInternal = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+
+ mBuilder = new DisplayRotationBuilder();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ if (mPreviousStatusBarManagerInternal != null) {
+ LocalServices.addService(StatusBarManagerInternal.class,
+ mPreviousStatusBarManagerInternal);
+ mPreviousStatusBarManagerInternal = null;
+ }
+ }
+
+ // ================================
+ // Display Settings Related Tests
+ // ================================
+ @Test
+ public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception {
+ mBuilder.build();
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+ assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+ assertEquals(0, Settings.System.getInt(mMockResolver,
+ Settings.System.ACCELEROMETER_ROTATION));
+ assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver,
+ Settings.System.USER_ROTATION));
+ }
+
+ @Test
+ public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.mIsDefaultDisplay = false;
+
+ mBuilder.build();
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+ assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+ verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent,
+ WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180);
+ }
+
+ @Test
+ public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception {
+ mBuilder.build();
+
+ thawRotation();
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
+
+ assertEquals(1, Settings.System.getInt(mMockResolver,
+ Settings.System.ACCELEROMETER_ROTATION));
+ }
+
+ @Test
+ public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.mIsDefaultDisplay = false;
+
+ mBuilder.build();
+
+ thawRotation();
+
+ assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
+
+ verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent),
+ eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt());
+ }
+
+ @Test
+ public void testPersistsFixedToUserRotation() throws Exception {
+ mBuilder.build();
+
+ mTarget.setFixedToUserRotation(true);
+
+ verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, true);
+
+ reset(mMockDisplayWindowSettings);
+ mTarget.setFixedToUserRotation(false);
+
+ verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, false);
+ }
+
+ // ========================================
+ // Tests for User Rotation based Rotation
+ // ========================================
+ @Test
+ public void testReturnsUserRotation_UserRotationLocked_NoAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsUserRotation_UserRotationLocked_CompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ final int rotation = mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90);
+ assertTrue("Rotation should be sideways, but it's "
+ + Surface.rotationToString(rotation),
+ rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ }
+
+ // =================================
+ // Tests for Sensor based Rotation
+ // =================================
+ private void verifyOrientationListenerRegistration(int numOfInvocation) {
+ final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
+ SensorEventListener.class);
+ verify(mMockSensorManager, times(numOfInvocation)).registerListener(
+ listenerCaptor.capture(),
+ same(mFakeSensor),
+ anyInt(),
+ any());
+ if (numOfInvocation > 0) {
+ mOrientationSensorListener = listenerCaptor.getValue();
+ }
+ }
+
+ @Test
+ public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception {
+ mBuilder.setSupportAutoRotation(false).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ScreenNotOn() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_NotAwake() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(false);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_FixedUserRotation() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.setFixedToUserRotation(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ @Test
+ public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
+
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(0);
+ }
+
+ private void enableOrientationSensor() {
+ when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+ when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+ when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+ when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+ mTarget.updateOrientationListener();
+ verifyOrientationListenerRegistration(1);
+ }
+
+ private SensorEvent createSensorEvent(int rotation) throws Exception {
+ final Constructor<SensorEvent> constructor =
+ SensorEvent.class.getDeclaredConstructor(int.class);
+ constructor.setAccessible(true);
+ final SensorEvent event = constructor.newInstance(1);
+ event.sensor = mFakeSensor;
+ event.values[0] = rotation;
+ event.timestamp = SystemClock.elapsedRealtimeNanos();
+ return event;
+ }
+
+ @Test
+ public void testReturnsSensorRotation_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ private boolean waitForUiHandler() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ UiThread.getHandler().post(latch::countDown);
+ return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ @Test
+ public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertTrue(waitForUiHandler());
+
+ verify(mMockWm).updateRotation(false, false);
+ }
+
+ @Test
+ public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+ assertTrue(waitForUiHandler());
+
+ verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
+ }
+
+ @Test
+ public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ thawRotation();
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+ final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE,
+ Surface.ROTATION_0);
+ assertTrue("Rotation should be sideways but it's "
+ + Surface.rotationToString(rotation),
+ rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ }
+
+ @Test
+ public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ enableOrientationSensor();
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ // =================================
+ // Tests for Policy based Rotation
+ // =================================
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception {
+ mBuilder.build();
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+ .thenReturn(true);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
+
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+ Surface.ROTATION_180));
+ }
+
+ @Test
+ public void testReturnsLidOpenRotation_LidOpen() throws Exception {
+ mBuilder.setLidOpenRotation(Surface.ROTATION_90).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getLidState()).thenReturn(
+ WindowManagerPolicy.WindowManagerFuncs.LID_OPEN);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ @Test
+ public void testReturnsCarDockRotation_CarDockedMode() throws Exception {
+ mBuilder.setCarDockRotation(Surface.ROTATION_270).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception {
+ mBuilder.setDeskDockRotation(Surface.ROTATION_270).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+ mTarget.setFixedToUserRotation(true);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ final int rotation = mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90);
+ assertEquals(Surface.ROTATION_180, rotation);
+ }
+
+ @Test
+ public void testReturnsUserRotation_NonDefaultDisplay() throws Exception {
+ mBuilder.setIsDefaultDisplay(false).build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
+ /**
+ * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
+ * according to given parameters.
+ */
+ private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) {
+ final int width;
+ final int height;
+ switch (displayOrientation) {
+ case SCREEN_ORIENTATION_LANDSCAPE:
+ width = 1920;
+ height = 1080;
+ break;
+ case SCREEN_ORIENTATION_PORTRAIT:
+ width = 1080;
+ height = 1920;
+ break;
+ default:
+ throw new IllegalArgumentException("displayOrientation needs to be either landscape"
+ + " or portrait, but we got "
+ + ActivityInfo.screenOrientationToString(displayOrientation));
+ }
+
+ final PackageManager mockPackageManager = mock(PackageManager.class);
+ when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
+ when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
+ .thenReturn(isCar);
+ when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ .thenReturn(isTv);
+
+ final int shortSizeDp = (isCar || isTv) ? 540 : 720;
+ final int longSizeDp = 960;
+ mTarget.configure(width, height, shortSizeDp, longSizeDp);
+ }
+
+ private void freezeRotation(int rotation) {
+ mTarget.freezeRotation(rotation);
+
+ if (mTarget.isDefaultDisplay) {
+ mAccelerometerRotationObserver.onChange(false);
+ mUserRotationObserver.onChange(false);
+ }
+ }
+
+ private void thawRotation() {
+ mTarget.thawRotation();
+
+ if (mTarget.isDefaultDisplay) {
+ mAccelerometerRotationObserver.onChange(false);
+ mUserRotationObserver.onChange(false);
+ }
+ }
+
+ private class DisplayRotationBuilder {
+ private boolean mIsDefaultDisplay = true;
+ private boolean mSupportAutoRotation = true;
+
+ private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+ private int mCarDockRotation;
+ private int mDeskDockRotation;
+ private int mUndockedHdmiRotation;
+
+ private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) {
+ mIsDefaultDisplay = isDefaultDisplay;
+ return this;
+ }
+
+ private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) {
+ mSupportAutoRotation = supportAutoRotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setLidOpenRotation(int rotation) {
+ mLidOpenRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setCarDockRotation(int rotation) {
+ mCarDockRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setDeskDockRotation(int rotation) {
+ mDeskDockRotation = rotation;
+ return this;
+ }
+
+ private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) {
+ mUndockedHdmiRotation = rotation;
+ return this;
+ }
+
+ private void captureObservers() {
+ ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(
+ ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mShowRotationSuggestionsObserver = captor.getValue();
+ }
+
+ captor = ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mAccelerometerRotationObserver = captor.getValue();
+ }
+
+ captor = ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mMockResolver, atMost(1)).registerContentObserver(
+ eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)),
+ anyBoolean(),
+ captor.capture(),
+ anyInt());
+ if (!captor.getAllValues().isEmpty()) {
+ mUserRotationObserver = captor.getValue();
+ }
+ }
+
+ private Sensor createSensor(int type) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+
+ setSensorType(sensor, type);
+ setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
+ setSensorField(sensor, "mVendor", "Mock Vendor");
+ setSensorField(sensor, "mVersion", 1);
+ setSensorField(sensor, "mHandle", -1);
+ setSensorField(sensor, "mMaxRange", 10);
+ setSensorField(sensor, "mResolution", 1);
+ setSensorField(sensor, "mPower", 1);
+ setSensorField(sensor, "mMinDelay", 1000);
+ setSensorField(sensor, "mMaxDelay", 1000000000);
+ setSensorField(sensor, "mFlags", 0);
+ setSensorField(sensor, "mId", -1);
+
+ return sensor;
+ }
+
+ private void setSensorType(Sensor sensor, int type) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ }
+
+ private void setSensorField(Sensor sensor, String fieldName, Object value)
+ throws Exception {
+ Field field = Sensor.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(sensor, value);
+ }
+
+ private int convertRotationToDegrees(@Surface.Rotation int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ return 0;
+ case Surface.ROTATION_90:
+ return 90;
+ case Surface.ROTATION_180:
+ return 180;
+ case Surface.ROTATION_270:
+ return 270;
+ default:
+ return -1;
+ }
+ }
+
+ private void build() throws Exception {
+ mMockContext = mock(Context.class);
+
+ mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class);
+ mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
+
+ mMockDisplayPolicy = mock(DisplayPolicy.class);
+
+ mMockRes = mock(Resources.class);
+ when(mMockContext.getResources()).thenReturn((mMockRes));
+ when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation))
+ .thenReturn(mSupportAutoRotation);
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation))
+ .thenReturn(convertRotationToDegrees(mLidOpenRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation))
+ .thenReturn(convertRotationToDegrees(mCarDockRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation))
+ .thenReturn(convertRotationToDegrees(mDeskDockRotation));
+ when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation))
+ .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
+
+ mMockSensorManager = mock(SensorManager.class);
+ when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
+ .thenReturn(mMockSensorManager);
+ mFakeSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
+ when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
+ Collections.singletonList(mFakeSensor));
+
+ mMockResolver = mock(ContentResolver.class);
+ when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
+ mFakeSettingsProvider = new FakeSettingsProvider();
+ when(mMockResolver.acquireProvider(Settings.AUTHORITY))
+ .thenReturn(mFakeSettingsProvider.getIContentProvider());
+
+ mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
+ mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
+ mMockDisplayWindowSettings, mMockContext, new Object());
+
+ captureObservers();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index b823e70..8e881b5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -21,13 +21,19 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Matchers.eq;
import android.app.WindowConfiguration;
import android.platform.test.annotations.Presubmit;
@@ -378,6 +384,33 @@
mSecondaryDisplay.getDisplayRotation().getUserRotation());
}
+ @Test
+ public void testNotFixedToUserRotationByDefault() {
+ mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+ Surface.ROTATION_0);
+
+ final DisplayRotation displayRotation = mock(DisplayRotation.class);
+ mPrimaryDisplay = spy(mPrimaryDisplay);
+ when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+
+ mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+ verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false));
+ }
+
+ @Test
+ public void testSetFixedToUserRotation() {
+ mTarget.setFixedToUserRotation(mPrimaryDisplay, true);
+
+ final DisplayRotation displayRotation = mock(DisplayRotation.class);
+ mPrimaryDisplay = spy(mPrimaryDisplay);
+ when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+
+ applySettingsToDisplayByNewInstance(mPrimaryDisplay);
+
+ verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true));
+ }
+
private static void assertOverscan(DisplayContent display, int left, int top, int right,
int bottom) {
final DisplayInfo info = display.getDisplayInfo();
diff --git a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
index a04bf16..3206208 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
@@ -30,12 +30,9 @@
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
-import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class DockedStackDividerControllerTests {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 55e766d..f1c6eab 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -20,13 +20,14 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.content.ClipData;
import android.graphics.PixelFormat;
@@ -171,7 +172,7 @@
try {
final SurfaceControl surface = new SurfaceControl.Builder(appSession)
.setName("drag surface")
- .setSize(100, 100)
+ .setBufferSize(100, 100)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
new file mode 100644
index 0000000..88215449
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wm;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsSourceProviderTest extends WindowTestsBase {
+
+ private InsetsSource mSource = new InsetsSource(TYPE_TOP_BAR);
+ private InsetsSourceProvider mProvider;
+
+ @Before
+ public void setUp() throws Exception {
+ mSource.setVisible(true);
+ mProvider = new InsetsSourceProvider(mSource,
+ mDisplayContent.getInsetsStateController(), mDisplayContent);
+ }
+
+ @Test
+ public void testPostLayout() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ topBar.mHasSurface = true;
+ mProvider.setWindow(topBar, null);
+ mProvider.onPostLayout();
+ assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 100, 0, 0),
+ mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+ }
+
+ @Test
+ public void testPostLayout_invisible() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ mProvider.setWindow(topBar, null);
+ mProvider.onPostLayout();
+ assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+ }
+
+ @Test
+ public void testPostLayout_frameProvider() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ mProvider.setWindow(topBar,
+ (displayFrames, windowState, rect) -> {
+ rect.set(10, 10, 20, 20);
+ });
+ mProvider.onPostLayout();
+ assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
new file mode 100644
index 0000000..11526a8
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+package com.android.server.wm;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsStateControllerTest extends WindowTestsBase {
+
+ @Test
+ public void testStripForDispatch_notOwn() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+ assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR));
+ }
+
+ @Test
+ public void testStripForDispatch_own() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+ .setWindow(topBar, null);
+ topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+ assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar));
+ }
+
+ @Test
+ public void testStripForDispatch_navBar() {
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
+ getController().getSourceProvider(TYPE_IME).setWindow(ime, null);
+ assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar));
+ }
+
+ @Test
+ public void testBarControllingWinChanged() {
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
+ getController().onBarControllingWindowChanged(app);
+ InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
+ assertEquals(2, controls.length);
+ }
+
+ @Test
+ public void testControlRevoked() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ getController().onBarControllingWindowChanged(app);
+ assertNotNull(getController().getControlsForDispatch(app));
+ getController().onBarControllingWindowChanged(null);
+ assertNull(getController().getControlsForDispatch(app));
+ }
+
+ @Test
+ public void testControlRevoked_animation() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+ getController().onBarControllingWindowChanged(app);
+ assertNotNull(getController().getControlsForDispatch(app));
+ topBar.cancelAnimation();
+ assertNull(getController().getControlsForDispatch(app));
+ }
+
+ private InsetsStateController getController() {
+ return mDisplayContent.getInsetsStateController();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index fa4898b..3720c85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -89,9 +89,9 @@
final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
final ActivityOptions options = mock(ActivityOptions.class);
- mController.calculate(record.getTask(), layout, record, source, options,
+ mController.calculate(record.getTaskRecord(), layout, record, source, options,
new LaunchParams());
- verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record),
+ verify(positioner, times(1)).onCalculate(eq(record.getTaskRecord()), eq(layout), eq(record),
eq(source), eq(options), any(), any());
}
@@ -114,7 +114,7 @@
mPersister.putLaunchParams(userId, name, expected);
- mController.calculate(activity.getTask(), null /*layout*/, activity, null /*source*/,
+ mController.calculate(activity.getTaskRecord(), null /*layout*/, activity, null /*source*/,
null /*options*/, new LaunchParams());
verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), eq(expected),
any());
@@ -228,7 +228,7 @@
final LaunchParams result = new LaunchParams();
final ActivityRecord vrActivity = new ActivityBuilder(mService).build();
- vrActivity.requestedVrComponent = vrActivity.realActivity;
+ vrActivity.requestedVrComponent = vrActivity.mActivityComponent;
// VR activities should always land on default display.
mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/,
@@ -412,8 +412,9 @@
@Override
void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams params) {
- final int userId = task != null ? task.userId : activity.userId;
- final ComponentName name = task != null ? task.realActivity : activity.realActivity;
+ final int userId = task != null ? task.userId : activity.mUserId;
+ final ComponentName name = task != null
+ ? task.realActivity : activity.mActivityComponent;
params.reset();
final Map<ComponentName, LaunchParams> map = mMap.get(userId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index dc22bc1..f3a125b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -110,8 +110,9 @@
final DisplayInfo info = new DisplayInfo();
info.uniqueId = mDisplayUniqueId;
mTestDisplay = createNewActivityDisplay(info);
- mSupervisor.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
- when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(mTestDisplay);
+ mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
+ when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId)))
+ .thenReturn(mTestDisplay);
ActivityStack stack = mTestDisplay.createStack(TEST_WINDOWING_MODE,
ACTIVITY_TYPE_STANDARD, /* onTop */ true);
@@ -184,7 +185,7 @@
public void testReturnsEmptyDisplayIfDisplayIsNotFound() {
mTarget.saveTask(mTestTask);
- when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null);
+ when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null);
mTarget.getLaunchParams(mTestTask, null, mResult);
@@ -271,6 +272,51 @@
}
@Test
+ public void testClearsRecordInMemory() {
+ mTarget.saveTask(mTestTask);
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ mTarget.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+ @Test
+ public void testClearsWriteQueueItem() {
+ mTarget.saveTask(mTestTask);
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+
+ target.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+ @Test
+ public void testClearsFile() {
+ mTarget.saveTask(mTestTask);
+ mPersisterQueue.flush();
+
+ mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+
+ target.getLaunchParams(mTestTask, null, mResult);
+
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
+
+ @Test
public void testClearsRecordInMemoryOnPackageUninstalled() {
mTarget.saveTask(mTestTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 33e6063..6259fa6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -104,6 +104,7 @@
new DexmakerShareClassLoaderRule();
@Mock private ActivityStackSupervisor mSupervisor;
+ @Mock private RootActivityContainer mRootActivityContainer;
@Mock private IDevicePolicyManager mDevicePolicyManager;
@Mock private IStatusBarService mStatusBarService;
@Mock private WindowManagerService mWindowManager;
@@ -129,6 +130,7 @@
}
mSupervisor.mRecentTasks = mRecentTasks;
+ mSupervisor.mRootActivityContainer = mRootActivityContainer;
mLockTaskController = new LockTaskController(mContext, mSupervisor,
new ImmediatelyExecuteHandler());
diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
similarity index 88%
rename from services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 1fae317..63d9fb9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -18,14 +18,15 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -46,6 +47,8 @@
@Presubmit
public class PinnedStackControllerTest extends WindowTestsBase {
+ private static final int SHELF_HEIGHT = 300;
+
@Mock private IPinnedStackListener mIPinnedStackListener;
@Mock private IPinnedStackListener.Stub mIPinnedStackListenerStub;
@@ -70,8 +73,6 @@
reset(mIPinnedStackListener);
- final int SHELF_HEIGHT = 300;
-
mWm.setShelfHeight(true, SHELF_HEIGHT);
verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 8596c77..3c7b4b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -113,9 +113,10 @@
mTestService = new MyTestActivityTaskManagerService(mContext);
mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
- mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+ mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks;
+ mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- mStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+ mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
@@ -872,6 +873,15 @@
}
return mTestStackSupervisor;
}
+
+ @Override
+ void initRootActivityContainerMocks(WindowManagerService wm) {
+ super.initRootActivityContainerMocks(wm);
+ mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
+ mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
+ mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+ }
}
private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
@@ -880,15 +890,6 @@
}
@Override
- public void initialize() {
- super.initialize();
- mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
- mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
- addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
- addChild(mDisplay, ActivityDisplay.POSITION_TOP);
- }
-
- @Override
RunningTasks createRunningTasks() {
mRunningTasks = new TestRunningTasks();
return mRunningTasks;
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index ee3bba7..cc6a58a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -21,6 +21,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -28,10 +32,6 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
import android.os.Binder;
import android.os.IInterface;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 070f073..0ff67d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -67,9 +67,9 @@
@Test
public void testCancelAnimationOnStackOrderChange() {
ActivityStack fullscreenStack =
- mService.mStackSupervisor.getDefaultDisplay().createStack(
+ mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- ActivityStack recentsStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ ActivityStack recentsStack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
ActivityRecord recentsActivity = new ActivityBuilder(mService)
.setComponent(mRecentsComponent)
@@ -77,7 +77,7 @@
.setStack(recentsStack)
.build();
ActivityStack fullscreenStack2 =
- mService.mStackSupervisor.getDefaultDisplay().createStack(
+ mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
ActivityRecord fsActivity = new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "App1"))
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index fa53795..ad2a708 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -18,13 +18,14 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
import android.graphics.Point;
import android.graphics.Rect;
@@ -143,8 +144,9 @@
@Test
public void testTimeout_scaled() throws Exception {
mWm.setAnimationScale(2, 5.0f);
- try{
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ try {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
+ "testWin");
final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
new Point(50, 100), new Rect(50, 100, 150, 150));
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
@@ -163,7 +165,6 @@
} finally {
mWm.setAnimationScale(2, 1.0f);
}
-
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
new file mode 100644
index 0000000..9b18388
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -0,0 +1,480 @@
+/*
+ * 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
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.MediumTest;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the {@link ActivityStackSupervisor} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:ActivityStackSupervisorTests
+ */
+@MediumTest
+@Presubmit
+public class RootActivityContainerTests extends ActivityTestsBase {
+ private ActivityStack mFullscreenStack;
+
+ @Before
+ public void setUp() throws Exception {
+ setupActivityTaskManagerService();
+ mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ }
+
+ /**
+ * This test ensures that we do not try to restore a task based off an invalid task id. We
+ * should expect {@code null} to be returned in this case.
+ */
+ @Test
+ public void testRestoringInvalidTask() {
+ ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
+ TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
+ assertNull(task);
+ }
+
+ /**
+ * This test ensures that an existing task in the pinned stack is moved to the fullscreen
+ * activity stack when a new task is added.
+ */
+ @Test
+ public void testReplacingTaskInPinnedStack() {
+ final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final TaskRecord firstTask = firstActivity.getTaskRecord();
+
+ final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final TaskRecord secondTask = secondActivity.getTaskRecord();
+
+ mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
+
+ // Ensure full screen stack has both tasks.
+ ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
+
+ // Move first activity to pinned stack.
+ final Rect sourceBounds = new Rect();
+ mRootActivityContainer.moveActivityToPinnedStack(firstActivity, sourceBounds,
+ 0f /*aspectRatio*/, "initialMove");
+
+ final ActivityDisplay display = mFullscreenStack.getDisplay();
+ ActivityStack pinnedStack = display.getPinnedStack();
+ // Ensure a task has moved over.
+ ensureStackPlacement(pinnedStack, firstTask);
+ ensureStackPlacement(mFullscreenStack, secondTask);
+
+ // Move second activity to pinned stack.
+ mRootActivityContainer.moveActivityToPinnedStack(secondActivity, sourceBounds,
+ 0f /*aspectRatio*/, "secondMove");
+
+ // Need to get stacks again as a new instance might have been created.
+ pinnedStack = display.getPinnedStack();
+ mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ // Ensure stacks have swapped tasks.
+ ensureStackPlacement(pinnedStack, secondTask);
+ ensureStackPlacement(mFullscreenStack, firstTask);
+ }
+
+ private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
+ final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
+ assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
+
+ if (tasks == null) {
+ return;
+ }
+
+ for (TaskRecord task : tasks) {
+ assertTrue(stackTasks.contains(task));
+ }
+ }
+
+ @Test
+ public void testApplySleepTokens() {
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final KeyguardController keyguard = mSupervisor.getKeyguardController();
+ final ActivityStack stack = mock(ActivityStack.class);
+ display.addChild(stack, 0 /* position */);
+
+ // Make sure we wake and resume in the case the display is turning on and the keyguard is
+ // not showing.
+ verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedStack */,
+ false /* keyguardShowing */, true /* expectWakeFromSleep */,
+ true /* expectResumeTopActivity */);
+
+ // Make sure we wake and don't resume when the display is turning on and the keyguard is
+ // showing.
+ verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedStack */,
+ true /* keyguardShowing */, true /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+
+ // Make sure we wake and don't resume when the display is turning on and the keyguard is
+ // not showing as unfocused.
+ verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+ false /* displayShouldSleep */, false /* isFocusedStack */,
+ false /* keyguardShowing */, true /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+
+ // Should not do anything if the display state hasn't changed.
+ verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedStack */,
+ false /* keyguardShowing */, false /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+ }
+
+ private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
+ ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
+ boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
+ boolean expectResumeTopActivity) {
+ reset(stack);
+
+ doReturn(displayShouldSleep).when(display).shouldSleep();
+ doReturn(displaySleeping).when(display).isSleeping();
+ doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+
+ doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
+ doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
+ mRootActivityContainer.applySleepTokens(true);
+ verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+ verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+ null /* target */, null /* targetOptions */);
+ }
+
+ /**
+ * Verifies that removal of activity with task and stack is done correctly.
+ */
+ @Test
+ public void testRemovingStackOnAppCrash() {
+ final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
+ final int originalStackCount = defaultDisplay.getChildCount();
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(stack).build();
+
+ assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
+
+ // Let's pretend that the app has crashed.
+ firstActivity.app.setThread(null);
+ mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test");
+
+ // Verify that the stack was removed.
+ assertEquals(originalStackCount, defaultDisplay.getChildCount());
+ }
+
+ @Test
+ public void testFocusability() {
+ final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(stack).build();
+
+ // Under split screen primary we should be focusable when not minimized
+ mRootActivityContainer.setDockedStackMinimized(false);
+ assertTrue(stack.isFocusable());
+ assertTrue(activity.isFocusable());
+
+ // Under split screen primary we should not be focusable when minimized
+ mRootActivityContainer.setDockedStackMinimized(true);
+ assertFalse(stack.isFocusable());
+ assertFalse(activity.isFocusable());
+
+ final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(pinnedStack).build();
+
+ // We should not be focusable when in pinned mode
+ assertFalse(pinnedStack.isFocusable());
+ assertFalse(pinnedActivity.isFocusable());
+
+ // Add flag forcing focusability.
+ pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+ // We should not be focusable when in pinned mode
+ assertTrue(pinnedStack.isFocusable());
+ assertTrue(pinnedActivity.isFocusable());
+
+ // Without the overridding activity, stack should not be focusable.
+ pinnedStack.removeTask(pinnedActivity.getTaskRecord(), "testFocusability",
+ REMOVE_TASK_MODE_DESTROYING);
+ assertFalse(pinnedStack.isFocusable());
+ }
+
+ /**
+ * Verify that split-screen primary stack will be chosen if activity is launched that targets
+ * split-screen secondary, but a matching existing instance is found on top of split-screen
+ * primary stack.
+ */
+ @Test
+ public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
+ // Create primary split-screen stack with a task and an activity.
+ final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
+ .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
+
+ // Find a launch stack for the top activity in split-screen primary, while requesting
+ // split-screen secondary.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ final ActivityStack result =
+ mRootActivityContainer.getLaunchStack(r, options, task, true /* onTop */);
+
+ // Assert that the primary stack is returned.
+ assertEquals(primaryStack, result);
+ }
+
+ /**
+ * Verify split-screen primary stack & task can resized by
+ * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
+ */
+ @Test
+ public void testResizeDockedStackForSplitScreenPrimary() {
+ final Rect taskSize = new Rect(0, 0, 600, 600);
+ final Rect stackSize = new Rect(0, 0, 300, 300);
+
+ // Create primary split-screen stack with a task.
+ final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
+ .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+
+ // Resize dock stack.
+ mService.resizeDockedStack(stackSize, taskSize, null, null, null);
+
+ // Verify dock stack & its task bounds if is equal as resized result.
+ assertEquals(primaryStack.getBounds(), stackSize);
+ assertEquals(task.getBounds(), taskSize);
+ }
+
+ /**
+ * Verify that home stack would be moved to front when the top activity is Recents.
+ */
+ @Test
+ public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
+ // Create stack/task on default display.
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final TestActivityStack targetStack =
+ new StackBuilder(mRootActivityContainer).setOnTop(false).build();
+ final TaskRecord targetTask = targetStack.getChildAt(0);
+
+ // Create Recents on top of the display.
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType(
+ ACTIVITY_TYPE_RECENTS).build();
+
+ final String reason = "findTaskToMoveToFront";
+ mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+ false);
+
+ verify(display).moveHomeStackToFront(contains(reason));
+ }
+
+ /**
+ * Verify that home stack won't be moved to front if the top activity on other display is
+ * Recents.
+ */
+ @Test
+ public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
+ // Create stack/task on default display.
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+ // Create Recents on secondary display.
+ final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+ ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ new ActivityBuilder(mService).setTask(task).build();
+
+ final String reason = "findTaskToMoveToFront";
+ mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+ false);
+
+ verify(display, never()).moveHomeStackToFront(contains(reason));
+ }
+
+ /**
+ * Verify if a stack is not at the topmost position, it should be able to resume its activity if
+ * the stack is the top focused.
+ */
+ @Test
+ public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
+ // Create a stack at bottom.
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+ final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+ display.positionChildAtBottom(targetStack);
+
+ // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
+ // is the current top focused stack.
+ assertFalse(targetStack.isTopStackOnDisplay());
+ doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
+
+ // Use the stack as target to resume.
+ mRootActivityContainer.resumeFocusedStacksTopActivities(
+ targetStack, activity, null /* targetOptions */);
+
+ // Verify the target stack should resume its activity.
+ verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
+ eq(activity), eq(null /* targetOptions */));
+ }
+
+ /**
+ * Tests home activities that targeted sdk before Q cannot start on secondary display.
+ */
+ @Test
+ public void testStartHomeTargetSdkBeforeQ() throws Exception {
+ final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+ mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
+ doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+ final ActivityInfo info = new ActivityInfo();
+ info.launchMode = LAUNCH_MULTIPLE;
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+ assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+ false /* allowInstrumenting */));
+
+ info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+ assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+ false /* allowInstrumenting */));
+ }
+
+ /**
+ * Tests that home activities can be started on the displays that supports system decorations.
+ */
+ @Test
+ public void testStartHomeOnAllDisplays() {
+ // Create secondary displays.
+ final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+ mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
+ doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+ // Create mock tasks and other necessary mocks.
+ TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
+ final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
+ TaskRecord.setTaskRecordFactory(factory);
+ doAnswer(i -> taskBuilder.build()).when(factory)
+ .create(any(), anyInt(), any(), any(), any(), any());
+ doReturn(true).when(mRootActivityContainer)
+ .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
+ doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
+
+ mRootActivityContainer.startHomeOnAllDisplays(0, "testStartHome");
+
+ assertTrue(mRootActivityContainer.getDefaultDisplay().getTopStack().isActivityTypeHome());
+ assertNotNull(secondDisplay.getTopStack());
+ assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
+ }
+
+ /**
+ * Tests that home activities won't be started before booting when display added.
+ */
+ @Test
+ public void testNotStartHomeBeforeBoot() {
+ final int displayId = 1;
+ final boolean isBooting = mService.mAmInternal.isBooting();
+ final boolean isBooted = mService.mAmInternal.isBooted();
+ try {
+ mService.mAmInternal.setBooting(false);
+ mService.mAmInternal.setBooted(false);
+ mRootActivityContainer.onDisplayAdded(displayId);
+ verify(mRootActivityContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
+ } finally {
+ mService.mAmInternal.setBooting(isBooting);
+ mService.mAmInternal.setBooted(isBooted);
+ }
+ }
+
+ /**
+ * Tests whether home can be started if being instrumented.
+ */
+ @Test
+ public void testCanStartHomeWhenInstrumented() {
+ final ActivityInfo info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ final WindowProcessController app = mock(WindowProcessController.class);
+ doReturn(app).when(mService).getProcessController(any(), anyInt());
+
+ // Can not start home if we don't want to start home while home is being instrumented.
+ doReturn(true).when(app).isInstrumenting();
+ assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ false /* allowInstrumenting*/));
+
+ // Can start home for other cases.
+ assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ true /* allowInstrumenting*/));
+
+ doReturn(false).when(app).isInstrumenting();
+ assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ false /* allowInstrumenting*/));
+ assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+ true /* allowInstrumenting*/));
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 0e1624e..a8b6dc3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -63,7 +63,7 @@
final int numStacks = 2;
for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
final ActivityStack stack =
- new StackBuilder(mSupervisor).setCreateActivity(false).build();
+ new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
display.addChild(stack, POSITION_BOTTOM);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 584f269..83e7ee7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -16,16 +16,17 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeastOnce;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import static java.util.concurrent.TimeUnit.SECONDS;
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 6833dc5..d14f30d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -16,16 +16,17 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
@@ -225,11 +226,9 @@
mTransaction = transaction;
mParent = wm.makeSurfaceBuilder(mSession)
.setName("test surface parent")
- .setSize(3000, 3000)
.build();
mSurface = wm.makeSurfaceBuilder(mSession)
.setName("test surface")
- .setSize(1, 1)
.build();
mFinishedCallbackCalled = false;
mLeash = null;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 6638eeb..9569c0d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -175,7 +175,7 @@
WINDOWING_MODE_FREEFORM);
ActivityRecord source = createSourceActivity(freeformDisplay);
- assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTask(), null /* layout */,
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTaskRecord(), null /* layout */,
null /* activity */, null /* source */, null /* options */, mCurrent, mResult));
assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
@@ -888,10 +888,10 @@
@Test
public void testAdjustBoundsToFitNewDisplay_LargerThanDisplay_RTL() {
- final Configuration overrideConfig = mSupervisor.getOverrideConfiguration();
+ final Configuration overrideConfig = mRootActivityContainer.getOverrideConfiguration();
// Egyptian Arabic is a RTL language.
overrideConfig.setLayoutDirection(new Locale("ar", "EG"));
- mSupervisor.onOverrideConfigurationChanged(overrideConfig);
+ mRootActivityContainer.onOverrideConfigurationChanged(overrideConfig);
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 785b955..b996bfb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -52,8 +52,8 @@
private static final boolean DEBUGGING = false;
private static final String TAG = "TaskPositionerTest";
- private final static int MOUSE_DELTA_X = 5;
- private final static int MOUSE_DELTA_Y = 5;
+ private static final int MOUSE_DELTA_X = 5;
+ private static final int MOUSE_DELTA_Y = 5;
private int mMinVisibleWidth;
private int mMinVisibleHeight;
@@ -315,7 +315,7 @@
// Drag all the way to the right and see the height also shrinking.
mPositioner.resizeDrag(2000.0f, midY);
final int w = mMinVisibleWidth;
- final int h = Math.round((float)w / MIN_ASPECT);
+ final int h = Math.round((float) w / MIN_ASPECT);
assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
mPositioner.getWindowDragBounds());
@@ -428,7 +428,7 @@
// Drag all the way to the right.
mPositioner.resizeDrag(2000.0f, midY);
w = mMinVisibleWidth;
- h = Math.max(Math.round((float)w * MIN_ASPECT), r.height());
+ h = Math.max(Math.round((float) w * MIN_ASPECT), r.height());
assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
mPositioner.getWindowDragBounds());
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 00b4629..3991e06 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -18,14 +18,15 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.platform.test.annotations.Presubmit;
import android.view.InputChannel;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index d2c0765..792e8a6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -48,7 +48,7 @@
public void testGetClosingApps_closing() {
final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
"closingWindow");
- closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
closingApps.add(closingWindow.mAppToken);
@@ -64,9 +64,9 @@
"closingWindow");
final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
FIRST_APPLICATION_WINDOW, "openingWindow");
- closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
- openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET,
+ openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
closingApps.add(closingWindow.mAppToken);
@@ -79,7 +79,7 @@
public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
"closingWindow");
- closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
closingApps.add(closingWindow.mAppToken);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index a569b9e..624ef9b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.server.wm;
@@ -20,14 +20,15 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager.TaskSnapshot;
import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index 1af79e4..bbf508d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -17,8 +17,6 @@
package com.android.server.wm;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
@@ -37,6 +35,7 @@
@Presubmit
public class TaskWindowContainerControllerTests extends WindowTestsBase {
+ /* Comment out due to removal of AppWindowContainerController
@Test
public void testRemoveContainer() {
final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -49,7 +48,9 @@
assertNull(taskController.mContainer);
assertNull(appController.mContainer);
}
+ */
+ /* Comment out due to removal of AppWindowContainerController
@Test
public void testRemoveContainer_deferRemoval() {
final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -74,6 +75,7 @@
assertNull(appController.mContainer);
assertNull(app.getController());
}
+ */
@Test
public void testReparent() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
similarity index 90%
rename from services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
rename to services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 99deeb9..29738ff 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -24,6 +24,8 @@
import android.view.DisplayCutout;
import android.view.DragEvent;
import android.view.IWindow;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import com.android.internal.os.IResultReceiver;
@@ -41,6 +43,15 @@
}
@Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)
+ throws RemoteException {
+ }
+
+ @Override
public void moved(int newX, int newY) throws RemoteException {
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
rename to services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 7b542cb..ba81bd1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -19,8 +19,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import android.annotation.Nullable;
import android.content.Context;
@@ -46,7 +46,8 @@
class TestWindowManagerPolicy implements WindowManagerPolicy {
private final Supplier<WindowManagerService> mWmSupplier;
- boolean keyguardShowingAndNotOccluded = false;
+ int mRotationToReport = 0;
+ boolean mKeyguardShowingAndNotOccluded = false;
private Runnable mRunnableWhenAddingSplashScreen;
@@ -236,7 +237,7 @@
@Override
public boolean isKeyguardLocked() {
- return keyguardShowingAndNotOccluded;
+ return mKeyguardShowingAndNotOccluded;
}
@Override
@@ -256,7 +257,7 @@
@Override
public boolean isKeyguardShowingAndNotOccluded() {
- return keyguardShowingAndNotOccluded;
+ return mKeyguardShowingAndNotOccluded;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 9e22c0a..612f9ad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -73,7 +73,7 @@
public void testClear() {
final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
- mDisplayContent.mUnknownAppVisibilityController.clear();;
+ mDisplayContent.mUnknownAppVisibilityController.clear();
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 4ea6b39..d07230e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -18,11 +18,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import android.graphics.Bitmap;
import android.os.IBinder;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 3643457..885a7e0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -231,7 +231,7 @@
final Configuration config = new Configuration();
final WindowConfiguration winConfig = config.windowConfiguration;
- stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/,
+ stackController.adjustConfigurationForBounds(bounds,
new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/,
false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig,
windowingMode);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index e59afd6..d4a32cf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,13 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -30,13 +37,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -459,13 +459,13 @@
@Test
public void testGetOrientation_childSpecified() {
testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE,
- SCREEN_ORIENTATION_LANDSCAPE);
+ SCREEN_ORIENTATION_LANDSCAPE);
testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET,
- SCREEN_ORIENTATION_UNSPECIFIED);
+ SCREEN_ORIENTATION_UNSPECIFIED);
}
private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation,
- int expectedOrientation) {
+ int expectedOrientation) {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = builder.setLayer(0).build();
root.setFillsParent(true);
@@ -704,7 +704,7 @@
final TestWindowContainer root = builder.build();
final TestWindowContainer child = root.addChildWindow();
- child.setBounds(new Rect(1,1,2,2));
+ child.setBounds(new Rect(1, 1, 2, 2));
final TestWindowContainer grandChild = mock(TestWindowContainer.class);
@@ -742,7 +742,7 @@
private static final Comparator<TestWindowContainer> SUBLAYER_COMPARATOR = (w1, w2) -> {
final int layer1 = w1.mLayer;
final int layer2 = w2.mLayer;
- if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
+ if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0)) {
// We insert the child window into the list ordered by the mLayer. For same layers,
// the negative one should go below others; the positive one should go above others.
return -1;
@@ -782,7 +782,7 @@
}
TestWindowContainer addChildWindow() {
- return addChildWindow(new TestWindowContainerBuilder(mService).setLayer(1));
+ return addChildWindow(new TestWindowContainerBuilder(mWmService).setLayer(1));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index fcde08e..4b666f5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -21,9 +21,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import android.platform.test.annotations.Presubmit;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 227eb00..60a8aeb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -76,7 +76,7 @@
private static class TaskWithBounds extends Task {
final Rect mBounds;
- final Rect mInsetBounds = new Rect();
+ final Rect mOverrideDisplayedBounds = new Rect();
boolean mFullscreenForTest = true;
TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) {
@@ -100,8 +100,8 @@
outBounds.set(mBounds);
}
@Override
- void getTempInsetBounds(Rect outBounds) {
- outBounds.set(mInsetBounds);
+ Rect getOverrideDisplayedBounds() {
+ return mOverrideDisplayedBounds;
}
@Override
boolean isFullscreen() {
@@ -290,7 +290,7 @@
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
w.computeFrameLw();
- assertFrame(w, 700, 0, 1000, 300);
+ assertFrame(w, 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
w.computeFrameLw();
assertFrame(w, 700, 700, 1000, 1000);
@@ -343,14 +343,14 @@
taskBottom - contentInsetBottom));
pf.set(0, 0, logicalWidth, logicalHeight);
- // However if we set temp inset bounds, the insets will be computed
- // as if our window was laid out there, but it will be laid out according to
- // the task bounds.
+ // If we set displayed bounds, the insets will be computed with the main task bounds
+ // but the frame will be positioned according to the displayed bounds.
final int insetLeft = logicalWidth / 5;
final int insetTop = logicalHeight / 5;
final int insetRight = insetLeft + (taskRight - taskLeft);
final int insetBottom = insetTop + (taskBottom - taskTop);
- task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
+ task.mOverrideDisplayedBounds.set(taskBounds);
+ task.mBounds.set(insetLeft, insetTop, insetRight, insetBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw();
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -430,7 +430,6 @@
final int taskBottom = logicalHeight / 4 * 3;
final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds);
- task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom);
task.mFullscreenForTest = false;
WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
@@ -486,12 +485,12 @@
}
@Test
- public void testDisplayCutout_tempInsetBounds() {
+ public void testDisplayCutout_tempDisplayedBounds() {
// Regular fullscreen task and window
final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm,
- new Rect(0, -500, 1000, 1500));
+ new Rect(0, 0, 1000, 2000));
task.mFullscreenForTest = false;
- task.mInsetBounds.set(0, 0, 1000, 2000);
+ task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
similarity index 88%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 9a13efb..266d884 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -21,14 +21,15 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import android.app.ActivityManagerInternal;
import android.content.Context;
@@ -100,6 +101,7 @@
mock(PowerManagerInternal.class));
final PowerManagerInternal pm =
LocalServices.getService(PowerManagerInternal.class);
+ doNothing().when(pm).registerLowPowerModeObserver(any());
PowerSaveState state = new PowerSaveState.Builder().build();
doReturn(state).when(pm).getLowPowerState(anyInt());
@@ -146,8 +148,8 @@
final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
final DisplayWindowController dcw = new DisplayWindowController(display, mService);
- // Display creation is driven by the ActivityManagerService via ActivityStackSupervisor.
- // We emulate those steps here.
+ // Display creation is driven by the ActivityManagerService via
+ // ActivityStackSupervisor. We emulate those steps here.
mService.mRoot.createDisplayContent(display, dcw);
}
@@ -171,21 +173,21 @@
};
}
- public WindowManagerService getWindowManagerService() {
+ WindowManagerService getWindowManagerService() {
return mService;
}
- public TestWindowManagerPolicy getWindowManagerPolicy() {
- return mPolicy;
- }
-
- public void waitUntilWindowManagerHandlersIdle() {
+ void waitUntilWindowManagerHandlersIdle() {
final WindowManagerService wm = getWindowManagerService();
- if (wm != null) {
- wm.mH.runWithScissors(() -> { }, 0);
- wm.mAnimationHandler.runWithScissors(() -> { }, 0);
- SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
+ if (wm == null) {
+ return;
}
+ wm.mH.removeCallbacksAndMessages(null);
+ wm.mAnimationHandler.removeCallbacksAndMessages(null);
+ SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
+ wm.mH.runWithScissors(() -> { }, 0);
+ wm.mAnimationHandler.runWithScissors(() -> { }, 0);
+ SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
}
private void destroyAllSurfaceTransactions() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 118ce89..7f78034 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -32,6 +32,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
@@ -43,11 +49,6 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
import android.graphics.Insets;
import android.graphics.Matrix;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index e56edab..aa0ecf8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
@@ -23,12 +26,24 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
import android.view.Display;
+import android.view.IApplicationToken;
+import android.view.IWindow;
import android.view.Surface;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
import org.mockito.invocation.InvocationOnMock;
@@ -37,6 +52,7 @@
* to WindowManager related test functionality.
*/
public class WindowTestUtils {
+ private static int sNextTaskId = 0;
/** An extension of {@link DisplayContent} to gain package scoped access. */
public static class TestDisplayContent extends DisplayContent {
@@ -54,6 +70,14 @@
return null;
}
+ /**
+ * Stubbing method of non-public parent class isn't supported, so here explicitly overrides.
+ */
+ @Override
+ DockedStackDividerController getDockedDividerController() {
+ return null;
+ }
+
/** Create a mocked default {@link DisplayContent}. */
public static TestDisplayContent create(Context context) {
final TestDisplayContent displayContent = mock(TestDisplayContent.class);
@@ -65,7 +89,7 @@
final DisplayRotation displayRotation = new DisplayRotation(
mock(WindowManagerService.class), displayContent, displayPolicy,
- context, new Object());
+ mock(DisplayWindowSettings.class), context, new Object());
displayRotation.mPortraitRotation = Surface.ROTATION_0;
displayRotation.mLandscapeRotation = Surface.ROTATION_90;
displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
@@ -96,16 +120,27 @@
// many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
// to properly set bounds values in the configuration. We must mimick those actions here.
doAnswer((InvocationOnMock invocationOnMock) -> {
- final Configuration config = invocationOnMock.<Configuration>getArgument(7);
+ final Configuration config = invocationOnMock.<Configuration>getArgument(6);
final Rect bounds = invocationOnMock.<Rect>getArgument(0);
config.windowConfiguration.setBounds(bounds);
return null;
- }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
+ }).when(controller).adjustConfigurationForBounds(any(), any(), any(),
anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
return controller;
}
+ /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
+ public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
+ int userId) {
+ synchronized (service.mGlobalLock) {
+ final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
+ new ActivityManager.TaskDescription(), null);
+ stack.addTask(newTask, POSITION_TOP);
+ return newTask;
+ }
+ }
+
/**
* An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
* normally be mocked out.
@@ -120,4 +155,233 @@
// Do nothing.
}
}
+
+ static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
+ synchronized (dc.mWmService.mGlobalLock) {
+ return new TestAppWindowToken(dc);
+ }
+ }
+
+ /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
+ public static class TestAppWindowToken extends AppWindowToken {
+ boolean mOnTop = false;
+ private Transaction mPendingTransactionOverride;
+
+ private TestAppWindowToken(DisplayContent dc) {
+ super(dc.mWmService, new IApplicationToken.Stub() {
+ @Override
+ public String getName() {
+ return null;
+ }
+ }, new ComponentName("", ""), false, dc, true /* fillsParent */);
+ }
+
+ TestAppWindowToken(WindowManagerService service, IApplicationToken token,
+ ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
+ long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
+ int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
+ boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) {
+ super(service, token, activityComponent, voiceInteraction, dc,
+ inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
+ orientation, rotationAnimationHint, configChanges, launchTaskBehind,
+ alwaysFocusable, activityRecord);
+ }
+
+ int getWindowsCount() {
+ return mChildren.size();
+ }
+
+ boolean hasWindow(WindowState w) {
+ return mChildren.contains(w);
+ }
+
+ WindowState getFirstChild() {
+ return mChildren.peekFirst();
+ }
+
+ WindowState getLastChild() {
+ return mChildren.peekLast();
+ }
+
+ int positionInParent() {
+ return getParent().mChildren.indexOf(this);
+ }
+
+ void setIsOnTop(boolean onTop) {
+ mOnTop = onTop;
+ }
+
+ @Override
+ boolean isOnTop() {
+ return mOnTop;
+ }
+
+ void setPendingTransaction(Transaction transaction) {
+ mPendingTransactionOverride = transaction;
+ }
+
+ @Override
+ public Transaction getPendingTransaction() {
+ return mPendingTransactionOverride == null
+ ? super.getPendingTransaction()
+ : mPendingTransactionOverride;
+ }
+ }
+
+ static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
+ return createTestWindowToken(type, dc, false /* persistOnEmpty */);
+ }
+
+ static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
+ boolean persistOnEmpty) {
+ synchronized (dc.mWmService.mGlobalLock) {
+ return new TestWindowToken(type, dc, persistOnEmpty);
+ }
+ }
+
+ /* Used so we can gain access to some protected members of the {@link WindowToken} class */
+ public static class TestWindowToken extends WindowToken {
+
+ private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
+ super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
+ false /* ownerCanManageAppTokens */);
+ }
+
+ int getWindowsCount() {
+ return mChildren.size();
+ }
+
+ boolean hasWindow(WindowState w) {
+ return mChildren.contains(w);
+ }
+ }
+
+ /* Used so we can gain access to some protected members of the {@link Task} class */
+ public static class TestTask extends Task {
+ boolean mShouldDeferRemoval = false;
+ boolean mOnDisplayChangedCalled = false;
+ private boolean mIsAnimating = false;
+
+ TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
+ int resizeMode, boolean supportsPictureInPicture,
+ TaskWindowContainerController controller) {
+ super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
+ new ActivityManager.TaskDescription(), controller);
+ }
+
+ boolean shouldDeferRemoval() {
+ return mShouldDeferRemoval;
+ }
+
+ int positionInParent() {
+ return getParent().mChildren.indexOf(this);
+ }
+
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ super.onDisplayChanged(dc);
+ mOnDisplayChangedCalled = true;
+ }
+
+ @Override
+ boolean isSelfAnimating() {
+ return mIsAnimating;
+ }
+
+ void setLocalIsAnimating(boolean isAnimating) {
+ mIsAnimating = isAnimating;
+ }
+ }
+
+ /**
+ * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
+ * class.
+ */
+ public static class TestTaskWindowContainerController extends TaskWindowContainerController {
+
+ static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() {
+ @Override
+ public void registerConfigurationChangeListener(
+ ConfigurationContainerListener listener) {
+ }
+
+ @Override
+ public void unregisterConfigurationChangeListener(
+ ConfigurationContainerListener listener) {
+ }
+
+ @Override
+ public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
+ }
+
+ @Override
+ public void requestResize(Rect bounds, int resizeMode) {
+ }
+ };
+
+ TestTaskWindowContainerController(WindowTestsBase testsBase) {
+ this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
+ }
+
+ TestTaskWindowContainerController(StackWindowController stackController) {
+ super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */,
+ RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/,
+ true /* showForAllUsers */, new ActivityManager.TaskDescription(),
+ stackController.mService);
+ }
+
+ @Override
+ TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode,
+ boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
+ return new TestTask(taskId, stack, userId, mService, resizeMode,
+ supportsPictureInPicture, this);
+ }
+ }
+
+ public static class TestIApplicationToken implements IApplicationToken {
+
+ private final Binder mBinder = new Binder();
+ @Override
+ public IBinder asBinder() {
+ return mBinder;
+ }
+ @Override
+ public String getName() {
+ return null;
+ }
+ }
+
+ /** Used to track resize reports. */
+ public static class TestWindowState extends WindowState {
+ boolean mResizeReported;
+
+ TestWindowState(WindowManagerService service, Session session, IWindow window,
+ WindowManager.LayoutParams attrs, WindowToken token) {
+ super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0,
+ false /* ownerCanAddInternalSystemWindow */);
+ }
+
+ @Override
+ void reportResized() {
+ super.reportResized();
+ mResizeReported = true;
+ }
+
+ @Override
+ public boolean isGoneForLayoutLw() {
+ return false;
+ }
+
+ @Override
+ void updateResizingWindowIfNeeded() {
+ // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
+ // the system that it can actually update the window.
+ boolean hadSurface = mHasSurface;
+ mHasSurface = true;
+
+ super.updateResizingWindowIfNeeded();
+
+ mHasSurface = hadSurface;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 1eb46fb..b3f56e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -37,7 +37,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.mockito.Mockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import android.content.Context;
import android.content.res.Configuration;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 0c40a6b..de40e0d 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -24,7 +24,6 @@
"libdexfile",
"slicer",
],
- cppflags: ["-std=c++17"],
}
cc_library_host_static {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index cef99865..d617de0 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -29,7 +29,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
-import java.lang.String;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
@@ -908,10 +907,16 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("[pa: ");
+ sb.append("[id: ");
+ sb.append(mTelecomCallId);
+ sb.append(", pa: ");
sb.append(mAccountHandle);
sb.append(", hdl: ");
- sb.append(Log.pii(mHandle));
+ sb.append(Log.piiHandle(mHandle));
+ sb.append(", hdlPres: ");
+ sb.append(mHandlePresentation);
+ sb.append(", videoState: ");
+ sb.append(VideoProfile.videoStateToString(mVideoState));
sb.append(", caps: ");
sb.append(capabilitiesToString(mCallCapabilities));
sb.append(", props: ");
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 7db6940..6628743 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -27,8 +27,8 @@
import android.os.RemoteException;
import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.ICallScreeningService;
import com.android.internal.telecom.ICallScreeningAdapter;
+import com.android.internal.telecom.ICallScreeningService;
/**
* This service can be implemented by the default dialer (see
@@ -147,7 +147,7 @@
private boolean mShouldSkipCallLog;
private boolean mShouldSkipNotification;
- /*
+ /**
* Sets whether the incoming call should be blocked.
*/
public Builder setDisallowCall(boolean shouldDisallowCall) {
@@ -155,7 +155,7 @@
return this;
}
- /*
+ /**
* Sets whether the incoming call should be disconnected as if the user had manually
* rejected it. This property should only be set to true if the call is disallowed.
*/
@@ -164,16 +164,20 @@
return this;
}
- /*
+ /**
* Sets whether the incoming call should not be displayed in the call log. This property
* should only be set to true if the call is disallowed.
+ * <p>
+ * Note: Calls will still be logged with type
+ * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
+ * is set.
*/
public Builder setSkipCallLog(boolean shouldSkipCallLog) {
mShouldSkipCallLog = shouldSkipCallLog;
return this;
}
- /*
+ /**
* Sets whether a missed call notification should not be shown for the incoming call.
* This property should only be set to true if the call is disallowed.
*/
@@ -211,6 +215,17 @@
* Called when a new incoming call is added.
* {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
* should be called to allow or disallow the call.
+ * <p>
+ * Note: The {@link Call.Details} instance provided to a call screening service will only have
+ * the following properties set. The rest of the {@link Call.Details} properties will be set to
+ * their default value or {@code null}.
+ * <ul>
+ * <li>{@link Call.Details#getState()}</li>
+ * <li>{@link Call.Details#getConnectTimeMillis()}</li>
+ * <li>{@link Call.Details#getCreationTimeMillis()}</li>
+ * <li>{@link Call.Details#getHandle()}</li>
+ * <li>{@link Call.Details#getHandlePresentation()}</li>
+ * </ul>
*
* @param callDetails Information about a new incoming call, see {@link Call.Details}.
*/
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d97f0c5f..143b323 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -304,4 +304,10 @@
* @see TelecomServiceImpl#handleCallIntent
*/
void handleCallIntent(in Intent intent);
+
+ void setTestDefaultCallScreeningApp(String packageName);
+
+ void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
+
+ void setTestAutoModeApp(String packageName);
}
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 054288b..2236cba 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -13,3 +13,4 @@
shuoq@google.com
refuhoo@google.com
paulye@google.com
+nazaninb@google.com
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/telephony/java/android/telephony/AvailableNetworkInfo.aidl
similarity index 69%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to telephony/java/android/telephony/AvailableNetworkInfo.aidl
index 5ca9d15..1d4378c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.aidl
@@ -1,6 +1,4 @@
-<?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");
@@ -15,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of overlay [CHAR LIMIT=64] -->
- <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+
+package android.telephony;
+
+parcelable AvailableNetworkInfo;
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
new file mode 100644
index 0000000..fe07370
--- /dev/null
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Defines available network information which includes corresponding subscription id,
+ * network plmns and corresponding priority to be used for network selection by Alternative Network
+ * Service.
+ */
+public final class AvailableNetworkInfo implements Parcelable {
+
+ /*
+ * Defines number of priority level high.
+ */
+ public static final int PRIORITY_HIGH = 1;
+
+ /*
+ * Defines number of priority level medium.
+ */
+ public static final int PRIORITY_MED = 2;
+
+ /*
+ * Defines number of priority level low.
+ */
+ public static final int PRIORITY_LOW = 3;
+
+ /**
+ * subscription Id of the available network. This value must be one of the entry retrieved from
+ * {@link SubscriptionManager#getOpportunisticSubscriptions}
+ */
+ private int mSubId;
+
+ /**
+ * Priority for the subscription id.
+ * Priorities are in the range of 1 to 3 where 1
+ * has the highest priority.
+ */
+ private int mPriority;
+
+ /**
+ * Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
+ * If this entry is left empty, then the platform software will not scan the network
+ * to revalidate the input.
+ */
+ private ArrayList<String> mMccMncs;
+
+ /**
+ * Return subscription Id of the available network.
+ * This value must be one of the entry retrieved from
+ * {@link SubscriptionManager#getOpportunisticSubscriptions}
+ * @return subscription id
+ */
+ public int getSubId() {
+ return mSubId;
+ }
+
+ /**
+ * Return priority for the subscription id. Valid value will be within
+ * [{@link AvailableNetworkInfo#PRIORITY_HIGH}, {@link AvailableNetworkInfo#PRIORITY_LOW}]
+ * @return priority level
+ */
+ public int getPriority() {
+ return mPriority;
+ }
+
+ /**
+ * Return List of PLMN ids (MCC-MNC) associated with the sub ID.
+ * If this entry is left empty, then the platform software will not scan the network
+ * to revalidate the input.
+ * @return list of PLMN ids
+ */
+ public List<String> getMccMncs() {
+ return (List<String>) mMccMncs.clone();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSubId);
+ dest.writeInt(mPriority);
+ dest.writeStringList(mMccMncs);
+ }
+
+ private AvailableNetworkInfo(Parcel in) {
+ mSubId = in.readInt();
+ mPriority = in.readInt();
+ in.readStringList(mMccMncs);
+ }
+
+ public AvailableNetworkInfo(int subId, int priority, ArrayList<String> mccMncs) {
+ mSubId = subId;
+ mPriority = priority;
+ mMccMncs = new ArrayList<String>(mccMncs);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ AvailableNetworkInfo ani;
+
+ try {
+ ani = (AvailableNetworkInfo) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return (mSubId == ani.mSubId
+ && mPriority == ani.mPriority
+ && (((mMccMncs != null)
+ && mMccMncs.equals(ani.mMccMncs))));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSubId, mPriority, mMccMncs);
+ }
+
+ public static final Parcelable.Creator<AvailableNetworkInfo> CREATOR =
+ new Creator<AvailableNetworkInfo>() {
+ @Override
+ public AvailableNetworkInfo createFromParcel(Parcel in) {
+ return new AvailableNetworkInfo(in);
+ }
+
+ @Override
+ public AvailableNetworkInfo[] newArray(int size) {
+ return new AvailableNetworkInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return ("AvailableNetworkInfo:"
+ + " mSubId: " + mSubId
+ + " mPriority: " + mPriority
+ + " mMccMncs: " + Arrays.toString(mMccMncs.toArray()));
+ }
+}
+
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 185c886..62cead1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1154,20 +1154,33 @@
*/
public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
- /**
- * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
- * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
- * the carrier
- * CallScreeningService with the opportunity to allow or block calls.
- * <p>
- * The String includes the package name/the class name.
- * Example:
- * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
- * <p>
- * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
- * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
- * ComponentName.
- */
+ /**
+ * String to override sim country iso.
+ * Sim country iso is based on sim MCC which is coarse and doesn't work with dual IMSI SIM where
+ * a SIM can have multiple MCC from different countries.
+ * Instead, each sim carrier should have a single country code, apply per carrier based iso
+ * code as an override. The overridden value can be read from
+ * {@link TelephonyManager#getSimCountryIso()} and {@link SubscriptionInfo#getCountryIso()}
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING =
+ "sim_country_iso_override_string";
+
+ /**
+ * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
+ * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
+ * the carrier
+ * CallScreeningService with the opportunity to allow or block calls.
+ * <p>
+ * The String includes the package name/the class name.
+ * Example:
+ * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
+ * <p>
+ * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
+ * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
+ * ComponentName.
+ */
public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
/**
@@ -2533,6 +2546,7 @@
sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+ sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, "");
sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
index b6e6cba..5d809d0 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationStates.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -33,17 +33,31 @@
*/
public final boolean isNrAvailable;
+ /**
+ * Indicates that if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving
+ * cell.
+ *
+ * True the primary serving cell is LTE cell and the plmn-InfoList-r15 is present in SIB2 and
+ * at least one bit in this list is true, otherwise this value should be false.
+ *
+ * Reference: 3GPP TS 36.331 v15.2.2 6.3.1 System information blocks.
+ */
+ public final boolean isEnDcAvailable;
+
DataSpecificRegistrationStates(
- int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable) {
+ int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
+ boolean isEnDcAvailable) {
this.maxDataCalls = maxDataCalls;
this.isDcNrRestricted = isDcNrRestricted;
this.isNrAvailable = isNrAvailable;
+ this.isEnDcAvailable = isEnDcAvailable;
}
private DataSpecificRegistrationStates(Parcel source) {
maxDataCalls = source.readInt();
isDcNrRestricted = source.readBoolean();
isNrAvailable = source.readBoolean();
+ isEnDcAvailable = source.readBoolean();
}
@Override
@@ -51,6 +65,7 @@
dest.writeInt(maxDataCalls);
dest.writeBoolean(isDcNrRestricted);
dest.writeBoolean(isNrAvailable);
+ dest.writeBoolean(isEnDcAvailable);
}
@Override
@@ -65,13 +80,14 @@
.append(" maxDataCalls = " + maxDataCalls)
.append(" isDcNrRestricted = " + isDcNrRestricted)
.append(" isNrAvailable = " + isNrAvailable)
+ .append(" isEnDcAvailable = " + isEnDcAvailable)
.append(" }")
.toString();
}
@Override
public int hashCode() {
- return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable);
+ return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable);
}
@Override
@@ -83,7 +99,8 @@
DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
return this.maxDataCalls == other.maxDataCalls
&& this.isDcNrRestricted == other.isDcNrRestricted
- && this.isNrAvailable == other.isNrAvailable;
+ && this.isNrAvailable == other.isNrAvailable
+ && this.isEnDcAvailable == other.isEnDcAvailable;
}
public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index aee744f..b00665e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -219,12 +219,13 @@
public NetworkRegistrationState(int domain, int transportType, int regState,
int accessNetworkTechnology, int rejectCause, boolean emergencyOnly,
int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls,
- boolean isDcNrRestricted, boolean isNrAvailable) {
+ boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable) {
this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
availableServices, cellIdentity);
mDataSpecificStates = new DataSpecificRegistrationStates(
- maxDataCalls, isDcNrRestricted, isNrAvailable);
+ maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable);
+ updateNrStatus(mDataSpecificStates);
}
protected NetworkRegistrationState(Parcel source) {
@@ -448,6 +449,34 @@
dest.writeInt(mNrStatus);
}
+ /**
+ * Use the 5G NR Non-Standalone indicators from the network registration state to update the
+ * NR status. There are 3 indicators in the network registration state:
+ *
+ * 1. if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving cell.
+ * 2. if NR is supported by the selected PLMN.
+ * 3. if the use of dual connectivity with NR is restricted.
+ *
+ * The network has 5G NR capability if E-UTRA-NR Dual Connectivity is supported by the primary
+ * serving cell.
+ *
+ * The use of NR 5G is not restricted If the network has 5G NR capability and both the use of
+ * DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
+ * NR is restricted.
+ *
+ * @param state data specific registration state contains the 5G NR indicators.
+ */
+ private void updateNrStatus(DataSpecificRegistrationStates state) {
+ mNrStatus = NR_STATUS_NONE;
+ if (state.isEnDcAvailable) {
+ if (!state.isDcNrRestricted && state.isNrAvailable) {
+ mNrStatus = NR_STATUS_NOT_RESTRICTED;
+ } else {
+ mNrStatus = NR_STATUS_RESTRICTED;
+ }
+ }
+ }
+
public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
new Parcelable.Creator<NetworkRegistrationState>() {
@Override
diff --git a/telephony/java/android/telephony/NumberVerificationCallback.java b/telephony/java/android/telephony/NumberVerificationCallback.java
new file mode 100644
index 0000000..b00c573
--- /dev/null
+++ b/telephony/java/android/telephony/NumberVerificationCallback.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * A callback for number verification. After a request for number verification is received,
+ * the system will call {@link #onCallReceived(String)} if a phone call was received from a number
+ * matching the provided {@link PhoneNumberRange} or it will call {@link #onVerificationFailed(int)}
+ * if an error occurs.
+ * @hide
+ */
+@SystemApi
+public interface NumberVerificationCallback {
+ /** @hide */
+ @IntDef(value = {REASON_UNSPECIFIED, REASON_TIMED_OUT, REASON_NETWORK_NOT_AVAILABLE,
+ REASON_TOO_MANY_CALLS, REASON_CONCURRENT_REQUESTS, REASON_IN_ECBM,
+ REASON_IN_EMERGENCY_CALL},
+ prefix = {"REASON_"})
+ @interface NumberVerificationFailureReason {}
+
+ /**
+ * Verification failed for an unspecified reason.
+ */
+ int REASON_UNSPECIFIED = 0;
+
+ /**
+ * Verification failed because no phone call was received from a matching number within the
+ * provided timeout.
+ */
+ int REASON_TIMED_OUT = 1;
+
+ /**
+ * Verification failed because no cellular voice network is available.
+ */
+ int REASON_NETWORK_NOT_AVAILABLE = 2;
+
+ /**
+ * Verification failed because there are currently too many ongoing phone calls for a new
+ * incoming phone call to be received.
+ */
+ int REASON_TOO_MANY_CALLS = 3;
+
+ /**
+ * Verification failed because a previous request for verification has not yet completed.
+ */
+ int REASON_CONCURRENT_REQUESTS = 4;
+
+ /**
+ * Verification failed because the phone is in emergency callback mode.
+ */
+ int REASON_IN_ECBM = 5;
+
+ /**
+ * Verification failed because the phone is currently in an emergency call.
+ */
+ int REASON_IN_EMERGENCY_CALL = 6;
+
+ /**
+ * Called when the device receives a phone call from the provided {@link PhoneNumberRange}.
+ * @param phoneNumber The phone number within the range that called. May or may not contain the
+ * country code, but will be entirely numeric.
+ */
+ default void onCallReceived(@NonNull String phoneNumber) { }
+
+ /**
+ * Called when verification fails for some reason.
+ * @param reason The reason for failure.
+ */
+ default void onVerificationFailed(@NumberVerificationFailureReason int reason) { }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/telephony/java/android/telephony/PhoneNumberRange.aidl
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to telephony/java/android/telephony/PhoneNumberRange.aidl
index 27d25b8..b0727be 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/telephony/java/android/telephony/PhoneNumberRange.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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.
@@ -13,12 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
-}
+package android.telephony;
+
+parcelable PhoneNumberRange;
diff --git a/telephony/java/android/telephony/PhoneNumberRange.java b/telephony/java/android/telephony/PhoneNumberRange.java
new file mode 100644
index 0000000..d65156f
--- /dev/null
+++ b/telephony/java/android/telephony/PhoneNumberRange.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to represent a range of phone numbers. Each range corresponds to a contiguous
+ * block of phone numbers.
+ *
+ * Example:
+ * {@code
+ * {
+ * mCountryCode = "1"
+ * mPrefix = "650555"
+ * mLowerBound = "0055"
+ * mUpperBound = "0899"
+ * }
+ * }
+ * would match 16505550089 and 6505550472, but not 63827593759 or 16505550900
+ * @hide
+ */
+@SystemApi
+public final class PhoneNumberRange implements Parcelable {
+ public static final Creator<PhoneNumberRange> CREATOR = new Creator<PhoneNumberRange>() {
+ @Override
+ public PhoneNumberRange createFromParcel(Parcel in) {
+ return new PhoneNumberRange(in);
+ }
+
+ @Override
+ public PhoneNumberRange[] newArray(int size) {
+ return new PhoneNumberRange[size];
+ }
+ };
+
+ private final String mCountryCode;
+ private final String mPrefix;
+ private final String mLowerBound;
+ private final String mUpperBound;
+
+ /**
+ * @param countryCode The country code, omitting the leading "+"
+ * @param prefix A prefix that all numbers matching the range must have.
+ * @param lowerBound When concatenated with the prefix, represents the lower bound of phone
+ * numbers that match this range.
+ * @param upperBound When concatenated with the prefix, represents the upper bound of phone
+ * numbers that match this range.
+ */
+ public PhoneNumberRange(@NonNull String countryCode, @NonNull String prefix,
+ @NonNull String lowerBound, @NonNull String upperBound) {
+ validateLowerAndUpperBounds(lowerBound, upperBound);
+ if (!Pattern.matches("[0-9]+", countryCode)) {
+ throw new IllegalArgumentException("Country code must be all numeric");
+ }
+ if (!Pattern.matches("[0-9]+", prefix)) {
+ throw new IllegalArgumentException("Prefix must be all numeric");
+ }
+ mCountryCode = countryCode;
+ mPrefix = prefix;
+ mLowerBound = lowerBound;
+ mUpperBound = upperBound;
+ }
+
+ private PhoneNumberRange(Parcel in) {
+ mCountryCode = in.readStringNoHelper();
+ mPrefix = in.readStringNoHelper();
+ mLowerBound = in.readStringNoHelper();
+ mUpperBound = in.readStringNoHelper();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStringNoHelper(mCountryCode);
+ dest.writeStringNoHelper(mPrefix);
+ dest.writeStringNoHelper(mLowerBound);
+ dest.writeStringNoHelper(mUpperBound);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PhoneNumberRange that = (PhoneNumberRange) o;
+ return Objects.equals(mCountryCode, that.mCountryCode)
+ && Objects.equals(mPrefix, that.mPrefix)
+ && Objects.equals(mLowerBound, that.mLowerBound)
+ && Objects.equals(mUpperBound, that.mUpperBound);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCountryCode, mPrefix, mLowerBound, mUpperBound);
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneNumberRange{"
+ + "mCountryCode='" + mCountryCode + '\''
+ + ", mPrefix='" + mPrefix + '\''
+ + ", mLowerBound='" + mLowerBound + '\''
+ + ", mUpperBound='" + mUpperBound + '\''
+ + '}';
+ }
+
+ private void validateLowerAndUpperBounds(String lowerBound, String upperBound) {
+ if (lowerBound.length() != upperBound.length()) {
+ throw new IllegalArgumentException("Lower and upper bounds must have the same length");
+ }
+ if (!Pattern.matches("[0-9]+", lowerBound)) {
+ throw new IllegalArgumentException("Lower bound must be all numeric");
+ }
+ if (!Pattern.matches("[0-9]+", upperBound)) {
+ throw new IllegalArgumentException("Upper bound must be all numeric");
+ }
+ if (Integer.parseInt(lowerBound) > Integer.parseInt(upperBound)) {
+ throw new IllegalArgumentException("Lower bound must be lower than upper bound");
+ }
+ }
+
+ /**
+ * Checks to see if the provided phone number matches this range.
+ * @param number A phone number, with or without separators or a country code.
+ * @return {@code true} if the number matches, {@code false} otherwise.
+ */
+ public boolean matches(String number) {
+ // Check the prefix, make sure it matches either with or without the country code.
+ String normalizedNumber = number.replaceAll("[^0-9]", "");
+ String prefixWithCountryCode = mCountryCode + mPrefix;
+ String numberPostfix;
+ if (normalizedNumber.startsWith(prefixWithCountryCode)) {
+ numberPostfix = normalizedNumber.substring(prefixWithCountryCode.length());
+ } else if (normalizedNumber.startsWith(mPrefix)) {
+ numberPostfix = normalizedNumber.substring(mPrefix.length());
+ } else {
+ return false;
+ }
+
+ // Next check the postfix to make sure it lies within the bounds.
+ try {
+ int lower = Integer.parseInt(mLowerBound);
+ int upper = Integer.parseInt(mUpperBound);
+ int numberToCheck = Integer.parseInt(numberPostfix);
+ return numberToCheck <= upper && numberToCheck >= lower;
+ } catch (NumberFormatException e) {
+ Log.e(PhoneNumberRange.class.getSimpleName(), "Invalid bounds or number.", e);
+ return false;
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index d2001ae..22ddb4a 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -16,11 +16,15 @@
package android.telephony;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
-import android.annotation.IntDef;
+import android.telephony.TelephonyManager.NetworkType;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
/**
* @hide
@@ -50,6 +54,7 @@
*
* <p>One of {@link #CONNECTION_PRIMARY_SERVING}, {@link #CONNECTION_SECONDARY_SERVING}.
*/
+ @ConnectionStatus
private int mCellConnectionStatus;
/**
@@ -57,15 +62,33 @@
*/
private int mCellBandwidthDownlinkKhz;
- public PhysicalChannelConfig(int status, int bandwidth) {
- mCellConnectionStatus = status;
- mCellBandwidthDownlinkKhz = bandwidth;
- }
+ /**
+ * The radio technology for this physical channel.
+ */
+ @NetworkType
+ private int mRat;
- public PhysicalChannelConfig(Parcel in) {
- mCellConnectionStatus = in.readInt();
- mCellBandwidthDownlinkKhz = in.readInt();
- }
+ /**
+ * The rough frequency range for this physical channel.
+ */
+ @ServiceState.FrequencyRange
+ private int mFrequencyRange;
+
+ /**
+ * The absolute radio frequency channel number, {@link Integer#MAX_VALUE} if unknown.
+ */
+ private int mChannelNumber;
+
+ /**
+ * A list of data calls mapped to this physical channel. An empty list means the physical
+ * channel has no data call mapped to it.
+ */
+ private int[] mContextIds;
+
+ /**
+ * The physical cell identifier for this cell - PCI, PSC, {@link Integer#MAX_VALUE} if known.
+ */
+ private int mPhysicalCellId;
@Override
public int describeContents() {
@@ -76,6 +99,11 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mCellConnectionStatus);
dest.writeInt(mCellBandwidthDownlinkKhz);
+ dest.writeInt(mRat);
+ dest.writeInt(mChannelNumber);
+ dest.writeInt(mFrequencyRange);
+ dest.writeIntArray(mContextIds);
+ dest.writeInt(mPhysicalCellId);
}
/**
@@ -86,6 +114,60 @@
}
/**
+ * Get the list of data call ids mapped to this physical channel. This list is sorted into
+ * ascending numerical order. Each id in this list must match the id in
+ * {@link com.android.internal.telephony.dataconnection.DataConnection}. An empty list means the
+ * physical channel has no data call mapped to it.
+ *
+ * @return an integer list indicates the data call ids.
+ */
+ public int[] getContextIds() {
+ return mContextIds;
+ }
+
+ /**
+ * @return the rough frequency range for this physical channel.
+ * @see {@link ServiceState#FREQUENCY_RANGE_LOW}
+ * @see {@link ServiceState#FREQUENCY_RANGE_MID}
+ * @see {@link ServiceState#FREQUENCY_RANGE_HIGH}
+ * @see {@link ServiceState#FREQUENCY_RANGE_MMWAVE}
+ */
+ @ServiceState.FrequencyRange
+ public int getFrequencyRange() {
+ return mFrequencyRange;
+ }
+
+ /**
+ * @return the absolute radio frequency channel number for this physical channel,
+ * {@link Integer#MAX_VALUE} if unknown.
+ */
+ public int getChannelNumber() {
+ return mChannelNumber;
+ }
+
+ /**
+ * In UTRAN, this value is primary scrambling code. The range is [0, 511].
+ * Reference: 3GPP TS 25.213 section 5.2.2.
+ *
+ * In EUTRAN, this value is physical layer cell identity. The range is [0, 503].
+ * Reference: 3GPP TS 36.211 section 6.11.
+ *
+ * In 5G RAN, this value is physical layer cell identity. The range is [0, 1008].
+ * Reference: 3GPP TS 38.211 section 7.4.2.1.
+ *
+ * @return the physical cell identifier for this cell, {@link Integer#MAX_VALUE} if unknown.
+ */
+ public int getPhysicalCellId() {
+ return mPhysicalCellId;
+ }
+
+ /**The radio technology for this physical channel. */
+ @NetworkType
+ public int getRat() {
+ return mRat;
+ }
+
+ /**
* Gets the connection status of the cell.
*
* @see #CONNECTION_PRIMARY_SERVING
@@ -125,12 +207,19 @@
PhysicalChannelConfig config = (PhysicalChannelConfig) o;
return mCellConnectionStatus == config.mCellConnectionStatus
- && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz;
+ && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz
+ && mRat == config.mRat
+ && mFrequencyRange == config.mFrequencyRange
+ && mChannelNumber == config.mChannelNumber
+ && mPhysicalCellId == config.mPhysicalCellId
+ && Arrays.equals(mContextIds, config.mContextIds);
}
@Override
public int hashCode() {
- return (mCellBandwidthDownlinkKhz * 29) + (mCellConnectionStatus * 31);
+ return Objects.hash(
+ mCellConnectionStatus, mCellBandwidthDownlinkKhz, mRat, mFrequencyRange,
+ mChannelNumber, mPhysicalCellId, mContextIds);
}
public static final Parcelable.Creator<PhysicalChannelConfig> CREATOR =
@@ -147,11 +236,111 @@
@Override
public String toString() {
return new StringBuilder()
- .append("{mConnectionStatus=")
- .append(getConnectionStatusString())
- .append(",mCellBandwidthDownlinkKhz=")
- .append(mCellBandwidthDownlinkKhz)
- .append("}")
- .toString();
+ .append("{mConnectionStatus=")
+ .append(getConnectionStatusString())
+ .append(",mCellBandwidthDownlinkKhz=")
+ .append(mCellBandwidthDownlinkKhz)
+ .append(",mRat=")
+ .append(mRat)
+ .append(",mFrequencyRange=")
+ .append(mFrequencyRange)
+ .append(",mChannelNumber=")
+ .append(mChannelNumber)
+ .append(",mContextIds=")
+ .append(mContextIds.toString())
+ .append(",mPhysicalCellId=")
+ .append(mPhysicalCellId)
+ .append("}")
+ .toString();
+ }
+
+ private PhysicalChannelConfig(Parcel in) {
+ mCellConnectionStatus = in.readInt();
+ mCellBandwidthDownlinkKhz = in.readInt();
+ mRat = in.readInt();
+ mChannelNumber = in.readInt();
+ mFrequencyRange = in.readInt();
+ mContextIds = in.createIntArray();
+ mPhysicalCellId = in.readInt();
+ }
+
+ private PhysicalChannelConfig(Builder builder) {
+ mCellConnectionStatus = builder.mCellConnectionStatus;
+ mCellBandwidthDownlinkKhz = builder.mCellBandwidthDownlinkKhz;
+ mRat = builder.mRat;
+ mChannelNumber = builder.mChannelNumber;
+ mFrequencyRange = builder.mFrequencyRange;
+ mContextIds = builder.mContextIds;
+ mPhysicalCellId = builder.mPhysicalCellId;
+ }
+
+ /** The builder of {@code PhysicalChannelConfig}. */
+ public static final class Builder {
+ private int mRat;
+ private int mFrequencyRange;
+ private int mChannelNumber;
+ private int mCellBandwidthDownlinkKhz;
+ private int mCellConnectionStatus;
+ private int[] mContextIds;
+ private int mPhysicalCellId;
+
+ /** @hide */
+ public Builder() {
+ mRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+ mChannelNumber = Integer.MAX_VALUE;
+ mCellBandwidthDownlinkKhz = 0;
+ mCellConnectionStatus = CONNECTION_UNKNOWN;
+ mContextIds = new int[0];
+ mPhysicalCellId = Integer.MAX_VALUE;
+ }
+
+ /** @hide */
+ public PhysicalChannelConfig build() {
+ return new PhysicalChannelConfig(this);
+ }
+
+ /** @hide */
+ public Builder setRat(int rat) {
+ this.mRat = rat;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setFrequencyRange(int frequencyRange) {
+ this.mFrequencyRange = frequencyRange;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setChannelNumber(int channelNumber) {
+ this.mChannelNumber = channelNumber;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) {
+ this.mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setCellConnectionStatus(int connectionStatus) {
+ this.mCellConnectionStatus = connectionStatus;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setContextIds(int[] contextIds) {
+ if (contextIds != null) Arrays.sort(contextIds);
+ this.mContextIds = contextIds;
+ return this;
+ }
+
+ /** @hide */
+ public Builder setPhysicalCellId(int physicalCellId) {
+ this.mPhysicalCellId = physicalCellId;
+ return this;
+ }
}
}
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index da3acc2..4482074 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -460,6 +460,9 @@
if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA)) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
}
+ if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_NR)) != 0) {
+ networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+ }
return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf;
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 777d219..ca0c854a 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -118,6 +118,13 @@
*/
public static final int FREQUENCY_RANGE_MMWAVE = 4;
+ private static final List<Integer> FREQUENCY_RANGE_ORDER = Arrays.asList(
+ FREQUENCY_RANGE_UNKNOWN,
+ FREQUENCY_RANGE_LOW,
+ FREQUENCY_RANGE_MID,
+ FREQUENCY_RANGE_HIGH,
+ FREQUENCY_RANGE_MMWAVE);
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "DUPLEX_MODE_",
@@ -223,9 +230,15 @@
public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19;
/**
- * Number of radio technologies for GSM, UMTS and CDMA.
+ * NR(New Radio) 5G.
+ * @hide
*/
- private static final int NEXT_RIL_RADIO_TECHNOLOGY = 20;
+ public static final int RIL_RADIO_TECHNOLOGY_NR = 20;
+
+ /**
+ * The number of the radio technologies.
+ */
+ private static final int NEXT_RIL_RADIO_TECHNOLOGY = 21;
/** @hide */
public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
@@ -1829,4 +1842,13 @@
mNetworkRegistrationStates.add(regState);
}
}
+
+ /**
+ * @hide
+ */
+ public static final int getBetterNRFrequencyRange(int range1, int range2) {
+ return FREQUENCY_RANGE_ORDER.indexOf(range1) > FREQUENCY_RANGE_ORDER.indexOf(range2)
+ ? range1
+ : range2;
+ }
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index b41e14e..bacfe61a 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -150,6 +150,11 @@
private String mGroupUUID;
/**
+ * A property in opportunistic subscription to indicate whether it is metered or not.
+ */
+ private boolean mIsMetered;
+
+ /**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
@@ -158,7 +163,7 @@
@Nullable UiccAccessRule[] accessRules, String cardId) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
- false, null);
+ false, null, true);
}
/**
@@ -168,7 +173,7 @@
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
- @Nullable String groupUUID) {
+ @Nullable String groupUUID, boolean isMetered) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -187,8 +192,10 @@
this.mCardId = cardId;
this.mIsOpportunistic = isOpportunistic;
this.mGroupUUID = groupUUID;
+ this.mIsMetered = isMetered;
}
+
/**
* @return the subscription ID.
*/
@@ -403,6 +410,18 @@
}
/**
+ * Used in opportunistic subscription ({@link #isOpportunistic()}) to indicate whether it's
+ * metered or not.This is one of the factors when deciding to switch to the subscription.
+ * (a non-metered subscription, for example, would likely be preferred over a metered one).
+ *
+ * @return whether subscription is metered.
+ * @hide
+ */
+ public boolean isMetered() {
+ return mIsMetered;
+ }
+
+ /**
* Checks whether the app with the given context is authorized to manage this subscription
* according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
* returns true).
@@ -496,10 +515,11 @@
String cardId = source.readString();
boolean isOpportunistic = source.readBoolean();
String groupUUID = source.readString();
+ boolean isMetered = source.readBoolean();
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- isEmbedded, accessRules, cardId, isOpportunistic, groupUUID);
+ isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered);
}
@Override
@@ -528,6 +548,7 @@
dest.writeString(mCardId);
dest.writeBoolean(mIsOpportunistic);
dest.writeString(mGroupUUID);
+ dest.writeBoolean(mIsMetered);
}
@Override
@@ -561,14 +582,14 @@
+ " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
+ " accessRules " + Arrays.toString(mAccessRules)
+ " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
- + " mGroupUUID=" + mGroupUUID + "}";
+ + " mGroupUUID=" + mGroupUUID + " isMetered=" + mIsMetered + "}";
}
@Override
public int hashCode() {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
- mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso,
- mCardId, mDisplayName, mCarrierName, mAccessRules);
+ mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
+ mCountryIso, mCardId, mDisplayName, mCarrierName, mAccessRules);
}
@Override
@@ -591,6 +612,7 @@
&& mIsEmbedded == toCompare.mIsEmbedded
&& mIsOpportunistic == toCompare.mIsOpportunistic
&& Objects.equals(mGroupUUID, toCompare.mGroupUUID)
+ && mIsMetered == toCompare.mIsMetered
&& Objects.equals(mIccId, toCompare.mIccId)
&& Objects.equals(mNumber, toCompare.mNumber)
&& Objects.equals(mMcc, toCompare.mMcc)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3200aea..2a01ac4 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -147,7 +147,8 @@
public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc");
/**
- * A content {@link Uri} used to receive updates on advanced calling user setting.
+ * A content {@link Uri} used to receive updates on advanced calling user setting
+ * @see ImsMmTelManager#isAdvancedCallingSettingEnabled().
* <p>
* Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
* subscription advanced calling enabled
@@ -406,6 +407,13 @@
public static final String MNC = "mnc";
/**
+ * TelephonyProvider column name for the iso country code associated with a SIM.
+ * <P>Type: TEXT (String)</P>
+ * @hide
+ */
+ public static final String ISO_COUNTRY_CODE = "iso_country_code";
+
+ /**
* TelephonyProvider column name for the sim provisioning status associated with a SIM.
* <P>Type: INTEGER (int)</P>
* @hide
@@ -568,14 +576,6 @@
public static final String IS_OPPORTUNISTIC = "is_opportunistic";
/**
- * TelephonyProvider column name for subId of parent subscription of an opportunistic
- * subscription.
- * if the parent sub id is valid, then is_opportunistic should always to true.
- * @hide
- */
- public static final String PARENT_SUB_ID = "parent_sub_id";
-
- /**
* TelephonyProvider column name for group ID. Subscriptions with same group ID
* are considered bundled together, and should behave as a single subscription at
* certain scenarios.
@@ -583,7 +583,12 @@
* @hide
*/
public static final String GROUP_UUID = "group_uuid";
-
+ /**
+ * TelephonyProvider column name for whether a subscription is metered or not, that is, whether
+ * the network it connects to charges for subscription or not. For example, paid CBRS or unpaid.
+ * @hide
+ */
+ public static final String IS_METERED = "is_metered";
/**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
@@ -1426,7 +1431,7 @@
* subscriptions in the slot.
*/
@Nullable
- public static int[] getSubscriptionIds(int slotIndex) {
+ public int[] getSubscriptionIds(int slotIndex) {
return getSubId(slotIndex);
}
@@ -2410,6 +2415,21 @@
return groupUUID;
}
+ /**
+ * Set metered by simInfo index
+ *
+ * @param isMetered whether it’s a metered subscription.
+ * @param subId the unique SubscriptionInfo index in database
+ * @return the number of records updated
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public int setMetered(boolean isMetered, int subId) {
+ if (VDBG) logd("[setIsMetered]+ isMetered:" + isMetered + " subId:" + subId);
+ return setSubscriptionPropertyHelper(subId, "setIsMetered",
+ (iSub)-> iSub.setMetered(isMetered, subId));
+ }
+
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 79ed93e..45d914e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -77,6 +77,7 @@
import com.android.internal.telecom.ITelecomService;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IAns;
+import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -1341,6 +1342,13 @@
*/
public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
+ /**
+ * The max value for the timeout passed in {@link #requestNumberVerification}.
+ * @hide
+ */
+ @SystemApi
+ public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000;
+
//
//
// Device Info
@@ -2243,9 +2251,11 @@
/** Current network is LTE_CA {@hide} */
@UnsupportedAppUsage
public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
+ /** Current network is NR(New Radio) 5G. */
+ public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
/** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
- public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA;
+ public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_NR;
/** @hide */
@IntDef({
@@ -2269,6 +2279,7 @@
NETWORK_TYPE_TD_SCDMA,
NETWORK_TYPE_IWLAN,
NETWORK_TYPE_LTE_CA,
+ NETWORK_TYPE_NR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface NetworkType{}
@@ -4214,7 +4225,8 @@
}
/**
- * Returns the voice mail count for a subscription. Return 0 if unavailable.
+ * Returns the voice mail count for a subscription. Return 0 if unavailable or the caller does
+ * not have the READ_PHONE_STATE permission.
* @param subId whose voice message count is returned
* @hide
*/
@@ -4225,7 +4237,7 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return 0;
- return telephony.getVoiceMessageCountForSubscriber(subId);
+ return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName());
} catch (RemoteException ex) {
return 0;
} catch (NullPointerException ex) {
@@ -5368,7 +5380,7 @@
/**
* Rollback modem configurations to factory default except some config which are in whitelist.
- * Used for device configuration by some CDMA operators.
+ * Used for device configuration by some carriers.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5395,7 +5407,7 @@
}
/**
- * Generate a radio modem reset. Used for device configuration by some CDMA operators.
+ * Generate a radio modem reset. Used for device configuration by some carriers.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5499,6 +5511,52 @@
}
/**
+ * Request that the next incoming call from a number matching {@code range} be intercepted.
+ *
+ * This API is intended for OEMs to provide a service for apps to verify the device's phone
+ * number. When called, the Telephony stack will store the provided {@link PhoneNumberRange} and
+ * intercept the next incoming call from a number that lies within the range, within a timeout
+ * specified by {@code timeoutMillis}.
+ *
+ * If such a phone call is received, the caller will be notified via
+ * {@link NumberVerificationCallback#onCallReceived(String)} on the provided {@link Executor}.
+ * If verification fails for any reason, the caller will be notified via
+ * {@link NumberVerificationCallback#onVerificationFailed(int)}
+ * on the provided {@link Executor}.
+ *
+ * In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of this
+ * API must also be listed in the device configuration as an authorized app in
+ * {@code packages/services/Telephony/res/values/config.xml} under the
+ * {@code config_number_verification_package_name} key.
+ *
+ * @hide
+ * @param range The range of phone numbers the caller expects a phone call from.
+ * @param timeoutMillis The amount of time to wait for such a call, or
+ * {@link #MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS}, whichever is lesser.
+ * @param executor The {@link Executor} that callbacks should be executed on.
+ * @param callback The callback to use for delivering results.
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull NumberVerificationCallback callback) {
+ INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() {
+ @Override
+ public void onCallReceived(String phoneNumber) throws RemoteException {
+ Binder.withCleanCallingIdentity(() -> callback.onCallReceived(phoneNumber));
+ }
+
+ @Override
+ public void onVerificationFailed(int reason) throws RemoteException {
+ Binder.withCleanCallingIdentity(() -> callback.onVerificationFailed(reason));
+ }
+ };
+
+ // TODO -- call the aidl method
+ }
+
+ /**
* Sets a per-phone telephony property with the value specified.
*
* @hide
@@ -8579,7 +8637,8 @@
/**
* Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
- * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
+ * All uicc applications are uniquely identified by application ID, represented by the hex
+ * string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
*
@@ -9216,6 +9275,7 @@
NETWORK_TYPE_BITMASK_TD_SCDMA,
NETWORK_TYPE_BITMASK_LTE,
NETWORK_TYPE_BITMASK_LTE_CA,
+ NETWORK_TYPE_BITMASK_NR,
})
public @interface NetworkTypeBitMask {}
@@ -9332,6 +9392,13 @@
public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA);
/**
+ * network type bitmask indicating the support of radio tech NR(New Radio) 5G.
+ * @hide
+ */
+ @SystemApi
+ public static final int NETWORK_TYPE_BITMASK_NR = (1 << NETWORK_TYPE_NR);
+
+ /**
* @return Modem supported radio access family bitmask
*
* <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
@@ -9520,4 +9587,34 @@
}
return subId;
}
+
+ /**
+ * Update availability of a list of networks in the current location.
+ *
+ * This api should be called to inform AlternativeNetwork Service about the availability
+ * of a network at the current location. This information will be used by AlternativeNetwork
+ * service to decide to attach to the network opportunistically. If an empty list is passed,
+ * it is assumed that no network is available.
+ * Requires that the calling app has carrier privileges on both primary and
+ * secondary subscriptions (see {@link #hasCarrierPrivileges}), or has permission
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ * @param availableNetworks is a list of available network information.
+ * @return true if request is accepted
+ *
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ public boolean updateAvailableNetworks(List<AvailableNetworkInfo> availableNetworks) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ boolean ret = false;
+ try {
+ IAns iAlternativeNetworkService = getIAns();
+ if (iAlternativeNetworkService != null) {
+ ret = iAlternativeNetworkService.updateAvailableNetworks(availableNetworks,
+ pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex);
+ }
+ return ret;
+ }
}
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 2d46ec2..41f7bd7 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -17,6 +17,7 @@
package android.telephony.emergency;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.hardware.radio.V1_4.EmergencyNumberSource;
import android.hardware.radio.V1_4.EmergencyServiceCategory;
import android.os.Parcel;
@@ -196,7 +197,7 @@
private final int mEmergencyNumberSourceBitmask;
/** @hide */
- public EmergencyNumber(String number, String countryIso,
+ public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
int emergencyServiceCategories,
int emergencyNumberSources) {
this.mNumber = number;
@@ -403,7 +404,7 @@
* 0 if both have equal display priority.
*/
@Override
- public int compareTo(EmergencyNumber emergencyNumber) {
+ public int compareTo(@NonNull EmergencyNumber emergencyNumber) {
if (this.getDisplayPriorityScore()
> emergencyNumber.getDisplayPriorityScore()) {
return -1;
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index f73036e..a6c24bf 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -23,6 +23,8 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.VideoProfile;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.util.Log;
import com.android.internal.telephony.PhoneConstants;
@@ -295,6 +297,28 @@
public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
/**
+ * The emergency service categories, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ private @EmergencyServiceCategories int mEmergencyServiceCategories =
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+
+ /**
* Extras associated with this {@link ImsCallProfile}.
* <p>
* Valid data types include:
@@ -495,6 +519,7 @@
out.writeInt(mCallType);
out.writeBundle(filteredExtras);
out.writeParcelable(mMediaProfile, 0);
+ out.writeInt(mEmergencyServiceCategories);
}
private void readFromParcel(Parcel in) {
@@ -502,6 +527,7 @@
mCallType = in.readInt();
mCallExtras = in.readBundle();
mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
+ mEmergencyServiceCategories = in.readInt();
}
public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
@@ -710,4 +736,53 @@
private static boolean isVideoStateSet(int videoState, int videoStateToCheck) {
return (videoState & videoStateToCheck) == videoStateToCheck;
}
+
+ /**
+ * Set the emergency service categories. The set value is valid only if
+ * {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ public void setEmergencyServiceCategories(
+ @EmergencyServiceCategories int emergencyServiceCategories) {
+ mEmergencyServiceCategories = emergencyServiceCategories;
+ }
+
+ /**
+ * Get the emergency service categories, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * @return the emergency service categories,
+ *
+ * If valid, the value is the bitwise-OR combination of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+ * </ol>
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ */
+ public @EmergencyServiceCategories int getEmergencyServiceCategories() {
+ return mEmergencyServiceCategories;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IAns.aidl b/telephony/java/com/android/internal/telephony/IAns.aidl
index e9a4649..98bcd41 100755
--- a/telephony/java/com/android/internal/telephony/IAns.aidl
+++ b/telephony/java/com/android/internal/telephony/IAns.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.telephony.AvailableNetworkInfo;
interface IAns {
@@ -78,4 +79,23 @@
*
*/
int getPreferredData(String callingPackage);
+
+ /**
+ * Update availability of a list of networks in the current location.
+ *
+ * This api should be called if the caller is aware of the availability of a network
+ * at the current location. This information will be used by AlternativeNetwork service
+ * to decide to attach to the network. If an empty list is passed,
+ * it is assumed that no network is available.
+ * Requires that the calling app has carrier privileges on both primary and
+ * secondary subscriptions (see
+ * {@link #hasCarrierPrivileges}), or has permission
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ * @param availableNetworks is a list of available network information.
+ * @param callingPackage caller's package name
+ * @return true if request is accepted
+ *
+ */
+ boolean updateAvailableNetworks(in List<AvailableNetworkInfo> availableNetworks,
+ String callingPackage);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
index 27d25b8..76918af 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
@@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.internal.telephony;
+
+oneway interface INumberVerificationCallback {
+ void onCallReceived(String phoneNumber);
+ void onVerificationFailed(int reason);
}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index bc44519..f9db4b0 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -184,6 +184,15 @@
String setSubscriptionGroup(in int[] subIdList, String callingPackage);
/**
+ * Set whether a subscription is metered
+ *
+ * @param isMetered whether it’s a metered subscription.
+ * @param subId the unique SubscriptionInfo index in database
+ * @return the number of records updated
+ */
+ int setMetered(boolean isMetered, int subId);
+
+ /**
* Set which subscription is preferred for cellular data. It's
* designed to overwrite default data subscription temporarily.
*
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index fc42de5..32e939a0 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -393,16 +393,11 @@
int getDataActivationState(int subId, String callingPackage);
/**
- * Returns the unread count of voicemails
- */
- int getVoiceMessageCount();
-
- /**
* Returns the unread count of voicemails for a subId.
* @param subId user preferred subId.
* Returns the unread count of voicemails
*/
- int getVoiceMessageCountForSubscriber(int subId);
+ int getVoiceMessageCountForSubscriber(int subId, String callingPackage);
/**
* Returns true if current state supports both voice and data
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 923ab06..76e7509 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -25,6 +25,7 @@
import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.emergency.EmergencyNumber;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -80,4 +81,5 @@
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
void notifyPreferredDataSubIdChanged(int preferredSubId);
void notifyRadioPowerStateChanged(in int state);
+ void notifyEmergencyNumberList(in List<EmergencyNumber> emergencyNumberList);
}
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index 7842a1c..5b58dd5 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -80,6 +80,7 @@
method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
+ method public java.lang.String getOpPackageName();
method public java.lang.String getPackageCodePath();
method public android.content.pm.PackageManager getPackageManager();
method public java.lang.String getPackageName();
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index f1ec000..8b2c815 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -1,9 +1,5 @@
package android.test.mock {
- public class MockContext extends android.content.Context {
- method public java.lang.String getOpPackageName();
- }
-
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
method public boolean arePermissionsIndividuallyControlled();
method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index 8d8fc84..b9e282e 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -38,7 +38,7 @@
public Engine onCreateEngine() {
return new Engine() {
@Override
- public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+ public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
ambientModeChangedCount[0]++;
}
};
@@ -47,12 +47,12 @@
WallpaperService.Engine engine = service.onCreateEngine();
engine.setCreated(true);
- engine.doAmbientModeChanged(false, false);
+ engine.doAmbientModeChanged(false, 0);
assertFalse("ambient mode should be false", engine.isInAmbientMode());
assertEquals("onAmbientModeChanged should have been called",
ambientModeChangedCount[0], 1);
- engine.doAmbientModeChanged(true, false);
+ engine.doAmbientModeChanged(true, 0);
assertTrue("ambient mode should be false", engine.isInAmbientMode());
assertEquals("onAmbientModeChanged should have been called",
ambientModeChangedCount[0], 2);
diff --git a/tests/TouchLatency/.gitignore b/tests/TouchLatency/.gitignore
index bd79078..7f4121a 100644
--- a/tests/TouchLatency/.gitignore
+++ b/tests/TouchLatency/.gitignore
@@ -3,4 +3,5 @@
/.idea
.DS_Store
/build
+/gen
.iml
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 2337110..2594322 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
+ compileSdkVersion 28
+ buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.prefabulated.touchlatency"
minSdkVersion 21
- targetSdkVersion 21
+ targetSdkVersion 28
versionCode 1
versionName "1.0"
}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index b4b5ca7..360c22f 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -19,11 +19,9 @@
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
-import android.os.CountDownTimer;
+import android.graphics.Paint.Align;
import android.os.Bundle;
-import android.text.method.Touch;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Menu;
@@ -31,15 +29,17 @@
import android.view.MotionEvent;
import android.view.View;
import android.os.Trace;
-
-import java.util.ArrayList;
-import java.util.Collections;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
class TouchLatencyView extends View implements View.OnTouchListener {
private static final String LOG_TAG = "TouchLatency";
private static final int BACKGROUND_COLOR = 0xFF400080;
private static final int INNER_RADIUS = 70;
- private static final int BALL_RADIUS = 100;
+ private static final int BALL_DIAMETER = 200;
+ private static final int SEC_TO_NANOS = 1000000000;
+ private static final float FPS_UPDATE_THRESHOLD = 20;
+ private static final long BALL_VELOCITY = 420;
public TouchLatencyView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -58,13 +58,17 @@
mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRedPaint.setColor(0xFFFF0000);
mRedPaint.setStyle(Paint.Style.FILL);
+ mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.setColor(0xFFFFFFFF);
+ mTextPaint.setTextSize(100);
+ mTextPaint.setTextAlign(Align.RIGHT);
mTouching = false;
- mBallX = 100.0f;
- mBallY = 100.0f;
- mVelocityX = 7.0f;
- mVelocityY = 7.0f;
+ mLastDrawNano = 0;
+ mFps = 0;
+ mLastFpsUpdate = 0;
+ mFrameCount = 0;
Trace.endSection();
}
@@ -113,43 +117,70 @@
}
}
+ private Paint getBallColor() {
+ if (mFps > 75)
+ return mGreenPaint;
+ else if (mFps > 45)
+ return mYellowPaint;
+ else
+ return mRedPaint;
+ }
+
private void drawBall(Canvas canvas) {
Trace.beginSection("TouchLatencyView drawBall");
int width = canvas.getWidth();
int height = canvas.getHeight();
+ float fps = 0f;
- // Update position
- mBallX += mVelocityX;
- mBallY += mVelocityY;
+ long t = System.nanoTime();
+ long tDiff = t - mLastDrawNano;
+ mLastDrawNano = t;
+ mFrameCount++;
- // Clamp and change velocity if necessary
- float left = mBallX - BALL_RADIUS;
- if (left < 0) {
- left = 0;
- mVelocityX *= -1;
+ if (tDiff < SEC_TO_NANOS) {
+ fps = 1f * SEC_TO_NANOS / tDiff;
}
- float top = mBallY - BALL_RADIUS;
- if (top < 0) {
- top = 0;
- mVelocityY *= -1;
+ long fDiff = t - mLastFpsUpdate;
+ if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
+ mFps = fps;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
+ } else if (fDiff > SEC_TO_NANOS) {
+ mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
}
- float right = mBallX + BALL_RADIUS;
- if (right > width) {
- right = width;
- mVelocityX *= -1;
- }
+ final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
+ final long xMax = width - BALL_DIAMETER;
+ final long yMax = height - BALL_DIAMETER;
+ long xOffset = pos % xMax;
+ long yOffset = pos % yMax;
- float bottom = mBallY + BALL_RADIUS;
- if (bottom > height) {
- bottom = height;
- mVelocityY *= -1;
+ float left, right, top, bottom;
+
+ if (((pos / xMax) & 1) == 0) {
+ left = xMax - xOffset;
+ } else {
+ left = xOffset;
}
+ right = left + BALL_DIAMETER;
+
+ if (((pos / yMax) & 1) == 0) {
+ top = yMax - yOffset;
+ } else {
+ top = yOffset;
+ }
+ bottom = top + BALL_DIAMETER;
// Draw the ball
canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawOval(left, top, right, bottom, mYellowPaint);
+ canvas.drawOval(left, top, right, bottom, getBallColor());
+ DecimalFormat df = new DecimalFormat("fps: #.##");
+ df.setRoundingMode(RoundingMode.HALF_UP);
+ canvas.drawText(df.format(mFps), width, 100, mTextPaint);
+
invalidate();
Trace.endSection();
}
@@ -176,15 +207,15 @@
Trace.endSection();
}
- private Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint;
+ private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
private int mMode;
private boolean mTouching;
private float mTouchX, mTouchY;
private float mLastDrawnX, mLastDrawnY;
- private float mBallX, mBallY;
- private float mVelocityX, mVelocityY;
+ private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
+ private float mFps;
}
public class TouchLatencyActivity extends Activity {
diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle
index d3ff69d..03abe82 100644
--- a/tests/TouchLatency/build.gradle
+++ b/tests/TouchLatency/build.gradle
@@ -3,9 +3,10 @@
buildscript {
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.1.0'
+ classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -15,5 +16,6 @@
allprojects {
repositories {
jcenter()
+ google()
}
}
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..111992a 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Tue Nov 27 13:37:59 PST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index ae3914e..d5987a5 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -26,6 +26,7 @@
import android.view.Display;
import android.view.DisplayCutout;
import android.view.IWindowSession;
+import android.view.InsetsState;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
@@ -105,7 +106,7 @@
window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
- new Surface());
+ new Surface(), new InsetsState());
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -131,8 +132,9 @@
final IWindowSession session = WindowManagerGlobal.getWindowSession();
final Rect tmpRect = new Rect();
try {
- final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, layoutParams,
- View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect);
+ final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq,
+ layoutParams, View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect,
+ new InsetsState());
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 4dc0341..d8f9618 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -542,7 +542,7 @@
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
- eq(pfd.getFileDescriptor()),
+ eq(pfd),
eq(mUid),
eq(IpSecManager.DIRECTION_OUT),
anyString(),
@@ -555,7 +555,7 @@
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
mIpSecService.removeTransportModeTransforms(pfd);
- verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+ verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
}
private IpSecTunnelInterfaceResponse createAndValidateTunnel(
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 2c94a60..724446e 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -425,7 +425,7 @@
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
mIpSecService.removeTransportModeTransforms(pfd);
- verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+ verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
}
@Test
@@ -620,10 +620,10 @@
mIpSecService.openUdpEncapsulationSocket(0, new Binder());
FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
- ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
+ ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> {
try {
StructStat sockStat = Os.fstat(sockFd);
- StructStat argStat = Os.fstat(arg);
+ StructStat argStat = Os.fstat(arg.getFileDescriptor());
return sockStat.st_ino == argStat.st_ino
&& sockStat.st_dev == argStat.st_dev;
diff --git a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
index b399b0d..6e07b26 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
@@ -40,6 +40,7 @@
import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiManager;
import android.os.Handler;
+import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -70,6 +71,7 @@
private @Mock Handler mHandler;
private @Mock IpConnectivityLog mLogger;
private @Mock NetworkAgentInfo mAgent;
+ private @Mock NetworkAgentInfo mNotMeteredAgent;
private @Mock NetworkInfo mNetworkInfo;
private @Mock NetworkRequest mRequest;
private @Mock TelephonyManager mTelephony;
@@ -87,6 +89,10 @@
private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
+ private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
+ private static final int RETURN_CODE_DNS_SUCCESS = 0;
+ private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+
@Before
public void setUp() throws IOException {
MockitoAnnotations.initMocks(this);
@@ -95,6 +101,12 @@
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
mAgent.networkInfo = mNetworkInfo;
+ mNotMeteredAgent.linkProperties = new LinkProperties();
+ mNotMeteredAgent.networkCapabilities = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ mNotMeteredAgent.networkInfo = mNetworkInfo;
+
when(mAgent.network()).thenReturn(mNetwork);
when(mDependencies.getNetwork(any())).thenReturn(mNetwork);
when(mDependencies.getRandom()).thenReturn(mRandom);
@@ -138,6 +150,40 @@
when(mNetwork.getAllByName(any())).thenReturn(new InetAddress[] {
InetAddress.parseNumericAddress("192.168.0.0")
});
+
+ setMinDataStallEvaluateInterval(500);
+ setDataStallEvaluationType(1 << DATA_STALL_EVALUATION_TYPE_DNS);
+ setValidDataStallDnsTimeThreshold(500);
+ setConsecutiveDnsTimeoutThreshold(5);
+ }
+
+ private class WrappedNetworkMonitor extends NetworkMonitor {
+ private long mProbeTime = 0;
+
+ WrappedNetworkMonitor(Context context, Handler handler,
+ NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
+ IpConnectivityLog logger, Dependencies deps) {
+ super(context, handler, networkAgentInfo, defaultRequest, logger, deps);
+ }
+
+ @Override
+ protected long getLastProbeTime() {
+ return mProbeTime;
+ }
+
+ protected void setLastProbeTime(long time) {
+ mProbeTime = time;
+ }
+ }
+
+ WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
+ return new WrappedNetworkMonitor(
+ mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
+ }
+
+ WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
+ return new WrappedNetworkMonitor(
+ mContext, mHandler, mNotMeteredAgent, mRequest, mLogger, mDependencies);
}
NetworkMonitor makeMonitor() {
@@ -272,6 +318,113 @@
assertPortal(makeMonitor().isCaptivePortal());
}
+ @Test
+ public void testIsDataStall_EvaluationDisabled() {
+ setDataStallEvaluationType(0);
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ assertFalse(wrappedMonitor.isDataStall());
+ }
+
+ @Test
+ public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertTrue(wrappedMonitor.isDataStall());
+ }
+
+ @Test
+ public void testIsDataStall_EvaluationDnsOnMeteredNetwork() {
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ assertFalse(wrappedMonitor.isDataStall());
+
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertTrue(wrappedMonitor.isDataStall());
+ }
+
+ @Test
+ public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() {
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 3);
+ assertFalse(wrappedMonitor.isDataStall());
+ // Reset consecutive timeout counts.
+ makeDnsSuccessEvent(wrappedMonitor, 1);
+ makeDnsTimeoutEvent(wrappedMonitor, 2);
+ assertFalse(wrappedMonitor.isDataStall());
+
+ makeDnsTimeoutEvent(wrappedMonitor, 3);
+ assertTrue(wrappedMonitor.isDataStall());
+
+ // Set the value to larger than the default dns log size.
+ setConsecutiveDnsTimeoutThreshold(51);
+ wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ makeDnsTimeoutEvent(wrappedMonitor, 50);
+ assertFalse(wrappedMonitor.isDataStall());
+
+ makeDnsTimeoutEvent(wrappedMonitor, 1);
+ assertTrue(wrappedMonitor.isDataStall());
+ }
+
+ @Test
+ public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() {
+ // Test dns events happened in valid dns time threshold.
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertFalse(wrappedMonitor.isDataStall());
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ assertTrue(wrappedMonitor.isDataStall());
+
+ // Test dns events happened before valid dns time threshold.
+ setValidDataStallDnsTimeThreshold(0);
+ wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+ makeDnsTimeoutEvent(wrappedMonitor, 5);
+ assertFalse(wrappedMonitor.isDataStall());
+ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+ assertFalse(wrappedMonitor.isDataStall());
+ }
+
+ private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
+ for (int i = 0; i < count; i++) {
+ wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
+ RETURN_CODE_DNS_TIMEOUT);
+ }
+ }
+
+ private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
+ for (int i = 0; i < count; i++) {
+ wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
+ RETURN_CODE_DNS_SUCCESS);
+ }
+ }
+
+ private void setDataStallEvaluationType(int type) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type);
+ }
+
+ private void setMinDataStallEvaluateInterval(int time) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time);
+ }
+
+ private void setValidDataStallDnsTimeThreshold(int time) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time);
+ }
+
+ private void setConsecutiveDnsTimeoutThreshold(int num) {
+ when(mDependencies.getSetting(any(),
+ eq(Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt()))
+ .thenReturn(num);
+ }
+
private void setFallbackUrl(String url) {
when(mDependencies.getSetting(any(),
eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url);
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index ed70fb3..df0daeb 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -240,6 +240,12 @@
}
break;
+ case android::RES_TABLE_OVERLAYABLE_TYPE:
+ if (!ParseOverlayable(parser.chunk())) {
+ return false;
+ }
+ break;
+
default:
diag_->Warn(DiagMessage(source_)
<< "unexpected chunk type "
@@ -383,24 +389,12 @@
return false;
}
- const uint32_t type_spec_flags = entry_type_spec_flags_[res_id];
- if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0 ||
- (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) != 0) {
- if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
- Visibility visibility;
- visibility.level = Visibility::Level::kPublic;
- visibility.source = source_.WithLine(0);
- if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
- return false;
- }
- }
-
- if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) {
- Overlayable overlayable;
- overlayable.source = source_.WithLine(0);
- if (!table_->AddOverlayableMangled(name, overlayable, diag_)) {
- return false;
- }
+ if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
+ Visibility visibility;
+ visibility.level = Visibility::Level::kPublic;
+ visibility.source = source_.WithLine(0);
+ if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
+ return false;
}
// Erase the ID from the map once processed, so that we don't mark the same symbol more than
@@ -433,6 +427,72 @@
return true;
}
+bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
+ const ResTable_overlayable_header* header = ConvertTo<ResTable_overlayable_header>(chunk);
+ if (!header) {
+ diag_->Error(DiagMessage(source_) << "corrupt ResTable_category_header chunk");
+ return false;
+ }
+
+ ResChunkPullParser parser(GetChunkData(chunk),
+ GetChunkDataLen(chunk));
+ while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+ if (util::DeviceToHost16(parser.chunk()->type) == android::RES_TABLE_OVERLAYABLE_POLICY_TYPE) {
+ const ResTable_overlayable_policy_header* policy_header =
+ ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
+
+ std::vector<Overlayable::Policy> policies;
+ if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
+ policies.push_back(Overlayable::Policy::kPublic);
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
+ policies.push_back(Overlayable::Policy::kSystem);
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
+ policies.push_back(Overlayable::Policy::kVendor);
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
+ policies.push_back(Overlayable::Policy::kProduct);
+ }
+ if (policy_header->policy_flags
+ & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
+ policies.push_back(Overlayable::Policy::kProductServices);
+ }
+
+ const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
+ ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
+ const ResTable_ref* const ref_end = ref_begin
+ + util::DeviceToHost32(policy_header->entry_count);
+ for (auto ref_iter = ref_begin; ref_iter != ref_end; ++ref_iter) {
+ ResourceId res_id(util::DeviceToHost32(ref_iter->ident));
+ const auto iter = id_index_.find(res_id);
+
+ // If the overlayable chunk comes before the type chunks, the resource ids and resource name
+ // pairing will not exist at this point.
+ if (iter == id_index_.cend()) {
+ diag_->Error(DiagMessage(source_) << "failed to find resource name for overlayable"
+ << " resource " << res_id);
+ return false;
+ }
+
+ for (Overlayable::Policy policy : policies) {
+ Overlayable overlayable;
+ overlayable.source = source_.WithLine(0);
+ overlayable.policy = policy;
+ if (!table_->AddOverlayable(iter->second, overlayable, diag_)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
const ConfigDescription& config,
const android::Res_value& value) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index 2bdc051..a2eee50 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -54,6 +54,7 @@
bool ParseTypeSpec(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool ParseLibrary(const android::ResChunk_header* chunk);
+ bool ParseOverlayable(const android::ResChunk_header* chunk);
std::unique_ptr<Item> ParseValue(const ResourceNameRef& name,
const android::ConfigDescription& config,
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 6c1a9ba..976c328 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -24,6 +24,7 @@
#include "android-base/logging.h"
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceUtils.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
@@ -216,6 +217,11 @@
size_t entry_count_ = 0;
};
+struct PolicyChunk {
+ uint32_t policy_flags;
+ std::set<ResourceId> ids;
+};
+
class PackageFlattener {
public:
PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
@@ -267,6 +273,8 @@
FlattenLibrarySpec(buffer);
}
+ FlattenOverlayable(buffer);
+
pkg_writer.Finish();
return true;
}
@@ -413,6 +421,97 @@
return sorted_entries;
}
+ void FlattenOverlayable(BigBuffer* buffer) {
+ std::vector<PolicyChunk> policies;
+
+ CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>";
+ for (auto& type : package_->types) {
+ CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
+ for (auto& entry : type->entries) {
+ CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
+
+ // TODO(b/120298168): Convert the policies vector to a policy set or bitmask
+ if (!entry->overlayable_declarations.empty()) {
+ uint16_t policy_flags = 0;
+ for (Overlayable overlayable : entry->overlayable_declarations) {
+ if (overlayable.policy) {
+ switch (overlayable.policy.value()) {
+ case Overlayable::Policy::kPublic:
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+ break;
+ case Overlayable::Policy::kSystem:
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+ break;
+ case Overlayable::Policy::kVendor:
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+ break;
+ case Overlayable::Policy::kProduct:
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+ break;
+ case Overlayable::Policy::kProductServices:
+ policy_flags |=
+ ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
+ break;
+ }
+ } else {
+ // Encode overlayable entries defined without a policy as publicly overlayable
+ policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+ }
+ }
+
+ // Find the overlayable policy chunk with the same policies as the entry
+ PolicyChunk* policy_chunk = nullptr;
+ for (PolicyChunk& policy : policies) {
+ if (policy.policy_flags == policy_flags) {
+ policy_chunk = &policy;
+ break;
+ }
+ }
+
+ // Create a new policy chunk if an existing one with the same policy cannot be found
+ if (policy_chunk == nullptr) {
+ PolicyChunk p;
+ p.policy_flags = policy_flags;
+ policies.push_back(p);
+ policy_chunk = &policies.back();
+ }
+
+ policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
+ entry->id.value()));
+ }
+ }
+ }
+
+ if (policies.empty()) {
+ // Only write the overlayable chunk if the APK has overlayable entries
+ return;
+ }
+
+ ChunkWriter writer(buffer);
+ writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
+
+ // Write each policy block for the overlayable
+ for (PolicyChunk& policy : policies) {
+ ChunkWriter policy_writer(buffer);
+ ResTable_overlayable_policy_header* policy_type =
+ policy_writer.StartChunk<ResTable_overlayable_policy_header>(
+ RES_TABLE_OVERLAYABLE_POLICY_TYPE);
+ policy_type->policy_flags = util::HostToDevice32(policy.policy_flags);
+ policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size()));
+
+ // Write the ids after the policy header
+ ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size());
+ for (const ResourceId& id : policy.ids) {
+ id_block->ident = util::HostToDevice32(id.id);
+ id_block++;
+ }
+
+ policy_writer.Finish();
+ }
+
+ writer.Finish();
+ }
+
bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
BigBuffer* buffer) {
ChunkWriter type_spec_writer(buffer);
@@ -446,11 +545,6 @@
config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
- if (!entry->overlayable_declarations.empty()) {
- config_masks[entry->id.value()] |=
- util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE);
- }
-
const size_t config_count = entry->values.size();
for (size_t i = 0; i < config_count; i++) {
const ConfigDescription& config = entry->values[i]->config;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index cd1414c7e..410efbe 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,24 +628,108 @@
}
TEST_F(TableFlattenerTest, FlattenOverlayable) {
+ std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
- .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000))
+ .AddSimple(name, ResourceId(0x7f020000))
+ .AddOverlayable(name, Overlayable::Policy::kProduct)
+ .AddOverlayable(name, Overlayable::Policy::kSystem)
+ .AddOverlayable(name, Overlayable::Policy::kVendor)
.Build();
- ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
- Overlayable{}, test::GetDiagnostics()));
+ ResourceTable output_table;
+ ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
- ResTable res_table;
- ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
-
- const StringPiece16 overlayable_name(u"com.app.test:integer/overlayable");
- uint32_t spec_flags = 0u;
- ASSERT_THAT(res_table.identifierForName(overlayable_name.data(), overlayable_name.size(), nullptr,
- 0u, nullptr, 0u, &spec_flags),
- Gt(0u));
- EXPECT_TRUE(spec_flags & android::ResTable_typeSpec::SPEC_OVERLAYABLE);
+ auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kSystem);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+ Overlayable::Policy::kVendor);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+ Overlayable::Policy::kProduct);
}
+TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
+ std::string name_zero = "com.app.test:integer/overlayable_zero";
+ std::string name_one = "com.app.test:integer/overlayable_one";
+ std::string name_two = "com.app.test:integer/overlayable_two";
+ std::string name_three = "com.app.test:integer/overlayable_three";
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddSimple(name_zero, ResourceId(0x7f020000))
+ .AddOverlayable(name_zero, Overlayable::Policy::kProduct)
+ .AddOverlayable(name_zero, Overlayable::Policy::kSystem)
+ .AddOverlayable(name_zero, Overlayable::Policy::kProductServices)
+ .AddSimple(name_one, ResourceId(0x7f020001))
+ .AddOverlayable(name_one, Overlayable::Policy::kPublic)
+ .AddOverlayable(name_one, Overlayable::Policy::kSystem)
+ .AddSimple(name_two, ResourceId(0x7f020002))
+ .AddOverlayable(name_two, Overlayable::Policy::kProduct)
+ .AddOverlayable(name_two, Overlayable::Policy::kSystem)
+ .AddOverlayable(name_two, Overlayable::Policy::kProductServices)
+ .AddSimple(name_three, ResourceId(0x7f020003))
+ .AddOverlayable(name_three, {})
+ .Build();
+
+ ResourceTable output_table;
+ ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
+
+ auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kSystem);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+ Overlayable::Policy::kProduct);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+ Overlayable::Policy::kProductServices);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 2);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kPublic);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+ Overlayable::Policy::kSystem);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kSystem);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+ Overlayable::Policy::kProduct);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+ Overlayable::Policy::kProductServices);
+
+ search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 1);
+ EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+ EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+ Overlayable::Policy::kPublic);
+
+}
+
+
} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index d1a70a7..31d205e 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -298,19 +298,20 @@
"<colgroup align=\"left\" />\n"
"<tr><th>Attribute</th><th>Description</th></tr>\n";
- // Build the table of attributes with their links and names.
- for (const StyleableAttr& entry : sorted_attributes) {
- if (SkipSymbol(entry.symbol)) {
- continue;
- }
-
+ // Removed and hidden attributes are public but hidden from the documentation, so don't emit
+ // them as part of the class documentation.
+ std::vector<StyleableAttr> documentation_attrs = sorted_attributes;
+ auto documentation_remove_iter = std::remove_if(documentation_attrs.begin(),
+ documentation_attrs.end(),
+ [&](StyleableAttr entry) -> bool {
StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
- if (attr_comment_line.contains("@removed")) {
- // Removed attributes are public but hidden from the documentation, so
- // don't emit them as part of the class documentation.
- continue;
- }
+ return SkipSymbol(entry.symbol) || attr_comment_line.contains("@removed")
+ || attr_comment_line.contains("@hide");
+ });
+ documentation_attrs.erase(documentation_remove_iter, documentation_attrs.end());
+ // Build the table of attributes with their links and names.
+ for (const StyleableAttr& entry : documentation_attrs) {
const ResourceName& attr_name = entry.attr_ref->name.value();
styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
<< (!attr_name.package.empty() ? attr_name.package
@@ -320,16 +321,14 @@
// Only use the comment up until the first '.'. This is to stay compatible with
// the way old AAPT did it (presumably to keep it short and to avoid including
// annotations like @hide which would affect this Styleable).
+ StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
styleable_comment << "<td>" << AnnotationProcessor::ExtractFirstSentence(attr_comment_line)
<< "</td></tr>\n";
}
styleable_comment << "</table>\n";
// Generate the @see lines for each attribute.
- for (const StyleableAttr& entry : sorted_attributes) {
- if (SkipSymbol(entry.symbol)) {
- continue;
- }
+ for (const StyleableAttr& entry : documentation_attrs) {
styleable_comment << "@see #" << entry.field_name << "\n";
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index fa208be..4f51fc4 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -366,6 +366,46 @@
ASSERT_TRUE(generator.Generate("android", &out));
out.Flush();
+ EXPECT_THAT(output, HasSubstr("#Container_one android:one"));
+ EXPECT_THAT(output, HasSubstr("@see #Container_one"));
+ EXPECT_THAT(output, HasSubstr("attr name android:one"));
+ EXPECT_THAT(output, HasSubstr("attr description"));
+ EXPECT_THAT(output, HasSubstr(attr.GetComment()));
+ EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
+}
+
+TEST(JavaClassGeneratorTest, CommentsForStyleableHiddenAttributesAreNotPresent) {
+ Attribute attr;
+ attr.SetComment(StringPiece("This is an attribute @hide"));
+
+ Styleable styleable;
+ styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
+ styleable.SetComment(StringPiece("This is a styleable"));
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
+ .AddValue("android:styleable/Container",
+ std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGeneratorOptions options;
+ options.use_final = false;
+ JavaClassGenerator generator(context.get(), table.get(), options);
+
+ std::string output;
+ StringOutputStream out(&output);
+ ASSERT_TRUE(generator.Generate("android", &out));
+ out.Flush();
+
+ EXPECT_THAT(output, Not(HasSubstr("#Container_one android:one")));
+ EXPECT_THAT(output, Not(HasSubstr("@see #Container_one")));
EXPECT_THAT(output, HasSubstr("attr name android:one"));
EXPECT_THAT(output, HasSubstr("attr description"));
EXPECT_THAT(output, HasSubstr(attr.GetComment()));
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 3a5d585..1b6626a 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -368,7 +368,16 @@
// Symbol state information may be lost if there is no value for the resource.
if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
- << "no definition for declared symbol '" << name << "'");
+ << "no definition for declared symbol '" << name
+ << "'");
+ error = true;
+ }
+
+ // Ensure that definitions for values declared as overlayable exist
+ if (!entry->overlayable_declarations.empty() && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_declarations[0].source)
+ << "no definition for overlayable symbol '"
+ << name << "'");
error = true;
}
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index e50c70d..f25fcdc 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -19,6 +19,6 @@
echo "If your change contains no confidential details (such as security fixes), please"
echo "upload and merge this change at https://android-review.googlesource.com/."
echo
- exit 77
+ exit 1
fi
fi
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 91cd1cb..cb8fef9 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1285,10 +1285,19 @@
if clazz.fullname == "android.os.UserManager": return
for m in clazz.methods:
- if m.name.endswith("AsUser") or m.name.endswith("ForUser"): continue
if re.match("on[A-Z]+", m.name): continue
- if "android.os.UserHandle" in m.args:
- warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' or 'queryFooForUser'")
+
+ has_arg = "android.os.UserHandle" in m.args
+ has_name = m.name.endswith("AsUser") or m.name.endswith("ForUser")
+
+ if clazz.fullname.endswith("Manager") and has_arg:
+ warn(clazz, m, None, "When a method overload is needed to target a specific "
+ "UserHandle, callers should be directed to use "
+ "Context.createPackageContextAsUser() and re-obtain the relevant "
+ "Manager, and no new API should be added")
+ elif has_arg and not has_name:
+ warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' "
+ "or 'queryFooForUser'")
def verify_params(clazz):
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
new file mode 100755
index 0000000..2291e5a
--- /dev/null
+++ b/tools/hiddenapi/exclude.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+set -e
+# Make sure that entries are not added for packages that are already fully handled using
+# annotations.
+LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
+# Each team should add a <team>_PACKAGES and <team>_EMAIL with the list of packages and
+# the team email to use in the event of this detecting an entry in a <team> package. Also
+# add <team> to the TEAMS list.
+LIBCORE_PACKAGES="\
+ android.icu \
+ android.system \
+ com.android.bouncycastle \
+ com.android.conscrypt \
+ com.android.okhttp \
+ com.sun \
+ dalvik \
+ java \
+ javax \
+ libcore \
+ org.apache.harmony \
+ org.json \
+ org.w3c.dom \
+ org.xml.sax \
+ sun \
+ "
+LIBCORE_EMAIL=libcore-team@android.com
+
+# List of teams.
+TEAMS=LIBCORE
+
+# Generate the list of packages and convert to a regular expression.
+PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
+RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+ ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file))
+ if [[ -n "${ENTRIES}" ]]; then
+ echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m"
+ echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m"
+ echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m"
+ # Partition the entries by team and provide contact details to aid in fixing the issue.
+ for t in ${TEAMS}
+ do
+ PACKAGES=$(eval echo \${${t}_PACKAGES})
+ RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+ TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}"))
+ if [[ -n "${TEAM_ENTRIES}" ]]; then
+ EMAIL=$(eval echo \${${t}_EMAIL})
+ echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m"
+ for i in ${ENTRIES}
+ do
+ echo -e "\e[33m ${i}\e[0m"
+ done
+ fi
+ done
+ exit 1
+ fi
+done
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index fdc800b..01728fa1 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -15,23 +15,56 @@
# limitations under the License.
"""
Generate API lists for non-SDK API enforcement.
-
-usage: generate-hiddenapi-lists.py [-h]
- --input-public INPUT_PUBLIC
- --input-private INPUT_PRIVATE
- [--input-whitelists [INPUT_WHITELISTS [INPUT_WHITELISTS ...]]]
- [--input-greylists [INPUT_GREYLISTS [INPUT_GREYLISTS ...]]]
- [--input-blacklists [INPUT_BLACKLISTS [INPUT_BLACKLISTS ...]]]
- --output-whitelist OUTPUT_WHITELIST
- --output-light-greylist OUTPUT_LIGHT_GREYLIST
- --output-dark-greylist OUTPUT_DARK_GREYLIST
- --output-blacklist OUTPUT_BLACKLIST
"""
import argparse
import os
import sys
import re
+# Names of flags recognized by the `hiddenapi` tool.
+FLAG_WHITELIST = "whitelist"
+FLAG_GREYLIST = "greylist"
+FLAG_BLACKLIST = "blacklist"
+FLAG_GREYLIST_MAX_O = "greylist-max-o"
+FLAG_GREYLIST_MAX_P = "greylist-max-p"
+
+# List of all known flags.
+FLAGS = [
+ FLAG_WHITELIST,
+ FLAG_GREYLIST,
+ FLAG_BLACKLIST,
+ FLAG_GREYLIST_MAX_O,
+ FLAG_GREYLIST_MAX_P,
+]
+FLAGS_SET = set(FLAGS)
+
+# Suffix used in command line args to express that only known and
+# otherwise unassigned entries should be assign the given flag.
+# For example, the P dark greylist is checked in as it was in P,
+# but signatures have changes since then. The flag instructs this
+# script to skip any entries which do not exist any more.
+FLAG_IGNORE_CONFLICTS_SUFFIX = "-ignore-conflicts"
+
+# Regex patterns of fields/methods used in serialization. These are
+# considered public API despite being hidden.
+SERIALIZATION_PATTERNS = [
+ r'readObject\(Ljava/io/ObjectInputStream;\)V',
+ r'readObjectNoData\(\)V',
+ r'readResolve\(\)Ljava/lang/Object;',
+ r'serialVersionUID:J',
+ r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
+ r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
+ r'writeReplace\(\)Ljava/lang/Object;',
+]
+
+# Single regex used to match serialization API. It combines all the
+# SERIALIZATION_PATTERNS into a single regular expression.
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+
+# Predicates to be used with filter_apis.
+IS_UNASSIGNED = lambda api, flags: not flags
+IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
+
def get_args():
"""Parses command line arguments.
@@ -39,21 +72,21 @@
Namespace: dictionary of parsed arguments
"""
parser = argparse.ArgumentParser()
- parser.add_argument('--input-public', required=True, help='List of all public members')
- parser.add_argument('--input-private', required=True, help='List of all private members')
- parser.add_argument(
- '--input-whitelists', nargs='*',
- help='Lists of members to force on whitelist')
- parser.add_argument(
- '--input-greylists', nargs='*',
- help='Lists of members to force on light greylist')
- parser.add_argument(
- '--input-blacklists', nargs='*',
- help='Lists of members to force on blacklist')
- parser.add_argument('--output-whitelist', required=True)
- parser.add_argument('--output-light-greylist', required=True)
- parser.add_argument('--output-dark-greylist', required=True)
- parser.add_argument('--output-blacklist', required=True)
+ parser.add_argument('--output', required=True)
+ parser.add_argument('--public', required=True, help='list of all public entries')
+ parser.add_argument('--private', required=True, help='list of all private entries')
+ parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+ help='CSV files to be merged into output')
+
+ for flag in FLAGS:
+ ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX
+ parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE',
+ help='lists of entries with flag "' + flag + '"')
+ parser.add_argument('--' + ignore_conflicts_flag, dest=ignore_conflicts_flag, nargs='*',
+ default=[], metavar='TXT_FILE',
+ help='lists of entries with flag "' + flag +
+ '". skip entry if missing or flag conflict.')
+
return parser.parse_args()
def read_lines(filename):
@@ -65,10 +98,13 @@
filename (string): Path to the file to read from.
Returns:
- list: Lines of the loaded file as a list of strings.
+ Lines of the file as a list of string.
"""
with open(filename, 'r') as f:
- return filter(lambda line: not line.startswith('#'), f.readlines())
+ lines = f.readlines();
+ lines = filter(lambda line: not line.startswith('#'), lines)
+ lines = map(lambda line: line.strip(), lines)
+ return set(lines)
def write_lines(filename, lines):
"""Writes list of lines into a file, overwriting the file it it exists.
@@ -77,167 +113,168 @@
filename (string): Path to the file to be writting into.
lines (list): List of strings to write into the file.
"""
+ lines = map(lambda line: line + '\n', lines)
with open(filename, 'w') as f:
f.writelines(lines)
-def move_between_sets(subset, src, dst, source = "<unknown>"):
- """Removes a subset of elements from one set and add it to another.
+class FlagsDict:
+ def __init__(self, public_api, private_api):
+ # Bootstrap the entries dictionary.
- Args:
- subset (set): The subset of `src` to be moved from `src` to `dst`.
- src (set): Source set. Must be a superset of `subset`.
- dst (set): Destination set. Must be disjoint with `subset`.
- """
- assert src.issuperset(subset), (
- "Error processing: {}\n"
- "The following entries were not found:\n"
- "{}"
- "Please visit go/hiddenapi for more information.").format(
- source, "".join(map(lambda x: " " + str(x), subset.difference(src))))
- assert dst.isdisjoint(subset)
- # Order matters if `src` and `subset` are the same object.
- dst.update(subset)
- src.difference_update(subset)
+ # Check that the two sets do not overlap.
+ public_api_set = set(public_api)
+ private_api_set = set(private_api)
+ assert public_api_set.isdisjoint(private_api_set), (
+ "Lists of public and private API overlap. " +
+ "This suggests an issue with the `hiddenapi` build tool.")
-def get_package_name(signature):
- """Returns the package name prefix of a class member signature.
+ # Compute the whole key set
+ self._dict_keyset = public_api_set.union(private_api_set)
- Example: "Ljava/lang/String;->hashCode()J" --> "Ljava/lang/"
+ # Create a dict that creates entries for both public and private API,
+ # and assigns public API to the whitelist.
+ self._dict = {}
+ for api in public_api:
+ self._dict[api] = set([ FLAG_WHITELIST ])
+ for api in private_api:
+ self._dict[api] = set()
- Args:
- signature (string): Member signature
+ def _check_entries_set(self, keys_subset, source):
+ assert isinstance(keys_subset, set)
+ assert keys_subset.issubset(self._dict_keyset), (
+ "Error processing: {}\n"
+ "The following entries were unexpected:\n"
+ "{}"
+ "Please visit go/hiddenapi for more information.").format(
+ source, "".join(map(lambda x: " " + str(x), keys_subset - self._dict_keyset)))
- Returns
- string: Package name of the given member
- """
- class_name_end = signature.find("->")
- assert class_name_end != -1, "Invalid signature: {}".format(signature)
- package_name_end = signature.rfind("/", 0, class_name_end)
- assert package_name_end != -1, "Invalid signature: {}".format(signature)
- return signature[:package_name_end + 1]
+ def _check_flags_set(self, flags_subset, source):
+ assert isinstance(flags_subset, set)
+ assert flags_subset.issubset(FLAGS_SET), (
+ "Error processing: {}\n"
+ "The following flags were not recognized: \n"
+ "{}\n"
+ "Please visit go/hiddenapi for more information.").format(
+ source, "\n".join(flags_subset - FLAGS_SET))
-def all_package_names(*args):
- """Returns a set of packages names in given lists of member signatures.
+ def filter_apis(self, filter_fn):
+ """Returns APIs which match a given predicate.
- Example: args = [ set([ "Lpkg1/ClassA;->foo()V", "Lpkg2/ClassB;->bar()J" ]),
- set([ "Lpkg1/ClassC;->baz()Z" ]) ]
- return value = set([ "Lpkg1/", "Lpkg2" ])
+ This is a helper function which allows to filter on both signatures (keys) and
+ flags (values). The built-in filter() invokes the lambda only with dict's keys.
- Args:
- *args (list): List of sets to iterate over and extract the package names
- of its elements (member signatures)
+ Args:
+ filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
- Returns:
- set: All package names extracted from the given lists of signatures.
- """
- packages = set()
- for arg in args:
- packages = packages.union(map(get_package_name, arg))
- return packages
+ Returns:
+ A set of APIs which match the predicate.
+ """
+ return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
-def move_all(src, dst):
- """Moves all elements of one set to another.
+ def get_valid_subset_of_unassigned_apis(self, api_subset):
+ """Sanitizes a key set input to only include keys which exist in the dictionary
+ and have not been assigned any flags.
- Args:
- src (set): Source set. Will become empty.
- dst (set): Destination set. Will contain all elements of `src`.
- """
- move_between_sets(src, src, dst)
+ Args:
+ entries_subset (set/list): Key set to be sanitized.
-def move_from_files(filenames, src, dst):
- """Loads member signatures from a list of files and moves them to a given set.
+ Returns:
+ Sanitized key set.
+ """
+ assert isinstance(api_subset, set)
+ return api_subset.intersection(self.filter_apis(IS_UNASSIGNED))
- Opens files in `filenames`, reads all their lines and moves those from `src`
- set to `dst` set.
+ def generate_csv(self):
+ """Constructs CSV entries from a dictionary.
- Args:
- filenames (list): List of paths to files to be loaded.
- src (set): Set that loaded lines should be moved from.
- dst (set): Set that loaded lines should be moved to.
- """
- if filenames:
- for filename in filenames:
- move_between_sets(set(read_lines(filename)), src, dst, filename)
+ Returns:
+ List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+ """
+ return sorted(map(lambda api: ",".join([api] + sorted(self._dict[api])), self._dict))
-def move_serialization(src, dst):
- """Moves all members matching serialization API signatures between given sets.
+ def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+ """Parses CSV entries and merges them into a given dictionary.
- Args:
- src (set): Set that will be searched for serialization API and that API
- will be removed from it.
- dst (set): Set that serialization API will be moved to.
- """
- serialization_patterns = [
- r'readObject\(Ljava/io/ObjectInputStream;\)V',
- r'readObjectNoData\(\)V',
- r'readResolve\(\)Ljava/lang/Object;',
- r'serialVersionUID:J',
- r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
- r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
- r'writeReplace\(\)Ljava/lang/Object;',
- ]
- regex = re.compile(r'.*->(' + '|'.join(serialization_patterns) + r')$')
- move_between_sets(filter(lambda api: regex.match(api), src), src, dst)
+ The expected CSV format is:
+ <api signature>,<flag1>,<flag2>,...,<flagN>
-def move_from_packages(packages, src, dst):
- """Moves all members of given package names from one set to another.
+ Args:
+ csv_lines (list of strings): Lines read from a CSV file.
+ source (string): Origin of `csv_lines`. Will be printed in error messages.
- Args:
- packages (list): List of string package names.
- src (set): Set that will be searched for API matching one of the given
- package names. Surch API will be removed from the set.
- dst (set): Set that matching API will be moved to.
- """
- move_between_sets(filter(lambda api: get_package_name(api) in packages, src), src, dst)
+ Throws:
+ AssertionError if parsed API signatures of flags are invalid.
+ """
+ # Split CSV lines into arrays of values.
+ csv_values = [ line.split(',') for line in csv_lines ]
+
+ # Check that all entries exist in the dict.
+ csv_keys = set([ csv[0] for csv in csv_values ])
+ self._check_entries_set(csv_keys, source)
+
+ # Check that all flags are known.
+ csv_flags = set(reduce(lambda x, y: set(x).union(y), [ csv[1:] for csv in csv_values ], []))
+ self._check_flags_set(csv_flags, source)
+
+ # Iterate over all CSV lines, find entry in dict and append flags to it.
+ for csv in csv_values:
+ self._dict[csv[0]].update(csv[1:])
+
+ def assign_flag(self, flag, apis, source="<unknown>"):
+ """Assigns a flag to given subset of entries.
+
+ Args:
+ flag (string): One of FLAGS.
+ apis (set): Subset of APIs to recieve the flag.
+ source (string): Origin of `entries_subset`. Will be printed in error messages.
+
+ Throws:
+ AssertionError if parsed API signatures of flags are invalid.
+ """
+ # Check that all APIs exist in the dict.
+ self._check_entries_set(apis, source)
+
+ # Check that the flag is known.
+ self._check_flags_set(set([ flag ]), source)
+
+ # Iterate over the API subset, find each entry in dict and assign the flag to it.
+ for api in apis:
+ self._dict[api].add(flag)
def main(argv):
- args = get_args()
+ # Parse arguments.
+ args = vars(get_args())
- # Initialize API sets by loading lists of public and private API. Public API
- # are all members resolvable from SDK API stubs, other members are private.
- # As an optimization, skip the step of moving public API from a full set of
- # members and start with a populated whitelist.
- whitelist = set(read_lines(args.input_public))
- uncategorized = set(read_lines(args.input_private))
- light_greylist = set()
- dark_greylist = set()
- blacklist = set()
+ flags = FlagsDict(read_lines(args["public"]), read_lines(args["private"]))
- # Assert that there is no overlap between public and private API.
- assert whitelist.isdisjoint(uncategorized)
- num_all_api = len(whitelist) + len(uncategorized)
+ # Combine inputs which do not require any particular order.
+ # (1) Assign serialization API to whitelist.
+ flags.assign_flag(FLAG_WHITELIST, flags.filter_apis(IS_SERIALIZATION))
- # Read all files which manually assign members to specific lists.
- move_from_files(args.input_whitelists, uncategorized, whitelist)
- move_from_files(args.input_greylists, uncategorized, light_greylist)
- move_from_files(args.input_blacklists, uncategorized, blacklist)
+ # (2) Merge input CSV files into the dictionary.
+ for filename in args["csv"]:
+ flags.parse_and_merge_csv(read_lines(filename), filename)
- # Iterate over all uncategorized members and move serialization API to whitelist.
- move_serialization(uncategorized, whitelist)
+ # (3) Merge text files with a known flag into the dictionary.
+ for flag in FLAGS:
+ for filename in args[flag]:
+ flags.assign_flag(flag, read_lines(filename), filename)
- # Extract package names of members from whitelist and light greylist, which
- # are assumed to have been finalized at this point. Assign all uncategorized
- # members from the same packages to the dark greylist.
- dark_greylist_packages = all_package_names(whitelist, light_greylist)
- move_from_packages(dark_greylist_packages, uncategorized, dark_greylist)
+ # Merge text files where conflicts should be ignored.
+ # This will only assign the given flag if:
+ # (a) the entry exists, and
+ # (b) it has not been assigned any other flag.
+ # Because of (b), this must run after all strict assignments have been performed.
+ for flag in FLAGS:
+ for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]:
+ valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename))
+ flags.assign_flag(flag, valid_entries, filename)
- # Assign all uncategorized members to the blacklist.
- move_all(uncategorized, blacklist)
+ # Assign all remaining entries to the blacklist.
+ flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(IS_UNASSIGNED))
- # Assert we have not missed anything.
- assert whitelist.isdisjoint(light_greylist)
- assert whitelist.isdisjoint(dark_greylist)
- assert whitelist.isdisjoint(blacklist)
- assert light_greylist.isdisjoint(dark_greylist)
- assert light_greylist.isdisjoint(blacklist)
- assert dark_greylist.isdisjoint(blacklist)
- assert num_all_api == len(whitelist) + len(light_greylist) + len(dark_greylist) + len(blacklist)
-
- # Write final lists to disk.
- write_lines(args.output_whitelist, whitelist)
- write_lines(args.output_light_greylist, light_greylist)
- write_lines(args.output_dark_greylist, dark_greylist)
- write_lines(args.output_blacklist, blacklist)
+ # Write output.
+ write_lines(args["output"], flags.generate_csv())
if __name__ == "__main__":
main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
index 4716241..249f37d 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py
@@ -2,14 +2,14 @@
#
# Copyright (C) 2018 The Android Open Source Project
#
-# Licensed under the Apache License, Version 2.0 (the "License");
+# 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,
+# 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.
@@ -18,90 +18,90 @@
from generate_hiddenapi_lists import *
class TestHiddenapiListGeneration(unittest.TestCase):
+ def test_init(self):
+ # Check empty lists
+ flags = FlagsDict([], [])
+ self.assertEquals(flags.generate_csv(), [])
- def test_move_between_sets(self):
- A = set([1, 2, 3, 4])
- B = set([5, 6, 7, 8])
- move_between_sets(set([2, 4]), A, B)
- self.assertEqual(A, set([1, 3]))
- self.assertEqual(B, set([2, 4, 5, 6, 7, 8]))
+ # Check valid input - two public and two private API signatures.
+ flags = FlagsDict(['A', 'B'], ['C', 'D'])
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_WHITELIST, 'B,' + FLAG_WHITELIST, 'C', 'D' ])
- def test_move_between_sets_fail_not_superset(self):
- A = set([1, 2, 3, 4])
- B = set([5, 6, 7, 8])
- with self.assertRaises(AssertionError) as ar:
- move_between_sets(set([0, 2]), A, B)
+ # Check invalid input - overlapping public/private API signatures.
+ with self.assertRaises(AssertionError):
+ flags = FlagsDict(['A', 'B'], ['B', 'C', 'D'])
- def test_move_between_sets_fail_not_disjoint(self):
- A = set([1, 2, 3, 4])
- B = set([4, 5, 6, 7, 8])
- with self.assertRaises(AssertionError) as ar:
- move_between_sets(set([1, 4]), A, B)
+ def test_filter_apis(self):
+ # Initialize flags so that A and B are put on the whitelist and
+ # C, D, E are left unassigned. Try filtering for the unassigned ones.
+ flags = FlagsDict(['A', 'B'], ['C', 'D', 'E'])
+ filter_set = flags.filter_apis(lambda api, flags: not flags)
+ self.assertTrue(isinstance(filter_set, set))
+ self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
- def test_get_package_name(self):
- self.assertEqual(get_package_name("Ljava/lang/String;->clone()V"), "Ljava/lang/")
+ def test_get_valid_subset_of_unassigned_keys(self):
+ # Create flags where only A is unassigned.
+ flags = FlagsDict(['A'], ['B', 'C'])
+ flags.assign_flag(FLAG_GREYLIST, set(['C']))
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_WHITELIST, 'B', 'C,' + FLAG_GREYLIST ])
- def test_get_package_name_fail_no_arrow(self):
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;-clone()V")
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;>clone()V")
- with self.assertRaises(AssertionError) as ar:
- get_package_name("Ljava/lang/String;__clone()V")
-
- def test_get_package_name_fail_no_package(self):
- with self.assertRaises(AssertionError) as ar:
- get_package_name("LString;->clone()V")
-
- def test_all_package_names(self):
- self.assertEqual(all_package_names(), set())
- self.assertEqual(all_package_names(set(["Lfoo/Bar;->baz()V"])), set(["Lfoo/"]))
+ # Check three things:
+ # (1) B is selected as valid unassigned
+ # (2) A is not selected because it is assigned 'whitelist'
+ # (3) D is not selected because it is not a valid key
self.assertEqual(
- all_package_names(set(["Lfoo/Bar;->baz()V", "Lfoo/BarX;->bazx()I"])),
- set(["Lfoo/"]))
- self.assertEqual(
- all_package_names(
- set(["Lfoo/Bar;->baz()V"]),
- set(["Lfoo/BarX;->bazx()I", "Labc/xyz/Mno;->ijk()J"])),
- set(["Lfoo/", "Labc/xyz/"]))
+ flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
- def test_move_all(self):
- src = set([ "abc", "xyz" ])
- dst = set([ "def" ])
- move_all(src, dst)
- self.assertEqual(src, set())
- self.assertEqual(dst, set([ "abc", "def", "xyz" ]))
+ def test_parse_and_merge_csv(self):
+ flags = FlagsDict(['A'], ['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
- def test_move_from_packages(self):
- src = set([ "Lfoo/bar/ClassA;->abc()J", # will be moved
- "Lfoo/bar/ClassA;->def()J", # will be moved
- "Lcom/pkg/example/ClassD;->ijk:J", # not moved: different package
- "Lfoo/bar/xyz/ClassC;->xyz()Z" ]) # not moved: subpackage
- dst = set()
- packages = set([ "Lfoo/bar/" ])
- move_from_packages(packages, src, dst)
- self.assertEqual(
- src, set([ "Lfoo/bar/xyz/ClassC;->xyz()Z", "Lcom/pkg/example/ClassD;->ijk:J" ]))
- self.assertEqual(
- dst, set([ "Lfoo/bar/ClassA;->abc()J", "Lfoo/bar/ClassA;->def()J" ]))
+ # Test empty CSV entry.
+ flags.parse_and_merge_csv(['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
- def test_move_serialization(self):
- # All the entries should be moved apart from the last one
- src = set([ "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)V",
- "Lfoo/bar/ClassA;->readObjectNoData()V",
- "Lfoo/bar/ClassA;->readResolve()Ljava/lang/Object;",
- "Lfoo/bar/ClassA;->serialVersionUID:J",
- "Lfoo/bar/ClassA;->serialPersistentFields:[Ljava/io/ObjectStreamField;",
- "Lfoo/bar/ClassA;->writeObject(Ljava/io/ObjectOutputStream;)V",
- "Lfoo/bar/ClassA;->writeReplace()Ljava/lang/Object;",
- # Should not be moved as signature does not match
- "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)I"])
- expectedToMove = len(src) - 1
- dst = set()
- packages = set([ "Lfoo/bar/" ])
- move_serialization(src, dst)
- self.assertEqual(len(src), 1)
- self.assertEqual(len(dst), expectedToMove)
+ # Test assigning an already assigned flag.
+ flags.parse_and_merge_csv(['A,' + FLAG_WHITELIST])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test new additions.
+ flags.parse_and_merge_csv([
+ 'A,' + FLAG_GREYLIST,
+ 'B,' + FLAG_BLACKLIST + ',' + FLAG_GREYLIST_MAX_O ])
+ self.assertEqual(flags.generate_csv(),
+ [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST,
+ 'B,' + FLAG_BLACKLIST + "," + FLAG_GREYLIST_MAX_O ])
+
+ # Test unknown API signature.
+ with self.assertRaises(AssertionError):
+ flags.parse_and_merge_csv([ 'C' ])
+
+ # Test unknown flag.
+ with self.assertRaises(AssertionError):
+ flags.parse_and_merge_csv([ 'A,foo' ])
+
+ def test_assign_flag(self):
+ flags = FlagsDict(['A'], ['B'])
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test assigning an already assigned flag.
+ flags.assign_flag(FLAG_WHITELIST, set([ 'A' ]))
+ self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+ # Test new additions.
+ flags.assign_flag(FLAG_GREYLIST, set([ 'A', 'B' ]))
+ self.assertEquals(flags.generate_csv(),
+ [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST, 'B,' + FLAG_GREYLIST ])
+
+ # Test invalid API signature.
+ with self.assertRaises(AssertionError):
+ flags.assign_flag(FLAG_WHITELIST, set([ 'C' ]))
+
+ # Test invalid flag.
+ with self.assertRaises(AssertionError):
+ flags.assign_flag('foo', set([ 'A' ]))
if __name__ == '__main__':
unittest.main()
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 3f42275..faa3547 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -262,8 +262,9 @@
return false;
case FieldDescriptor::TYPE_STRING:
if (getPrivacyFlags(field).patterns_size() != 0) return false;
+ break;
default:
- continue;
+ break;
}
}
parents->erase(descriptor->full_name());
diff --git a/tools/powermodel/Android.bp b/tools/powermodel/Android.bp
new file mode 100644
index 0000000..f597aab
--- /dev/null
+++ b/tools/powermodel/Android.bp
@@ -0,0 +1,26 @@
+
+java_library_host {
+ name: "powermodel",
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "guava",
+ ],
+}
+
+java_test_host {
+ name: "powermodel-test",
+
+ test_suites: ["general-tests"],
+
+ srcs: ["test/**/*.java"],
+ java_resource_dirs: ["test-resource"],
+
+ static_libs: [
+ "powermodel",
+ "junit",
+ "mockito",
+ ],
+}
+
diff --git a/tools/powermodel/TEST_MAPPING b/tools/powermodel/TEST_MAPPING
new file mode 100644
index 0000000..c8db339
--- /dev/null
+++ b/tools/powermodel/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "powermodel-test"
+ }
+ ]
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ActivityReport.java b/tools/powermodel/src/com/android/powermodel/ActivityReport.java
new file mode 100644
index 0000000..4a8f633
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ActivityReport.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * ActivityReport contains the summary of the activity that consumes power
+ * as reported by batterystats or statsd.
+ */
+public class ActivityReport {
+ private AppList<AppActivity> mApps;
+
+ public ImmutableList<AppActivity> getAllApps() {
+ return mApps.getAllApps();
+ }
+
+ public ImmutableList<AppActivity> getRegularApps() {
+ return mApps.getRegularApps();
+ }
+
+ public List<AppActivity> findApp(String pkg) {
+ return mApps.findApp(pkg);
+ }
+
+ public AppActivity findApp(SpecialApp specialApp) {
+ return mApps.findApp(specialApp);
+ }
+
+ /**
+ * Find a component in the GLOBAL app.
+ * <p>
+ * Returns null if either the global app doesn't exist (bad data?) or the component
+ * doesn't exist in the global app.
+ */
+ public ComponentActivity findGlobalComponent(Component component) {
+ final AppActivity global = mApps.findApp(SpecialApp.GLOBAL);
+ if (global == null) {
+ return null;
+ }
+ return global.getComponentActivity(component);
+ }
+
+ public static class Builder {
+ private AppList.Builder<AppActivity,AppActivity.Builder> mApps = new AppList.Builder();
+
+ public Builder() {
+ }
+
+ public ActivityReport build() {
+ final ActivityReport result = new ActivityReport();
+ result.mApps = mApps.build();
+ return result;
+ }
+
+ public void addActivity(Component component, Collection<ComponentActivity> activities) {
+ for (final ComponentActivity activity: activities) {
+ addActivity(component, activity);
+ }
+ }
+
+ public void addActivity(Component component, ComponentActivity activity) {
+ AppActivity.Builder app = mApps.get(activity.attribution);
+ if (app == null) {
+ app = new AppActivity.Builder();
+ app.setAttribution(activity.attribution);
+ mApps.put(activity.attribution, app);
+ }
+ app.addComponentActivity(component, activity);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppActivity.java b/tools/powermodel/src/com/android/powermodel/AppActivity.java
new file mode 100644
index 0000000..b87426c
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppActivity.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.util.HashMap;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class AppActivity extends AppInfo {
+
+ private ImmutableMap<Component, ComponentActivity> mComponents;
+ // TODO: power rails
+ // private ImmutableMap<Component, PowerRailActivity> mRails;
+
+ private AppActivity() {
+ }
+
+ /**
+ * Returns the {@link ComponentActivity} for the {@link Component} provided,
+ * or null if this AppActivity does not have that component.
+ * @more
+ * If there is no ComponentActivity for a particular Component, then
+ * there was no usage associated with that app for the app in question.
+ */
+ public ComponentActivity getComponentActivity(Component component) {
+ return mComponents.get(component);
+ }
+
+ public ImmutableSet<Component> getComponents() {
+ return mComponents.keySet();
+ }
+
+ public ImmutableMap<Component,ComponentActivity> getComponentActivities() {
+ return mComponents;
+ }
+
+ // TODO: power rails
+ // public ComponentActivity getPowerRail(Component component) {
+ // return mComponents.get(component);
+ // }
+ //
+ // public Set<Component> getPowerRails() {
+ // return mComponents.keySet();
+ // }
+
+ public static class Builder extends AppInfo.Builder<AppActivity> {
+ private HashMap<Component, ComponentActivity> mComponents = new HashMap();
+ // TODO power rails.
+
+ public Builder() {
+ }
+
+ public AppActivity build() {
+ final AppActivity result = new AppActivity();
+ init(result);
+ result.mComponents = ImmutableMap.copyOf(mComponents);
+ return result;
+ }
+
+ public void addComponentActivity(Component component, ComponentActivity activity) {
+ mComponents.put(component, activity);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppInfo.java b/tools/powermodel/src/com/android/powermodel/AppInfo.java
new file mode 100644
index 0000000..208339e
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppInfo.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+class AppInfo {
+ private AttributionKey mAttribution;
+
+ protected AppInfo() {
+ }
+
+ public boolean hasPackage(String pkg) {
+ return mAttribution.hasPackage(pkg);
+ }
+
+ public AttributionKey getAttribution() {
+ return mAttribution;
+ }
+
+ abstract static class Builder<APP extends AppInfo> {
+ private AttributionKey mAttribution;
+
+ public Builder() {
+ }
+
+ public abstract APP build();
+
+ protected void init(AppInfo app) {
+ if (mAttribution == null) {
+ throw new RuntimeException("setAttribution(AttributionKey attribution) not called");
+ }
+ app.mAttribution = mAttribution;
+ }
+
+ public void setAttribution(AttributionKey attribution) {
+ mAttribution = attribution;
+ }
+
+ public AttributionKey getAttribution() {
+ return mAttribution;
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppList.java b/tools/powermodel/src/com/android/powermodel/AppList.java
new file mode 100644
index 0000000..19572fc
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppList.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+class AppList<APP extends AppInfo> {
+ private ImmutableList<APP> mAllApps;
+ private ImmutableList<APP> mRegularApps;
+ private ImmutableMap<SpecialApp,APP> mSpecialApps;
+
+ private AppList() {
+ }
+
+ public ImmutableList<APP> getAllApps() {
+ return mAllApps;
+ }
+
+ public ImmutableList<APP> getRegularApps() {
+ return mRegularApps;
+ }
+
+ public List<APP> findApp(String pkg) {
+ List<APP> result = new ArrayList();
+ for (APP app: mRegularApps) {
+ if (app.hasPackage(pkg)) {
+ result.add(app);
+ }
+ }
+ return result;
+ }
+
+ public APP findApp(SpecialApp specialApp) {
+ return mSpecialApps.get(specialApp);
+ }
+
+ public static class Builder<APP extends AppInfo, BUILDER extends AppInfo.Builder<APP>> {
+ private final HashMap<AttributionKey,BUILDER> mApps = new HashMap();
+
+ public Builder() {
+ }
+
+ public AppList<APP> build() {
+ final AppList<APP> result = new AppList();
+ final ArrayList<APP> allApps = new ArrayList();
+ final ArrayList<APP> regularApps = new ArrayList();
+ final HashMap<SpecialApp,APP> specialApps = new HashMap();
+ for (AppInfo.Builder<APP> app: mApps.values()) {
+ final AttributionKey attribution = app.getAttribution();
+ final APP appActivity = app.build();
+ allApps.add(appActivity);
+ if (attribution.isSpecialApp()) {
+ specialApps.put(attribution.getSpecialApp(), appActivity);
+ } else {
+ regularApps.add(appActivity);
+ }
+ }
+ result.mAllApps = ImmutableList.copyOf(allApps);
+ result.mRegularApps = ImmutableList.copyOf(regularApps);
+ result.mSpecialApps = ImmutableMap.copyOf(specialApps);
+ return result;
+ }
+
+ public BUILDER get(AttributionKey attribution) {
+ return mApps.get(attribution);
+ }
+
+ public BUILDER put(AttributionKey attribution, BUILDER app) {
+ return mApps.put(attribution, app);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppPower.java b/tools/powermodel/src/com/android/powermodel/AppPower.java
new file mode 100644
index 0000000..283982b
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppPower.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+
+public class AppPower extends AppInfo {
+ private ImmutableMap<Component, ComponentPower> mComponents;
+
+ private double mAppPowerMah;
+
+
+ private AppPower() {
+ }
+
+ /**
+ * Returns the {@link ComponentPower} for the {@link Component} provided,
+ * or null if this AppPower does not have that component.
+ * @more
+ * If the component was in the power profile for this device, there
+ * will be a component for it, even if there was no power used
+ * by that component. In that case, the
+ * {@link ComponentPower.getUsage() ComponentPower.getUsage()}
+ * method will return 0.
+ */
+ public ComponentPower getComponentPower(Component component) {
+ return mComponents.get(component);
+ }
+
+ public Set<Component> getComponents() {
+ return mComponents.keySet();
+ }
+
+ /**
+ * Return the total power used by this app.
+ */
+ public double getAppPowerMah() {
+ return mAppPowerMah;
+ }
+
+ /**
+ * Builder class for {@link AppPower}
+ */
+ public static class Builder extends AppInfo.Builder<AppPower> {
+ private HashMap<Component, ComponentPower> mComponents = new HashMap();
+
+ public Builder() {
+ }
+
+ public AppPower build() {
+ final AppPower result = new AppPower();
+ init(result);
+ result.mComponents = ImmutableMap.copyOf(mComponents);
+
+ // Add up the components
+ double appPowerMah = 0;
+ for (final ComponentPower componentPower: mComponents.values()) {
+ appPowerMah += componentPower.powerMah;
+ }
+ result.mAppPowerMah = appPowerMah;
+
+ return result;
+ }
+
+ public void addComponentPower(Component component, ComponentPower componentPower) {
+ mComponents.put(component, componentPower);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AttributionKey.java b/tools/powermodel/src/com/android/powermodel/AttributionKey.java
new file mode 100644
index 0000000..f19e0b7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AttributionKey.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.util.Set;
+import java.util.HashSet;
+
+import com.google.common.collect.ImmutableSet;
+
+public class AttributionKey {
+ private final int mUid;
+ private final ImmutableSet<String> mPackages;
+ private final SpecialApp mSpecialApp;
+
+ public AttributionKey(SpecialApp specialApp) {
+ mUid = -1;
+ mPackages = ImmutableSet.of();
+ mSpecialApp = specialApp;
+ }
+
+ public AttributionKey(int uid, Set<String> packages) {
+ mUid = uid;
+ mPackages = ImmutableSet.copyOf(packages);
+ mSpecialApp = null;
+ }
+
+ public ImmutableSet<String> getPackages() {
+ return mPackages;
+ }
+
+ public boolean hasPackage(String pkg) {
+ return mPackages.contains(pkg);
+ }
+
+ public SpecialApp getSpecialApp() {
+ return mSpecialApp;
+ }
+
+ public boolean isSpecialApp() {
+ return mSpecialApp != null;
+ }
+
+ /**
+ * Returns the uid for this attribution, or -1 if there isn't one
+ * (e.g. if it is a special app).
+ */
+ public int getUid() {
+ return mUid;
+ }
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = (31 * hash) + (mUid);
+ hash = (31 * hash) + (mPackages == null ? 0 : mPackages.hashCode());
+ hash = (31 * hash) + (mSpecialApp == null ? 0 : mSpecialApp.hashCode());
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null) {
+ return false;
+ }
+ if (this.getClass() != o.getClass()) {
+ return false;
+ }
+ final AttributionKey that = (AttributionKey)o;
+ return (this.mUid == that.mUid)
+ && this.mPackages != null && this.mPackages.equals(that.mPackages)
+ && this.mSpecialApp != null && this.mSpecialApp.equals(that.mSpecialApp);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder str = new StringBuilder("AttributionKey(");
+ if (mUid >= 0) {
+ str.append(" uid=");
+ str.append(mUid);
+ }
+ if (mPackages.size() > 0) {
+ str.append(" packages=[");
+ for (String pkg: mPackages) {
+ str.append(' ');
+ str.append(pkg);
+ }
+ str.append(" ]");
+ }
+ if (mSpecialApp != null) {
+ str.append(" specialApp=");
+ str.append(mSpecialApp.name());
+ }
+ str.append(" )");
+ return str.toString();
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java
new file mode 100644
index 0000000..595c661
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import com.android.powermodel.component.ModemBatteryStatsReader;
+
+public class BatteryStatsReader {
+ /**
+ * Construct a reader.
+ */
+ public BatteryStatsReader() {
+ }
+
+ /**
+ * Parse a powermodel.xml file and return a PowerProfile object.
+ *
+ * @param stream An InputStream containing the batterystats output.
+ *
+ * @throws ParseException Thrown when the xml file can not be parsed.
+ * @throws IOException When there is a problem reading the stream.
+ */
+ public static ActivityReport parse(InputStream stream) throws ParseException, IOException {
+ final Parser parser = new Parser(stream);
+ return parser.parse();
+ }
+
+ /**
+ * Implements the reading and power model logic.
+ */
+ private static class Parser {
+ final InputStream mStream;
+ final ActivityReport mResult;
+ RawBatteryStats mBs;
+
+ /**
+ * Constructor to capture the parameters to read.
+ */
+ Parser(InputStream stream) {
+ mStream = stream;
+ mResult = new ActivityReport();
+ }
+
+ /**
+ * Read the stream, parse it, and apply the power model.
+ * Do not call this more than once.
+ */
+ ActivityReport parse() throws ParseException, IOException {
+ mBs = RawBatteryStats.parse(mStream);
+
+ final ActivityReport.Builder report = new ActivityReport.Builder();
+
+ report.addActivity(Component.MODEM, ModemBatteryStatsReader.createActivities(mBs));
+
+ return report.build();
+ }
+ }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/Component.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/Component.java
index 27d25b8..baae6d7 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/Component.java
@@ -13,12 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
+
+package com.android.powermodel;
/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
+ * The hardware components that use power on a device.
*/
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+public enum Component {
+ CPU,
+ SCREEN,
+ MODEM,
+ WIFI,
+ BLUETOOTH,
+ VIDEO,
+ AUDIO,
+ FLASHLIGHT,
+ CAMERA,
+ GPS,
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentActivity.java b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java
new file mode 100644
index 0000000..c1e2662
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+
+/**
+ * Encapsulates the work done by an app (including synthetic apps) that costs power.
+ */
+public class ComponentActivity {
+ public AttributionKey attribution;
+
+ protected ComponentActivity(AttributionKey attribution) {
+ this.attribution = attribution;
+ }
+
+ // TODO: Can we refactor what goes into the activities so this function
+ // doesn't need the global state?
+ /**
+ * Apply the power profile for this component. Subclasses should implement this
+ * to do the per-component calculatinos. The default implementation returns null.
+ * If this method returns null, then there will be no power associated for this
+ * component, which, for example is true with some of the GLOBAL activities.
+ */
+ public ComponentPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ return null;
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentPower.java b/tools/powermodel/src/com/android/powermodel/ComponentPower.java
new file mode 100644
index 0000000..b22ff87
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentPower.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+/**
+ * The hardware component that uses power on a device.
+ * <p>
+ * This base class contains the total power used by each Component in an app.
+ * Subclasses may add more detail, which is a drill-down, but is not to be
+ * <i>added</i> to {@link #powerMah}.
+ */
+public abstract class ComponentPower<ACTIVITY extends ComponentActivity> {
+ /**
+ * The app associated with this ComponentPower.
+ */
+ public AttributionKey attribution;
+
+ /**
+ * The app activity that resulted in the power usage for this component.
+ */
+ public ACTIVITY activity;
+
+ /**
+ * The total power used by this component in this app.
+ */
+ public double powerMah;
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/ComponentProfile.java
index 27d25b8..e76e5fb 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
@@ -13,12 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel;
+
+public class ComponentProfile {
}
diff --git a/tools/powermodel/src/com/android/powermodel/CsvParser.java b/tools/powermodel/src/com/android/powermodel/CsvParser.java
new file mode 100644
index 0000000..78cd261
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/CsvParser.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+/**
+ * Parses CSV.
+ * <p>
+ * Call parse() with an InputStream.
+ * <p>
+ * CsvLineProcessor.onLine() will be called for each line in the source document.
+ * <p>
+ * To simplify parsing and to protect against using too much memory for bad
+ * data, the maximum field length is {@link #MAX_FIELD_SIZE}.
+ */
+class CsvParser {
+ /**
+ * The maximum size of a single field in bytes.
+ */
+ public static final int MAX_FIELD_SIZE = (8*1024)-1;
+
+ /**
+ * Callback interface for each line of CSV as it is parsed.
+ */
+ interface LineProcessor {
+ /**
+ * A line of CSV was parsed.
+ *
+ * @param lineNumber the line number in the file, starting at 1
+ * @param fields the comma separated fields for the line
+ */
+ void onLine(int lineNumber, ArrayList<String> fields) throws ParseException;
+ }
+
+ /**
+ * Parse the CSV text in input, calling onto processor for each row.
+ */
+ public static void parse(InputStream input, LineProcessor processor)
+ throws IOException, ParseException {
+ final Charset utf8 = StandardCharsets.UTF_8;
+ final byte[] buf = new byte[MAX_FIELD_SIZE+1];
+ int lineNumber = 1;
+ int readPos = 0;
+ int prev = 0;
+ ArrayList<String> fields = new ArrayList<String>();
+ boolean finalBuffer = false;
+ boolean escaping = false;
+ boolean sawQuote = false;
+
+ while (!finalBuffer) {
+ int amt = input.read(buf, readPos, buf.length-readPos);
+ if (amt < 0) {
+ // No more data. Process whatever's left from before.
+ amt = readPos;
+ finalBuffer = true;
+ } else {
+ // Process whatever's left from before, plus the new data.
+ amt += readPos;
+ finalBuffer = false;
+ }
+
+ // Process as much of this buffer as we can.
+ int fieldStart = 0;
+ int index = readPos;
+ int escapeIndex = escaping ? readPos : -1;
+ while (index < amt) {
+ byte c = buf[index];
+ if (c == '\r' || c == '\n') {
+ if (escaping) {
+ // TODO: Quotes do not escape newlines in our CSV dialect,
+ // but we actually see some data where it should.
+ fields.add(new String(buf, fieldStart, escapeIndex-fieldStart));
+ escapeIndex = -1;
+ escaping = false;
+ sawQuote = false;
+ } else {
+ fields.add(new String(buf, fieldStart, index-fieldStart));
+ }
+ // Don't report blank lines
+ if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) {
+ processor.onLine(lineNumber, fields);
+ }
+ fields = new ArrayList<String>();
+ if (!(c == '\n' && prev == '\r')) {
+ // Don't double increment for dos line endings.
+ lineNumber++;
+ }
+ fieldStart = index = index + 1;
+ } else {
+ if (escaping) {
+ // Field started with a " so quotes are escaped with " and commas
+ // don't matter except when following a single quote.
+ if (c == '"') {
+ if (sawQuote) {
+ buf[escapeIndex] = buf[index];
+ escapeIndex++;
+ sawQuote = false;
+ } else {
+ sawQuote = true;
+ }
+ index++;
+ } else if (sawQuote && c == ',') {
+ fields.add(new String(buf, fieldStart, escapeIndex-fieldStart));
+ fieldStart = index = index + 1;
+ escapeIndex = -1;
+ escaping = false;
+ sawQuote = false;
+ } else {
+ buf[escapeIndex] = buf[index];
+ escapeIndex++;
+ index++;
+ sawQuote = false;
+ }
+ } else {
+ if (c == ',') {
+ fields.add(new String(buf, fieldStart, index-fieldStart));
+ fieldStart = index + 1;
+ } else if (c == '"' && fieldStart == index) {
+ // First character is a "
+ escaping = true;
+ fieldStart = escapeIndex = index + 1;
+ }
+ index++;
+ }
+ }
+ prev = c;
+ }
+
+ // A single field is greater than buf.length, so fail.
+ if (fieldStart == 0 && index == buf.length) {
+ throw new ParseException(lineNumber, "Line is too long: "
+ + new String(buf, 0, 20, utf8) + "...");
+ }
+
+ // Move whatever we didn't process to the beginning of the buffer
+ // and try again.
+ if (fieldStart != amt) {
+ readPos = (escaping ? escapeIndex : index) - fieldStart;
+ System.arraycopy(buf, fieldStart, buf, 0, readPos);
+ } else {
+ readPos = 0;
+ }
+
+ // Process whatever's left over
+ if (finalBuffer) {
+ fields.add(new String(buf, 0, readPos));
+ // If there is any content, return the last line.
+ if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) {
+ processor.onLine(lineNumber, fields);
+ }
+ }
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/ParseException.java b/tools/powermodel/src/com/android/powermodel/ParseException.java
new file mode 100644
index 0000000..e1f232b
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ParseException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+public class ParseException extends Exception {
+ public final int line;
+
+ public ParseException(int line, String message, Throwable th) {
+ super(message, th);
+ this.line = line;
+ }
+
+ public ParseException(int line, String message) {
+ this(line, message, null);
+ }
+
+ public ParseException(String message) {
+ this(0, message, null);
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/PowerProfile.java b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
new file mode 100644
index 0000000..373a9c9
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
@@ -0,0 +1,527 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import com.android.powermodel.util.Conversion;
+
+public class PowerProfile {
+
+ // Remaining fields from the android code for which the actual usage is unclear.
+ // battery.capacity
+ // bluetooth.controller.voltage
+ // modem.controller.voltage
+ // gps.voltage
+ // wifi.controller.voltage
+ // radio.on
+ // radio.scanning
+ // radio.active
+ // memory.bandwidths
+ // wifi.batchedscan
+ // wifi.scan
+ // wifi.on
+ // wifi.active
+ // wifi.controller.tx_levels
+
+ private static Pattern RE_CLUSTER_POWER = Pattern.compile("cpu.cluster_power.cluster([0-9]*)");
+ private static Pattern RE_CORE_SPEEDS = Pattern.compile("cpu.core_speeds.cluster([0-9]*)");
+ private static Pattern RE_CORE_POWER = Pattern.compile("cpu.core_power.cluster([0-9]*)");
+
+ private HashMap<Component, ComponentProfile> mComponents = new HashMap();
+
+ /**
+ * Which element we are currently parsing.
+ */
+ enum ElementState {
+ BEGIN,
+ TOP,
+ ITEM,
+ ARRAY,
+ VALUE
+ }
+
+ /**
+ * Implements the reading and power model logic.
+ */
+ private static class Parser {
+ private final InputStream mStream;
+ private final PowerProfile mResult;
+
+ // Builders for the ComponentProfiles.
+ private final AudioProfile mAudio = new AudioProfile();
+ private final BluetoothProfile mBluetooth = new BluetoothProfile();
+ private final CameraProfile mCamera = new CameraProfile();
+ private final CpuProfile.Builder mCpuBuilder = new CpuProfile.Builder();
+ private final FlashlightProfile mFlashlight = new FlashlightProfile();
+ private final GpsProfile.Builder mGpsBuilder = new GpsProfile.Builder();
+ private final ModemProfile.Builder mModemBuilder = new ModemProfile.Builder();
+ private final ScreenProfile mScreen = new ScreenProfile();
+ private final VideoProfile mVideo = new VideoProfile();
+ private final WifiProfile mWifi = new WifiProfile();
+
+ /**
+ * Constructor to capture the parameters to read.
+ */
+ Parser(InputStream stream) {
+ mStream = stream;
+ mResult = new PowerProfile();
+ }
+
+ /**
+ * Read the stream, parse it, and apply the power model.
+ * Do not call this more than once.
+ */
+ PowerProfile parse() throws ParseException {
+ final SAXParserFactory factory = SAXParserFactory.newInstance();
+ AndroidResourceHandler handler = null;
+ try {
+ final SAXParser saxParser = factory.newSAXParser();
+
+ handler = new AndroidResourceHandler() {
+ @Override
+ public void onItem(Locator locator, String name, float value)
+ throws SAXParseException {
+ Parser.this.onItem(locator, name, value);
+ }
+
+ @Override
+ public void onArray(Locator locator, String name, float[] value)
+ throws SAXParseException {
+ Parser.this.onArray(locator, name, value);
+ }
+ };
+
+ saxParser.parse(mStream, handler);
+ } catch (ParserConfigurationException ex) {
+ // Coding error, not runtime error.
+ throw new RuntimeException(ex);
+ } catch (SAXParseException ex) {
+ throw new ParseException(ex.getLineNumber(), ex.getMessage(), ex);
+ } catch (SAXException | IOException ex) {
+ // Make a guess about the line number.
+ throw new ParseException(handler.getLineNumber(), ex.getMessage(), ex);
+ }
+
+ // TODO: This doesn't cover the multiple algorithms. Some refactoring will
+ // be necessary.
+ mResult.mComponents.put(Component.AUDIO, mAudio);
+ mResult.mComponents.put(Component.BLUETOOTH, mBluetooth);
+ mResult.mComponents.put(Component.CAMERA, mCamera);
+ mResult.mComponents.put(Component.CPU, mCpuBuilder.build());
+ mResult.mComponents.put(Component.FLASHLIGHT, mFlashlight);
+ mResult.mComponents.put(Component.GPS, mGpsBuilder.build());
+ mResult.mComponents.put(Component.MODEM, mModemBuilder.build());
+ mResult.mComponents.put(Component.SCREEN, mScreen);
+ mResult.mComponents.put(Component.VIDEO, mVideo);
+ mResult.mComponents.put(Component.WIFI, mWifi);
+
+ return mResult;
+ }
+
+ /**
+ * Handles an item tag in the power_profile.xml.
+ */
+ public void onItem(Locator locator, String name, float value) throws SAXParseException {
+ Integer index;
+ try {
+ if ("ambient.on".equals(name)) {
+ mScreen.ambientMa = value;
+ } else if ("audio".equals(name)) {
+ mAudio.onMa = value;
+ } else if ("bluetooth.controller.idle".equals(name)) {
+ mBluetooth.idleMa = value;
+ } else if ("bluetooth.controller.rx".equals(name)) {
+ mBluetooth.rxMa = value;
+ } else if ("bluetooth.controller.tx".equals(name)) {
+ mBluetooth.txMa = value;
+ } else if ("camera.avg".equals(name)) {
+ mCamera.onMa = value;
+ } else if ("camera.flashlight".equals(name)) {
+ mFlashlight.onMa = value;
+ } else if ("cpu.suspend".equals(name)) {
+ mCpuBuilder.setSuspendMa(value);
+ } else if ("cpu.idle".equals(name)) {
+ mCpuBuilder.setIdleMa(value);
+ } else if ("cpu.active".equals(name)) {
+ mCpuBuilder.setActiveMa(value);
+ } else if ((index = matchIndexedRegex(locator, RE_CLUSTER_POWER, name)) != null) {
+ mCpuBuilder.setClusterPower(index, value);
+ } else if ("gps.on".equals(name)) {
+ mGpsBuilder.setOnMa(value);
+ } else if ("modem.controller.sleep".equals(name)) {
+ mModemBuilder.setSleepMa(value);
+ } else if ("modem.controller.idle".equals(name)) {
+ mModemBuilder.setIdleMa(value);
+ } else if ("modem.controller.rx".equals(name)) {
+ mModemBuilder.setRxMa(value);
+ } else if ("radio.scanning".equals(name)) {
+ mModemBuilder.setScanningMa(value);
+ } else if ("screen.on".equals(name)) {
+ mScreen.onMa = value;
+ } else if ("screen.full".equals(name)) {
+ mScreen.fullMa = value;
+ } else if ("video".equals(name)) {
+ mVideo.onMa = value;
+ } else if ("wifi.controller.idle".equals(name)) {
+ mWifi.idleMa = value;
+ } else if ("wifi.controller.rx".equals(name)) {
+ mWifi.rxMa = value;
+ } else if ("wifi.controller.tx".equals(name)) {
+ mWifi.txMa = value;
+ } else {
+ // TODO: Uncomment this when we have all of the items parsed.
+ // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+ // locator, ex);
+
+ }
+ } catch (ParseException ex) {
+ throw new SAXParseException(ex.getMessage(), locator, ex);
+ }
+ }
+
+ /**
+ * Handles an array tag in the power_profile.xml.
+ */
+ public void onArray(Locator locator, String name, float[] value) throws SAXParseException {
+ Integer index;
+ try {
+ if ("cpu.clusters.cores".equals(name)) {
+ mCpuBuilder.setCoreCount(Conversion.toIntArray(value));
+ } else if ((index = matchIndexedRegex(locator, RE_CORE_SPEEDS, name)) != null) {
+ mCpuBuilder.setCoreSpeeds(index, Conversion.toIntArray(value));
+ } else if ((index = matchIndexedRegex(locator, RE_CORE_POWER, name)) != null) {
+ mCpuBuilder.setCorePower(index, value);
+ } else if ("gps.signalqualitybased".equals(name)) {
+ mGpsBuilder.setSignalMa(value);
+ } else if ("modem.controller.tx".equals(name)) {
+ mModemBuilder.setTxMa(value);
+ } else {
+ // TODO: Uncomment this when we have all of the items parsed.
+ // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+ // locator, ex);
+ }
+ } catch (ParseException ex) {
+ throw new SAXParseException(ex.getMessage(), locator, ex);
+ }
+ }
+ }
+
+ /**
+ * SAX XML handler that can parse the android resource files.
+ * In our case, all elements are floats.
+ */
+ abstract static class AndroidResourceHandler extends DefaultHandler {
+ /**
+ * The set of names already processed. Map of name to line number.
+ */
+ private HashMap<String,Integer> mAlreadySeen = new HashMap<String,Integer>();
+
+ /**
+ * Where in the document we are parsing.
+ */
+ private Locator mLocator;
+
+ /**
+ * Which element we are currently parsing.
+ */
+ private ElementState mState = ElementState.BEGIN;
+
+ /**
+ * Saved name from item and array elements.
+ */
+ private String mName;
+
+ /**
+ * The text that is currently being captured, or null if {@link #startCapturingText()}
+ * has not been called.
+ */
+ private StringBuilder mText;
+
+ /**
+ * The array values that have been parsed so for for this array. Null if we are
+ * not inside an array tag.
+ */
+ private ArrayList<Float> mArray;
+
+ /**
+ * Called when an item tag is encountered.
+ */
+ public abstract void onItem(Locator locator, String name, float value)
+ throws SAXParseException;
+
+ /**
+ * Called when an array is encountered.
+ */
+ public abstract void onArray(Locator locator, String name, float[] value)
+ throws SAXParseException;
+
+ /**
+ * If we have a Locator set, return the line number, otherwise return 0.
+ */
+ public int getLineNumber() {
+ return mLocator != null ? mLocator.getLineNumber() : 0;
+ }
+
+ /**
+ * Handle setting the parse location object.
+ */
+ public void setDocumentLocator(Locator locator) {
+ mLocator = locator;
+ }
+
+ /**
+ * Handle beginning of an element.
+ *
+ * @param ns Namespace uri
+ * @param ln Local name (inside namespace)
+ * @param element Tag name
+ */
+ @Override
+ public void startElement(String ns, String ln, String element,
+ Attributes attr) throws SAXException {
+ switch (mState) {
+ case BEGIN:
+ // Outer element, we don't care the tag name.
+ mState = ElementState.TOP;
+ return;
+ case TOP:
+ if ("item".equals(element)) {
+ mState = ElementState.ITEM;
+ saveNameAttribute(attr);
+ startCapturingText();
+ return;
+ } else if ("array".equals(element)) {
+ mState = ElementState.ARRAY;
+ mArray = new ArrayList<Float>();
+ saveNameAttribute(attr);
+ return;
+ }
+ break;
+ case ARRAY:
+ if ("value".equals(element)) {
+ mState = ElementState.VALUE;
+ startCapturingText();
+ return;
+ }
+ break;
+ }
+ throw new SAXParseException("unexpected element: '" + element + "'", mLocator);
+ }
+
+ /**
+ * Handle end of an element.
+ *
+ * @param ns Namespace uri
+ * @param ln Local name (inside namespace)
+ * @param element Tag name
+ */
+ @Override
+ public void endElement(String ns, String ln, String element) throws SAXException {
+ switch (mState) {
+ case ITEM: {
+ float value = parseFloat(finishCapturingText());
+ mState = ElementState.TOP;
+ onItem(mLocator, mName, value);
+ break;
+ }
+ case ARRAY: {
+ final int N = mArray.size();
+ float[] values = new float[N];
+ for (int i=0; i<N; i++) {
+ values[i] = mArray.get(i);
+ }
+ mArray = null;
+ mState = ElementState.TOP;
+ onArray(mLocator, mName, values);
+ break;
+ }
+ case VALUE: {
+ mArray.add(parseFloat(finishCapturingText()));
+ mState = ElementState.ARRAY;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Interstitial text received.
+ *
+ * @throws SAXException if there shouldn't be non-whitespace text here
+ */
+ @Override
+ public void characters(char text[], int start, int length) throws SAXException {
+ if (mText == null && length > 0 && !isWhitespace(text, start, length)) {
+ throw new SAXParseException("unexpected text: '"
+ + firstLine(text, start, length).trim() + "'", mLocator);
+ }
+ if (mText != null) {
+ mText.append(text, start, length);
+ }
+ }
+
+ /**
+ * Begin collecting text from inside an element.
+ */
+ private void startCapturingText() {
+ if (mText != null) {
+ throw new RuntimeException("ASSERTION FAILED: Shouldn't be already capturing"
+ + " text. mState=" + mState.name()
+ + " line=" + mLocator.getLineNumber()
+ + " column=" + mLocator.getColumnNumber());
+ }
+ mText = new StringBuilder();
+ }
+
+ /**
+ * Stop capturing text from inside an element.
+ *
+ * @return the captured text
+ */
+ private String finishCapturingText() {
+ if (mText == null) {
+ throw new RuntimeException("ASSERTION FAILED: Should already be capturing"
+ + " text. mState=" + mState.name()
+ + " line=" + mLocator.getLineNumber()
+ + " column=" + mLocator.getColumnNumber());
+ }
+ final String result = mText.toString().trim();
+ mText = null;
+ return result;
+ }
+
+ /**
+ * Get the "name" attribute.
+ *
+ * @throws SAXParseException if the name attribute is not present or if
+ * the name has already been seen in the file.
+ */
+ private void saveNameAttribute(Attributes attr) throws SAXParseException {
+ final String name = attr.getValue("name");
+ if (name == null) {
+ throw new SAXParseException("expected 'name' attribute", mLocator);
+ }
+ Integer prev = mAlreadySeen.put(name, mLocator.getLineNumber());
+ if (prev != null) {
+ throw new SAXParseException("name '" + name + "' already seen on line: " + prev,
+ mLocator);
+ }
+ mName = name;
+ }
+
+ /**
+ * Gets the float value of the string.
+ *
+ * @throws SAXParseException if 'text' can't be parsed as a float.
+ */
+ private float parseFloat(String text) throws SAXParseException {
+ try {
+ return Float.parseFloat(text);
+ } catch (NumberFormatException ex) {
+ throw new SAXParseException("not a valid float value: '" + text + "'",
+ mLocator, ex);
+ }
+ }
+ }
+
+ /**
+ * Return whether the given substring is all whitespace.
+ */
+ private static boolean isWhitespace(char[] text, int start, int length) {
+ for (int i = start; i < (start + length); i++) {
+ if (!Character.isSpace(text[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the contents of text up to the first newline.
+ */
+ private static String firstLine(char[] text, int start, int length) {
+ // TODO: The line number will be wrong if we skip preceeding blank lines.
+ while (length > 0) {
+ if (Character.isSpace(text[start])) {
+ start++;
+ length--;
+ }
+ }
+ int newlen = 0;
+ for (; newlen < length; newlen++) {
+ final char c = text[newlen];
+ if (c == '\n' || c == '\r') {
+ break;
+ }
+ }
+ return new String(text, start, newlen);
+ }
+
+ /**
+ * If the pattern matches, return the first group of that as an Integer.
+ * If not return null.
+ */
+ private static Integer matchIndexedRegex(Locator locator, Pattern pattern, String text)
+ throws SAXParseException {
+ final Matcher m = pattern.matcher(text);
+ if (m.matches()) {
+ try {
+ return Integer.parseInt(m.group(1));
+ } catch (NumberFormatException ex) {
+ throw new SAXParseException("Invalid field name: '" + text + "'", locator, ex);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public static PowerProfile parse(InputStream stream) throws ParseException {
+ return (new Parser(stream)).parse();
+ }
+
+ private PowerProfile() {
+ }
+
+ public ComponentProfile getComponent(Component component) {
+ return mComponents.get(component);
+ }
+
+}
diff --git a/tools/powermodel/src/com/android/powermodel/PowerReport.java b/tools/powermodel/src/com/android/powermodel/PowerReport.java
new file mode 100644
index 0000000..76ba672
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerReport.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * PowerReport contains the summary of all power used on a device
+ * as reported by batterystats or statsd, based on the power profile.
+ */
+public class PowerReport {
+ private AppList<AppPower> mApps;
+ private double mTotalPowerMah;
+
+ private PowerReport() {
+ }
+
+ /**
+ * The total power used by this device for this PowerReport.
+ */
+ public double getTotalPowerMah() {
+ return mTotalPowerMah;
+ }
+
+ public List<AppPower> getAllApps() {
+ return mApps.getAllApps();
+ }
+
+ public List<AppPower> getRegularApps() {
+ return mApps.getRegularApps();
+ }
+
+ public List<AppPower> findApp(String pkg) {
+ return mApps.findApp(pkg);
+ }
+
+ public AppPower findApp(SpecialApp specialApp) {
+ return mApps.findApp(specialApp);
+ }
+
+ public static PowerReport createReport(PowerProfile profile, ActivityReport activityReport) {
+ final PowerReport.Builder powerReport = new PowerReport.Builder();
+ for (final AppActivity appActivity: activityReport.getAllApps()) {
+ final AppPower.Builder appPower = new AppPower.Builder();
+ appPower.setAttribution(appActivity.getAttribution());
+
+ for (final ImmutableMap.Entry<Component,ComponentActivity> entry:
+ appActivity.getComponentActivities().entrySet()) {
+ final ComponentPower componentPower = entry.getValue()
+ .applyProfile(activityReport, profile);
+ if (componentPower != null) {
+ appPower.addComponentPower(entry.getKey(), componentPower);
+ }
+ }
+
+ powerReport.add(appPower);
+ }
+ return powerReport.build();
+ }
+
+ private static class Builder {
+ private AppList.Builder mApps = new AppList.Builder();
+
+ public Builder() {
+ }
+
+ public PowerReport build() {
+ final PowerReport report = new PowerReport();
+
+ report.mApps = mApps.build();
+
+ for (AppPower app: report.mApps.getAllApps()) {
+ report.mTotalPowerMah += app.getAppPowerMah();
+ }
+
+ return report;
+ }
+
+ public void add(AppPower.Builder app) {
+ mApps.put(app.getAttribution(), app);
+ }
+ }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
new file mode 100644
index 0000000..76c0482
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
@@ -0,0 +1,1175 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RawBatteryStats {
+ /**
+ * The factory objects for the records. Initialized in the static block.
+ */
+ private static HashMap<String,RecordFactory> sFactories
+ = new HashMap<String,RecordFactory>();
+
+ /**
+ * The Record objects that have been parsed.
+ */
+ private ArrayList<Record> mRecords = new ArrayList<Record>();
+
+ /**
+ * The Record objects that have been parsed, indexed by type.
+ *
+ * Don't use this before {@link #indexRecords()} has been called.
+ */
+ private ImmutableMap<String,ImmutableList<Record>> mRecordsByType;
+
+ /**
+ * The attribution keys for which we have data (corresponding to UIDs we've seen).
+ * <p>
+ * Does not include the synthetic apps.
+ * <p>
+ * Don't use this before {@link #indexRecords()} has been called.
+ */
+ private ImmutableSet<AttributionKey> mApps;
+
+ /**
+ * The warnings that have been issued during parsing.
+ */
+ private ArrayList<Warning> mWarnings = new ArrayList<Warning>();
+
+ /**
+ * The version of the BatteryStats dumpsys that we are using. This value
+ * is set to -1 initially, and then when parsing the (hopefully) first
+ * line, 'vers', it is set to the correct version.
+ */
+ private int mDumpsysVersion = -1;
+
+ /**
+ * Enum used in the Line annotation to mark whether a field is expected to be
+ * system-wide or scoped to an app.
+ */
+ public enum Scope {
+ SYSTEM,
+ UID
+ }
+
+ /**
+ * Enum used to indicated the expected number of results.
+ */
+ public enum Count {
+ SINGLE,
+ MULTIPLE
+ }
+
+ /**
+ * Annotates classes that represent a line of CSV in the batterystats CSV
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ @interface Line {
+ String tag();
+ Scope scope();
+ Count count();
+ }
+
+ /**
+ * Annotates fields that should be parsed automatically from CSV
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface Field {
+ /**
+ * The "column" of this field in the most recent version of the CSV.
+ * When parsing old versions, fields that were added will be automatically
+ * removed and the indices will be fixed up.
+ *
+ * The header fields (version, uid, category, type) will be automatically
+ * handled for the base Line type. The index 0 should start after those.
+ */
+ int index();
+
+ /**
+ * First version that this field appears in.
+ */
+ int added() default 0;
+ }
+
+ /**
+ * Each line in the BatteryStats CSV is tagged with a category, that says
+ * which of the time collection modes was used for the data.
+ */
+ public enum Category {
+ INFO("i"),
+ LAST("l"),
+ UNPLUGGED("u"),
+ CURRENT("c");
+
+ public final String tag;
+
+ Category(String tag) {
+ this.tag = tag;
+ }
+ }
+
+ /**
+ * Base class for all lines in a batterystats CSV file.
+ */
+ public static class Record {
+ /**
+ * Whether all of the fields for the indicated version of this record
+ * have been filled in.
+ */
+ public boolean complete;
+
+
+ @Field(index=-4)
+ public int lineVersion;
+
+ @Field(index=-3)
+ public int uid;
+
+ @Field(index=-2)
+ public Category category;
+
+ @Field(index=-1)
+ public String lineType;
+ }
+
+ @Line(tag="bt", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Battery extends Record {
+ // If which != STATS_SINCE_CHARGED, the csv will be "N/A" and we will get
+ // a parsing warning. Nobody uses anything other than STATS_SINCE_CHARGED.
+ @Field(index=0)
+ public int startCount;
+
+ @Field(index=1)
+ public long whichBatteryRealtimeMs;
+
+ @Field(index=2)
+ public long whichBatteryUptimeMs;
+
+ @Field(index=3)
+ public long totalRealtimeMs;
+
+ @Field(index=4)
+ public long totalUptimeMs;
+
+ @Field(index=5)
+ public long getStartClockTimeMs;
+
+ @Field(index=6)
+ public long whichBatteryScreenOffRealtimeMs;
+
+ @Field(index=7)
+ public long whichBatteryScreenOffUptimeMs;
+
+ @Field(index=8)
+ public long estimatedBatteryCapacityMah;
+
+ @Field(index=9)
+ public long minLearnedBatteryCapacityMah;
+
+ @Field(index=10)
+ public long maxLearnedBatteryCapacityMah;
+
+ @Field(index=11)
+ public long screenDozeTimeMs;
+ }
+
+ @Line(tag="gn", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class GlobalNetwork extends Record {
+ @Field(index=0)
+ public long mobileRxTotalBytes;
+
+ @Field(index=1)
+ public long mobileTxTotalBytes;
+
+ @Field(index=2)
+ public long wifiRxTotalBytes;
+
+ @Field(index=3)
+ public long wifiTxTotalBytes;
+
+ @Field(index=4)
+ public long mobileRxTotalPackets;
+
+ @Field(index=5)
+ public long mobileTxTotalPackets;
+
+ @Field(index=6)
+ public long wifiRxTotalPackets;
+
+ @Field(index=7)
+ public long wifiTxTotalPackets;
+
+ @Field(index=8)
+ public long btRxTotalBytes;
+
+ @Field(index=9)
+ public long btTxTotalBytes;
+ }
+
+ @Line(tag="gmcd", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class GlobalModemController extends Record {
+ @Field(index=0)
+ public long idleMs;
+
+ @Field(index=1)
+ public long rxTimeMs;
+
+ @Field(index=2)
+ public long powerMaMs;
+
+ @Field(index=3)
+ public long[] txTimeMs;
+ }
+
+ @Line(tag="m", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Misc extends Record {
+ @Field(index=0)
+ public long screenOnTimeMs;
+
+ @Field(index=1)
+ public long phoneOnTimeMs;
+
+ @Field(index=2)
+ public long fullWakeLockTimeTotalMs;
+
+ @Field(index=3)
+ public long partialWakeLockTimeTotalMs;
+
+ @Field(index=4)
+ public long mobileRadioActiveTimeMs;
+
+ @Field(index=5)
+ public long mobileRadioActiveAdjustedTimeMs;
+
+ @Field(index=6)
+ public long interactiveTimeMs;
+
+ @Field(index=7)
+ public long powerSaveModeEnabledTimeMs;
+
+ @Field(index=8)
+ public int connectivityChangeCount;
+
+ @Field(index=9)
+ public long deepDeviceIdleModeTimeMs;
+
+ @Field(index=10)
+ public long deepDeviceIdleModeCount;
+
+ @Field(index=11)
+ public long deepDeviceIdlingTimeMs;
+
+ @Field(index=12)
+ public long deepDeviceIdlingCount;
+
+ @Field(index=13)
+ public long mobileRadioActiveCount;
+
+ @Field(index=14)
+ public long mobileRadioActiveUnknownTimeMs;
+
+ @Field(index=15)
+ public long lightDeviceIdleModeTimeMs;
+
+ @Field(index=16)
+ public long lightDeviceIdleModeCount;
+
+ @Field(index=17)
+ public long lightDeviceIdlingTimeMs;
+
+ @Field(index=18)
+ public long lightDeviceIdlingCount;
+
+ @Field(index=19)
+ public long lightLongestDeviceIdleModeTimeMs;
+
+ @Field(index=20)
+ public long deepLongestDeviceIdleModeTimeMs;
+ }
+
+ @Line(tag="nt", scope=Scope.UID, count=Count.SINGLE)
+ public static class Network extends Record {
+ @Field(index=0)
+ public long mobileRxBytes;
+
+ @Field(index=1)
+ public long mobileTxBytes;
+
+ @Field(index=2)
+ public long wifiRxBytes;
+
+ @Field(index=3)
+ public long wifiTxBytes;
+
+ @Field(index=4)
+ public long mobileRxPackets;
+
+ @Field(index=5)
+ public long mobileTxPackets;
+
+ @Field(index=6)
+ public long wifiRxPackets;
+
+ @Field(index=7)
+ public long wifiTxPackets;
+
+ // This is microseconds, because... batterystats.
+ @Field(index=8)
+ public long mobileRadioActiveTimeUs;
+
+ @Field(index=9)
+ public long mobileRadioActiveCount;
+
+ @Field(index=10)
+ public long btRxBytes;
+
+ @Field(index=11)
+ public long btTxBytes;
+
+ @Field(index=12)
+ public long mobileWakeupCount;
+
+ @Field(index=13)
+ public long wifiWakeupCount;
+
+ @Field(index=14)
+ public long mobileBgRxBytes;
+
+ @Field(index=15)
+ public long mobileBgTxBytes;
+
+ @Field(index=16)
+ public long wifiBgRxBytes;
+
+ @Field(index=17)
+ public long wifiBgTxBytes;
+
+ @Field(index=18)
+ public long mobileBgRxPackets;
+
+ @Field(index=19)
+ public long mobileBgTxPackets;
+
+ @Field(index=20)
+ public long wifiBgRxPackets;
+
+ @Field(index=21)
+ public long wifiBgTxPackets;
+ }
+
+ @Line(tag="sgt", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class SignalStrengthTime extends Record {
+ @Field(index=0)
+ public long[] phoneSignalStrengthTimeMs;
+ }
+
+ @Line(tag="sst", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class SignalScanningTime extends Record {
+ @Field(index=0)
+ public long phoneSignalScanningTimeMs;
+ }
+
+ @Line(tag="uid", scope=Scope.UID, count=Count.MULTIPLE)
+ public static class Uid extends Record {
+ @Field(index=0)
+ public int uidKey;
+
+ @Field(index=1)
+ public String pkg;
+ }
+
+ @Line(tag="vers", scope=Scope.SYSTEM, count=Count.SINGLE)
+ public static class Version extends Record {
+ @Field(index=0)
+ public int dumpsysVersion;
+
+ @Field(index=1)
+ public int parcelVersion;
+
+ @Field(index=2)
+ public String startPlatformVersion;
+
+ @Field(index=3)
+ public String endPlatformVersion;
+ }
+
+ /**
+ * Codes for the warnings to classify warnings without parsing them.
+ */
+ public enum WarningId {
+ /**
+ * A row didn't have enough fields to match any records and let us extract
+ * a line type.
+ */
+ TOO_FEW_FIELDS_FOR_LINE_TYPE,
+
+ /**
+ * We couldn't find a Record for the given line type.
+ */
+ NO_MATCHING_LINE_TYPE,
+
+ /**
+ * Couldn't set the value of a field. Usually this is because the
+ * contents of a numeric type couldn't be parsed.
+ */
+ BAD_FIELD_TYPE,
+
+ /**
+ * There were extra field values in the input text.
+ */
+ TOO_MANY_FIELDS,
+
+ /**
+ * There were fields that we were expecting (for this version
+ * of the dumpsys) that weren't provided in the CSV data.
+ */
+ NOT_ENOUGH_FIELDS,
+
+ /**
+ * The dumpsys version in the 'vers' CSV line couldn't be parsed.
+ */
+ BAD_DUMPSYS_VERSION
+ }
+
+ /**
+ * A non-fatal problem detected during parsing.
+ */
+ public static class Warning {
+ private int mLineNumber;
+ private WarningId mId;
+ private ArrayList<String> mFields;
+ private String mMessage;
+ private String[] mExtras;
+
+ public Warning(int lineNumber, WarningId id, ArrayList<String> fields, String message,
+ String[] extras) {
+ mLineNumber = lineNumber;
+ mId = id;
+ mFields = fields;
+ mMessage = message;
+ mExtras = extras;
+ }
+
+ public int getLineNumber() {
+ return mLineNumber;
+ }
+
+ public ArrayList<String> getFields() {
+ return mFields;
+ }
+
+ public String getMessage() {
+ return mMessage;
+ }
+
+ public String[] getExtras() {
+ return mExtras;
+ }
+ }
+
+ /**
+ * Base class for classes to set fields on Record objects via reflection.
+ */
+ private abstract static class FieldSetter {
+ private int mIndex;
+ private int mAdded;
+ protected java.lang.reflect.Field mField;
+
+ FieldSetter(int index, int added, java.lang.reflect.Field field) {
+ mIndex = index;
+ mAdded = added;
+ mField = field;
+ }
+
+ String getName() {
+ return mField.getName();
+ }
+
+ int getIndex() {
+ return mIndex;
+ }
+
+ int getAdded() {
+ return mAdded;
+ }
+
+ boolean isArray() {
+ return mField.getType().isArray();
+ }
+
+ abstract void setField(int lineNumber, Record object, String value) throws ParseException;
+ abstract void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException;
+
+ @Override
+ public String toString() {
+ final String className = getClass().getName();
+ int startIndex = Math.max(className.lastIndexOf('.'), className.lastIndexOf('$'));
+ if (startIndex < 0) {
+ startIndex = 0;
+ } else {
+ startIndex++;
+ }
+ return className.substring(startIndex) + "(index=" + mIndex + " added=" + mAdded
+ + " field=" + mField.getName()
+ + " type=" + mField.getType().getSimpleName()
+ + ")";
+ }
+ }
+
+ /**
+ * Sets int fields on Record objects using reflection.
+ */
+ private static class IntFieldSetter extends FieldSetter {
+ IntFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.setInt(object, Integer.parseInt(value.trim()));
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse as integer: " + value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final int[] array = new int[endIndex-startIndex];
+ for (int i=startIndex; i<endIndex; i++) {
+ final String value = values.get(startIndex+i);
+ try {
+ array[i] = Integer.parseInt(value.trim());
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse field "
+ + i + " as integer: " + value);
+ }
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets long fields on Record objects using reflection.
+ */
+ private static class LongFieldSetter extends FieldSetter {
+ LongFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.setLong(object, Long.parseLong(value.trim()));
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse as long: " + value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final long[] array = new long[endIndex-startIndex];
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ final String value = values.get(startIndex+i);
+ try {
+ array[i] = Long.parseLong(value.trim());
+ } catch (NumberFormatException ex) {
+ throw new ParseException(lineNumber, "can't parse field "
+ + i + " as long: " + value);
+ }
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets String fields on Record objects using reflection.
+ */
+ private static class StringFieldSetter extends FieldSetter {
+ StringFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ try {
+ mField.set(object, value);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final String[] array = new String[endIndex-startIndex];
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ array[i] = values.get(startIndex+1);
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Sets enum fields on Record objects using reflection.
+ *
+ * To be parsed automatically, enums must have a public final String tag
+ * field, which is the string that will appear in the csv for that enum value.
+ */
+ private static class EnumFieldSetter extends FieldSetter {
+ private final HashMap<String,Enum> mTags = new HashMap<String,Enum>();
+
+ EnumFieldSetter(int index, int added, java.lang.reflect.Field field) {
+ super(index, added, field);
+
+ // Build the mapping of tags to values.
+ final Class<?> fieldType = field.getType();
+
+ java.lang.reflect.Field tagField = null;
+ try {
+ tagField = fieldType.getField("tag");
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException("Missing tag field."
+ + " To be parsed automatically, enums must have"
+ + " a String field called tag. Enum class: " + fieldType.getName()
+ + " Containing class: " + field.getDeclaringClass().getName()
+ + " Field: " + field.getName());
+
+ }
+ if (!String.class.equals(tagField.getType())) {
+ throw new RuntimeException("Tag field is not string."
+ + " To be parsed automatically, enums must have"
+ + " a String field called tag. Enum class: " + fieldType.getName()
+ + " Containing class: " + field.getDeclaringClass().getName()
+ + " Field: " + field.getName()
+ + " Tag field type: " + tagField.getType().getName());
+ }
+
+ for (final Object enumValue: fieldType.getEnumConstants()) {
+ String tag = null;
+ try {
+ tag = (String)tagField.get(enumValue);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ mTags.put(tag, (Enum)enumValue);
+ }
+ }
+
+ void setField(int lineNumber, Record object, String value) throws ParseException {
+ final Enum enumValue = mTags.get(value);
+ if (enumValue == null) {
+ throw new ParseException(lineNumber, "Could not find enum for field "
+ + getName() + " for tag: " + value);
+ }
+ try {
+ mField.set(object, enumValue);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ void setArray(int lineNumber, Record object, ArrayList<String> values,
+ int startIndex, int endIndex) throws ParseException {
+ try {
+ final Object array = Array.newInstance(mField.getType().getComponentType(),
+ endIndex-startIndex);
+ for (int i=0; i<(endIndex-startIndex); i++) {
+ final String value = values.get(startIndex+i);
+ final Enum enumValue = mTags.get(value);
+ if (enumValue == null) {
+ throw new ParseException(lineNumber, "Could not find enum for field "
+ + getName() + " for tag: " + value);
+ }
+ Array.set(array, i, enumValue);
+ }
+ mField.set(object, array);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | ExceptionInInitializerError ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * Factory for the record classes. Uses reflection to create
+ * the fields.
+ */
+ private static class RecordFactory {
+ private String mTag;
+ private Class<? extends Record> mSubclass;
+ private ArrayList<FieldSetter> mFieldSetters;
+
+ RecordFactory(String tag, Class<? extends Record> subclass,
+ ArrayList<FieldSetter> fieldSetters) {
+ mTag = tag;
+ mSubclass = subclass;
+ mFieldSetters = fieldSetters;
+ }
+
+ /**
+ * Create an object of one of the subclasses of Record, and fill
+ * in the fields marked with the Field annotation.
+ *
+ * @return a new Record with the fields filled in. If there are missing
+ * fields, the {@link Record.complete} field will be set to false.
+ */
+ Record create(RawBatteryStats bs, int dumpsysVersion, int lineNumber,
+ ArrayList<String> fieldValues) {
+ final boolean debug = false;
+ Record record = null;
+ try {
+ if (debug) {
+ System.err.println("Creating object: " + mSubclass.getSimpleName());
+ }
+ record = mSubclass.newInstance();
+ } catch (IllegalAccessException | InstantiationException
+ | ExceptionInInitializerError | SecurityException ex) {
+ throw new RuntimeException("Exception creating " + mSubclass.getName()
+ + " for '" + mTag + "' record.", ex);
+ }
+ record.complete = true;
+ int fieldIndex = 0;
+ int setterIndex = 0;
+ while (fieldIndex < fieldValues.size() && setterIndex < mFieldSetters.size()) {
+ final FieldSetter setter = mFieldSetters.get(setterIndex);
+
+ if (dumpsysVersion >= 0 && dumpsysVersion < setter.getAdded()) {
+ // The version being parsed doesn't have the field for this setter,
+ // so skip the setter but not the field.
+ setterIndex++;
+ continue;
+ }
+
+ final String value = fieldValues.get(fieldIndex);
+ try {
+ if (debug) {
+ System.err.println(" setting field " + setter + " to: " + value);
+ }
+ if (setter.isArray()) {
+ setter.setArray(lineNumber, record, fieldValues,
+ fieldIndex, fieldValues.size());
+ // The rest of the fields have been consumed.
+ fieldIndex = fieldValues.size();
+ setterIndex = mFieldSetters.size();
+ break;
+ } else {
+ setter.setField(lineNumber, record, value);
+ }
+ } catch (ParseException ex) {
+ bs.addWarning(lineNumber, WarningId.BAD_FIELD_TYPE, fieldValues,
+ ex.getMessage(), mTag, value);
+ record.complete = false;
+ }
+
+ fieldIndex++;
+ setterIndex++;
+ }
+
+ // If there are extra fields, this record is complete, there are just
+ // extra values, so we issue a warning but don't mark it incomplete.
+ if (fieldIndex < fieldValues.size()) {
+ bs.addWarning(lineNumber, WarningId.TOO_MANY_FIELDS, fieldValues,
+ "Line '" + mTag + "' has extra fields.",
+ mTag, Integer.toString(fieldIndex), Integer.toString(fieldValues.size()));
+ if (debug) {
+ for (int i=0; i<mFieldSetters.size(); i++) {
+ System.err.println(" setter: [" + i + "] " + mFieldSetters.get(i));
+ }
+ }
+ }
+
+ // If we have any fields that are missing, add a warning and return null.
+ for (; setterIndex < mFieldSetters.size(); setterIndex++) {
+ final FieldSetter setter = mFieldSetters.get(setterIndex);
+ if (dumpsysVersion >= 0 && dumpsysVersion >= setter.getAdded()) {
+ bs.addWarning(lineNumber, WarningId.NOT_ENOUGH_FIELDS, fieldValues,
+ "Line '" + mTag + "' missing field: index=" + setterIndex
+ + " name=" + setter.getName(),
+ mTag, Integer.toString(setterIndex));
+ record.complete = false;
+ }
+ }
+
+ return record;
+ }
+ }
+
+ /**
+ * Parse the input stream and return a RawBatteryStats object.
+ */
+ public static RawBatteryStats parse(InputStream input) throws ParseException, IOException {
+ final RawBatteryStats result = new RawBatteryStats();
+ result.parseImpl(input);
+ return result;
+ }
+
+ /**
+ * Get a record.
+ * <p>
+ * If multiple of that record are found, returns the first one. There will already
+ * have been a warning recorded if the count annotation did not match what was in the
+ * csv.
+ * <p>
+ * Returns null if there are no records of that type.
+ */
+ public <T extends Record> T getSingle(Class<T> cl) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return null;
+ }
+ // Notes:
+ // - List can never be empty because the list itself wouldn't have been added.
+ // - Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return (T)list.get(0);
+ }
+
+ /**
+ * Get a record.
+ * <p>
+ * If multiple of that record are found, returns the first one that matches that uid.
+ * <p>
+ * Returns null if there are no records of that type that match the given uid.
+ */
+ public <T extends Record> T getSingle(Class<T> cl, int uid) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return null;
+ }
+ for (final Record record: list) {
+ if (record.uid == uid) {
+ // Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return (T)record;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the records of the given type.
+ */
+ public <T extends Record> List<T> getMultiple(Class<T> cl) {
+ final List<Record> list = mRecordsByType.get(cl.getName());
+ if (list == null) {
+ return ImmutableList.<T>of();
+ }
+ // Cast is safe because list was populated based on class name (let's assume
+ // there's only one class loader involved here).
+ return ImmutableList.copyOf((List<T>)list);
+ }
+
+ /**
+ * Get the UIDs that are covered by this batterystats dump.
+ */
+ public Set<AttributionKey> getApps() {
+ return mApps;
+ }
+
+ /**
+ * No public constructor. Use {@link #parse}.
+ */
+ private RawBatteryStats() {
+ }
+
+ /**
+ * Get the list of Record objects that were parsed from the csv.
+ */
+ public List<Record> getRecords() {
+ return mRecords;
+ }
+
+ /**
+ * Gets the warnings that were encountered during parsing.
+ */
+ public List<Warning> getWarnings() {
+ return mWarnings;
+ }
+
+ /**
+ * Implementation of the csv parsing.
+ */
+ private void parseImpl(InputStream input) throws ParseException, IOException {
+ // Parse the csv
+ CsvParser.parse(input, new CsvParser.LineProcessor() {
+ @Override
+ public void onLine(int lineNumber, ArrayList<String> fields)
+ throws ParseException {
+ handleCsvLine(lineNumber, fields);
+ }
+ });
+
+ // Gather the records by class name for the getSingle() and getMultiple() functions.
+ indexRecords();
+
+ // Gather the uids from all the places UIDs come from, for getApps().
+ indexApps();
+ }
+
+ /**
+ * Handle a line of CSV input, creating the right Record object.
+ */
+ private void handleCsvLine(int lineNumber, ArrayList<String> fields) throws ParseException {
+ // The standard rows all have the 4 core fields. Anything less isn't what we're
+ // looking for.
+ if (fields.size() <= 4) {
+ addWarning(lineNumber, WarningId.TOO_FEW_FIELDS_FOR_LINE_TYPE, fields,
+ "Line with too few fields (" + fields.size() + ")",
+ Integer.toString(fields.size()));
+ return;
+ }
+
+ final String lineType = fields.get(3);
+
+ // Handle the vers line specially, because we need the version number
+ // to make the rest of the machinery work.
+ if ("vers".equals(lineType)) {
+ final String versionText = fields.get(4);
+ try {
+ mDumpsysVersion = Integer.parseInt(versionText);
+ } catch (NumberFormatException ex) {
+ addWarning(lineNumber, WarningId.BAD_DUMPSYS_VERSION, fields,
+ "Couldn't parse dumpsys version number: '" + versionText,
+ versionText);
+ }
+ }
+
+ // Find the right factory.
+ final RecordFactory factory = sFactories.get(lineType);
+ if (factory == null) {
+ addWarning(lineNumber, WarningId.NO_MATCHING_LINE_TYPE, fields,
+ "No Record for line type '" + lineType + "'",
+ lineType);
+ return;
+ }
+
+ // Create the record.
+ final Record record = factory.create(this, mDumpsysVersion, lineNumber, fields);
+ mRecords.add(record);
+ }
+
+ /**
+ * Add to the list of warnings.
+ */
+ private void addWarning(int lineNumber, WarningId id,
+ ArrayList<String> fields, String message, String... extras) {
+ mWarnings.add(new Warning(lineNumber, id, fields, message, extras));
+ final boolean debug = false;
+ if (debug) {
+ final StringBuilder text = new StringBuilder("line ");
+ text.append(lineNumber);
+ text.append(": WARNING: ");
+ text.append(message);
+ text.append("\n fields: ");
+ for (int i=0; i<fields.size(); i++) {
+ final String field = fields.get(i);
+ if (field.indexOf('"') >= 0) {
+ text.append('"');
+ text.append(field.replace("\"", "\"\""));
+ text.append('"');
+ } else {
+ text.append(field);
+ }
+ if (i != fields.size() - 1) {
+ text.append(',');
+ }
+ }
+ text.append('\n');
+ for (String extra: extras) {
+ text.append(" extra: ");
+ text.append(extra);
+ text.append('\n');
+ }
+ System.err.print(text.toString());
+ }
+ }
+
+ /**
+ * Group records by class name.
+ */
+ private void indexRecords() {
+ final HashMap<String,ArrayList<Record>> map = new HashMap<String,ArrayList<Record>>();
+
+ // Iterate over all of the records
+ for (Record record: mRecords) {
+ final String className = record.getClass().getName();
+
+ ArrayList<Record> list = map.get(className);
+ if (list == null) {
+ list = new ArrayList<Record>();
+ map.put(className, list);
+ }
+
+ list.add(record);
+ }
+
+ // Make it immutable
+ final HashMap<String,ImmutableList<Record>> result
+ = new HashMap<String,ImmutableList<Record>>();
+ for (HashMap.Entry<String,ArrayList<Record>> entry: map.entrySet()) {
+ result.put(entry.getKey(), ImmutableList.copyOf(entry.getValue()));
+ }
+
+ // Initialize here so uninitialized access will result in NPE.
+ mRecordsByType = ImmutableMap.copyOf(result);
+ }
+
+ /**
+ * Collect the UIDs from the csv.
+ *
+ * They come from two places.
+ * <ul>
+ * <li>The uid to package name map entries ({@link #Uid}) at the beginning.
+ * <li>The uid fields of the rest of the entries, some of which might not
+ * have package names associated with them.
+ * </ul>
+ *
+ * TODO: Is this where we should also do the logic about the special UIDs?
+ */
+ private void indexApps() {
+ final HashMap<Integer,HashSet<String>> uids = new HashMap<Integer,HashSet<String>>();
+
+ // The Uid rows, from which we get package names
+ for (Uid record: getMultiple(Uid.class)) {
+ HashSet<String> list = uids.get(record.uidKey);
+ if (list == null) {
+ list = new HashSet<String>();
+ uids.put(record.uidKey, list);
+ }
+ list.add(record.pkg);
+ }
+
+ // The uid fields of everything
+ for (Record record: mRecords) {
+ // The 0 in the INFO records isn't really root, it's just unfilled data.
+ // The root uid (0) will show up practically in every record, but don't force it.
+ if (record.category != Category.INFO) {
+ if (uids.get(record.uid) == null) {
+ // There is no other data about this UID, but it does exist!
+ uids.put(record.uid, new HashSet<String>());
+ }
+ }
+ }
+
+ // Turn our temporary lists of package names into AttributionKeys.
+ final HashSet<AttributionKey> result = new HashSet<AttributionKey>();
+ for (HashMap.Entry<Integer,HashSet<String>> entry: uids.entrySet()) {
+ result.add(new AttributionKey(entry.getKey(), entry.getValue()));
+ }
+
+ // Initialize here so uninitialized access will result in NPE.
+ mApps = ImmutableSet.copyOf(result);
+ }
+
+ /**
+ * Init the factory classes.
+ */
+ static {
+ for (Class<?> cl: RawBatteryStats.class.getClasses()) {
+ final Line lineAnnotation = cl.getAnnotation(Line.class);
+ if (lineAnnotation != null && Record.class.isAssignableFrom(cl)) {
+ final ArrayList<FieldSetter> fieldSetters = new ArrayList<FieldSetter>();
+
+ for (java.lang.reflect.Field field: cl.getFields()) {
+ final Field fa = field.getAnnotation(Field.class);
+ if (fa != null) {
+ final Class<?> fieldType = field.getType();
+ final Class<?> innerType = fieldType.isArray()
+ ? fieldType.getComponentType()
+ : fieldType;
+ if (Integer.TYPE.equals(innerType)) {
+ fieldSetters.add(new IntFieldSetter(fa.index(), fa.added(), field));
+ } else if (Long.TYPE.equals(innerType)) {
+ fieldSetters.add(new LongFieldSetter(fa.index(), fa.added(), field));
+ } else if (String.class.equals(innerType)) {
+ fieldSetters.add(new StringFieldSetter(fa.index(), fa.added(), field));
+ } else if (innerType.isEnum()) {
+ fieldSetters.add(new EnumFieldSetter(fa.index(), fa.added(), field));
+ } else {
+ throw new RuntimeException("Unsupported field type '"
+ + fieldType.getName() + "' on "
+ + cl.getName() + "." + field.getName());
+ }
+ }
+ }
+ // Sort by index
+ Collections.sort(fieldSetters, new Comparator<FieldSetter>() {
+ @Override
+ public int compare(FieldSetter a, FieldSetter b) {
+ return a.getIndex() - b.getIndex();
+ }
+ });
+ // Only the last one can be an array
+ for (int i=0; i<fieldSetters.size()-1; i++) {
+ if (fieldSetters.get(i).isArray()) {
+ throw new RuntimeException("Only the last (highest index) @Field"
+ + " in class " + cl.getName() + " can be an array: "
+ + fieldSetters.get(i).getName());
+ }
+ }
+ // Add to the map
+ sFactories.put(lineAnnotation.tag(), new RecordFactory(lineAnnotation.tag(),
+ (Class<Record>)cl, fieldSetters));
+ }
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/SpecialApp.java b/tools/powermodel/src/com/android/powermodel/SpecialApp.java
new file mode 100644
index 0000000..df1e1fb
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/SpecialApp.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+/**
+ * Identifiers for well-known apps that have unique characteristics.
+ *
+ * @more
+ * This includes three categories:
+ * <ul>
+ * <li><b>Built-in system components</b> – These have predefined UIDs that are
+ * always the same. For example, the system UID is always 1000.</li>
+ * <li><b>Well known apps with shared UIDs</b> – These do not have predefined
+ * UIDs (i.e. are different on each device), but since they have shared UIDs
+ * with varying sets of package names (GmsCore is the canonical example), we
+ * have special logic to capture these into a single entity with a well defined
+ * key. These have the {@link #uid uid} field set to
+ * {@link Uid#UID_VARIES Uid.UID_VARIES}.</li>
+ * <li><b>Synthetic remainder app</b> – The {@link #REMAINDER REMAINDER} app doesn't
+ * represent a real app. It contains accounting for usage which is not attributed
+ * to any UID. This app has the {@link #uid uid} field set to
+ * {@link Uid#UID_SYNTHETIC Uid.UID_SYNTHETIC}.</li>
+ * </ul>
+ */
+public enum SpecialApp {
+
+ /**
+ * Synthetic app that accounts for the remaining amount of resources used
+ * that is unaccounted for by apps, or overcounted because of inaccuracies
+ * in the model.
+ */
+ REMAINDER(Uid.UID_SYNTHETIC),
+
+ /**
+ * Synthetic app that holds system-wide numbers, for example the total amount
+ * of various resources used, device-wide.
+ */
+ GLOBAL(Uid.UID_SYNTHETIC),
+
+ SYSTEM(1000),
+
+ GOOGLE_SERVICES(Uid.UID_VARIES);
+
+ /**
+ * Constants for SpecialApps where the uid is not actually a UID.
+ */
+ public static class Uid {
+ /**
+ * Constant to indicate that this special app does not have a fixed UID.
+ */
+ public static final int UID_VARIES = -1;
+
+ /**
+ * Constant to indicate that this special app is not actually an app with a UID.
+ *
+ * @see SpecialApp#REMAINDER
+ * @see SpecialApp#GLOBAL
+ */
+ public static final int UID_SYNTHETIC = -2;
+ }
+
+ /**
+ * The fixed UID value of this special app, or {@link #UID_VARIES} if there
+ * isn't one.
+ */
+ public final int uid;
+
+ private SpecialApp(int uid) {
+ this.uid = uid;
+ }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
index 27d25b8..63ff3a6 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class AudioProfile extends ComponentProfile {
+ public float onMa;
}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
index 27d25b8..8f5e7d0 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class BluetoothProfile extends ComponentProfile {
+ public float idleMa;
+ public float rxMa;
+ public float txMa;
}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
index 27d25b8..8ee22d0 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CameraProfile extends ComponentProfile {
+ public float onMa;
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
new file mode 100644
index 0000000..0b34fc8
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CpuProfile extends ComponentProfile {
+ public float suspendMa;
+ public float idleMa;
+ public float activeMa;
+ public Cluster[] clusters;
+
+ public static class Cluster {
+ public int coreCount;
+ public float onMa;
+ public Frequency[] frequencies;
+ }
+
+ public static class Frequency {
+ public int speedHz;
+ public float onMa;
+ }
+
+ public static class Builder {
+ private float mSuspendMa;
+ private float mIdleMa;
+ private float mActiveMa;
+ private int[] mCoreCount;
+ private HashMap<Integer,Float> mClusterOnPower = new HashMap<Integer,Float>();
+ private HashMap<Integer,int[]> mCoreSpeeds = new HashMap<Integer,int[]>();
+ private HashMap<Integer,float[]> mCorePower = new HashMap<Integer,float[]>();
+
+ public Builder() {
+ }
+
+ public void setSuspendMa(float value) throws ParseException {
+ mSuspendMa = value;
+ }
+
+ public void setIdleMa(float value) throws ParseException {
+ mIdleMa = value;
+ }
+
+ public void setActiveMa(float value) throws ParseException {
+ mActiveMa = value;
+ }
+
+ public void setCoreCount(int[] value) throws ParseException {
+ mCoreCount = Arrays.copyOf(value, value.length);
+ }
+
+ public void setClusterPower(int cluster, float value) throws ParseException {
+ mClusterOnPower.put(cluster, value);
+ }
+
+ public void setCoreSpeeds(int cluster, int[] value) throws ParseException {
+ mCoreSpeeds.put(cluster, Arrays.copyOf(value, value.length));
+ float[] power = mCorePower.get(cluster);
+ if (power != null && value.length != power.length) {
+ throw new ParseException("length of cpu.core_speeds.cluster" + cluster
+ + " (" + value.length + ") is different from length of"
+ + " cpu.core_power.cluster" + cluster + " (" + power.length + ")");
+ }
+ if (mCoreCount != null && cluster >= mCoreCount.length) {
+ throw new ParseException("cluster " + cluster
+ + " in cpu.core_speeds.cluster" + cluster
+ + " is larger than the number of clusters specified in cpu.clusters.cores ("
+ + mCoreCount.length + ")");
+ }
+ }
+
+ public void setCorePower(int cluster, float[] value) throws ParseException {
+ mCorePower.put(cluster, Arrays.copyOf(value, value.length));
+ int[] speeds = mCoreSpeeds.get(cluster);
+ if (speeds != null && value.length != speeds.length) {
+ throw new ParseException("length of cpu.core_power.cluster" + cluster
+ + " (" + value.length + ") is different from length of"
+ + " cpu.clusters.cores" + cluster + " (" + speeds.length + ")");
+ }
+ if (mCoreCount != null && cluster >= mCoreCount.length) {
+ throw new ParseException("cluster " + cluster
+ + " in cpu.core_power.cluster" + cluster
+ + " is larger than the number of clusters specified in cpu.clusters.cores ("
+ + mCoreCount.length + ")");
+ }
+ }
+
+ public CpuProfile build() throws ParseException {
+ final CpuProfile result = new CpuProfile();
+
+ // Validate cluster count
+
+ // All null or none null
+ // TODO
+
+ // Same size
+ // TODO
+
+ // No gaps
+ // TODO
+
+ // Fill in values
+ result.suspendMa = mSuspendMa;
+ result.idleMa = mIdleMa;
+ result.activeMa = mActiveMa;
+ if (mCoreCount != null) {
+ result.clusters = new Cluster[mCoreCount.length];
+ for (int i = 0; i < result.clusters.length; i++) {
+ final Cluster cluster = result.clusters[i] = new Cluster();
+ cluster.coreCount = mCoreCount[i];
+ cluster.onMa = mClusterOnPower.get(i);
+ int[] speeds = mCoreSpeeds.get(i);
+ float[] power = mCorePower.get(i);
+ cluster.frequencies = new Frequency[speeds.length];
+ for (int j = 0; j < speeds.length; j++) {
+ final Frequency freq = cluster.frequencies[j] = new Frequency();
+ freq.speedHz = speeds[j];
+ freq.onMa = power[j];
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
similarity index 70%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
index 27d25b8..c85f3ff 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
@@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class FlashlightProfile extends ComponentProfile {
+ public float onMa;
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
new file mode 100644
index 0000000..83c06a7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class GpsProfile extends ComponentProfile {
+ public float onMa;
+ public float[] signalQualityMa;
+
+ public static class Builder {
+ private float onMa;
+ private float[] mSignalQualityMa;
+
+ public Builder() {
+ }
+
+ public void setOnMa(float value) throws ParseException {
+ onMa = value;
+ }
+
+ public void setSignalMa(float[] value) throws ParseException {
+ mSignalQualityMa = value;
+ }
+
+ public GpsProfile build() throws ParseException {
+ GpsProfile result = new GpsProfile();
+ result.onMa = onMa;
+ result.signalQualityMa = mSignalQualityMa == null
+ ? new float[0]
+ : Arrays.copyOf(mSignalQualityMa, mSignalQualityMa.length);
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java
new file mode 100644
index 0000000..cb70051
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.PowerProfile;
+import com.android.powermodel.util.Conversion;
+
+/**
+ * Encapsulates the work done by the celluar modem on behalf of an app.
+ */
+public class ModemAppActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemAppActivity.
+ */
+ public ModemAppActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * The number of packets received by the app.
+ */
+ public long rxPacketCount;
+
+ /**
+ * The number of packets sent by the app.
+ */
+ public long txPacketCount;
+
+ @Override
+ public ModemAppPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ // Profile
+ final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM);
+ if (modemProfile == null) {
+ // TODO: This is kind of a big problem... Should this throw instead?
+ return null;
+ }
+
+ // Activity
+ final ModemGlobalActivity global
+ = (ModemGlobalActivity)activityReport.findGlobalComponent(Component.MODEM);
+ if (global == null) {
+ return null;
+ }
+
+ final double averageModemPowerMa = getAverageModemPowerMa(modemProfile);
+ final long totalPacketCount = global.rxPacketCount + global.txPacketCount;
+ final long appPacketCount = this.rxPacketCount + this.txPacketCount;
+
+ final ModemAppPower result = new ModemAppPower();
+ result.attribution = this.attribution;
+ result.activity = this;
+ result.powerMah = Conversion.msToHr(
+ (totalPacketCount > 0 ? (appPacketCount / (double)totalPacketCount) : 0)
+ * global.totalActiveTimeMs
+ * averageModemPowerMa);
+ return result;
+ }
+
+ static final double getAverageModemPowerMa(ModemProfile profile) {
+ double sumMa = profile.getRxMa();
+ for (float powerAtTxLevelMa: profile.getTxMa()) {
+ sumMa += powerAtTxLevelMa;
+ }
+ return sumMa / (profile.getTxMa().length + 1);
+ }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
index 27d25b8..f553127 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentPower;
+
+public class ModemAppPower extends ComponentPower<ModemAppActivity> {
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java
new file mode 100644
index 0000000..6dbfbc2
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.RawBatteryStats;
+import com.android.powermodel.SpecialApp;
+
+public class ModemBatteryStatsReader {
+ private ModemBatteryStatsReader() {
+ }
+
+ public static List<ComponentActivity> createActivities(RawBatteryStats bs) {
+ final List<ComponentActivity> result = new ArrayList<ComponentActivity>();
+
+ // The whole system
+ createGlobal(result, bs);
+
+ // The apps
+ createApps(result, bs);
+
+ // The synthetic "cell" app.
+ createRemainder(result, bs);
+
+ return result;
+ }
+
+ private static void createGlobal(List<ComponentActivity> result, RawBatteryStats bs) {
+ final ModemGlobalActivity global
+ = new ModemGlobalActivity(new AttributionKey(SpecialApp.GLOBAL));
+
+ final RawBatteryStats.GlobalNetwork gn = bs.getSingle(RawBatteryStats.GlobalNetwork.class);
+ final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class);
+
+ // Null here just means no network activity.
+ if (gn != null && misc != null) {
+ global.rxPacketCount = gn.mobileRxTotalPackets;
+ global.txPacketCount = gn.mobileTxTotalPackets;
+
+ global.totalActiveTimeMs = misc.mobileRadioActiveTimeMs;
+ }
+
+ result.add(global);
+ }
+
+ private static void createApps(List<ComponentActivity> result, RawBatteryStats bs) {
+ for (AttributionKey key: bs.getApps()) {
+ final int uid = key.getUid();
+ final RawBatteryStats.Network network
+ = bs.getSingle(RawBatteryStats.Network.class, uid);
+
+ // Null here just means no network activity.
+ if (network != null) {
+ final ModemAppActivity app = new ModemAppActivity(key);
+
+ app.rxPacketCount = network.mobileRxPackets;
+ app.txPacketCount = network.mobileTxPackets;
+
+ result.add(app);
+ }
+ }
+ }
+
+ private static void createRemainder(List<ComponentActivity> result, RawBatteryStats bs) {
+ final RawBatteryStats.SignalStrengthTime strength
+ = bs.getSingle(RawBatteryStats.SignalStrengthTime.class);
+ final RawBatteryStats.SignalScanningTime scanning
+ = bs.getSingle(RawBatteryStats.SignalScanningTime.class);
+ final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class);
+
+ if (strength != null && scanning != null && misc != null) {
+ final ModemRemainderActivity remainder
+ = new ModemRemainderActivity(new AttributionKey(SpecialApp.REMAINDER));
+
+ // Signal strength buckets
+ remainder.strengthTimeMs = strength.phoneSignalStrengthTimeMs;
+
+ // Time spent scanning
+ remainder.scanningTimeMs = scanning.phoneSignalScanningTimeMs;
+
+ // Unaccounted for active time
+ final long totalActiveTimeMs = misc.mobileRadioActiveTimeMs;
+ long appActiveTimeMs = 0;
+ for (RawBatteryStats.Network nw: bs.getMultiple(RawBatteryStats.Network.class)) {
+ appActiveTimeMs += nw.mobileRadioActiveTimeUs / 1000;
+ }
+ remainder.activeTimeMs = totalActiveTimeMs - appActiveTimeMs;
+
+ result.add(remainder);
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java
new file mode 100644
index 0000000..a53b53e
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.ComponentPower;
+import com.android.powermodel.PowerProfile;
+
+/**
+ * Encapsulates total work done by the modem for the device.
+ */
+public class ModemGlobalActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemGlobalActivity.
+ */
+ public ModemGlobalActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * Returns the total number of packets received in the whole device.
+ */
+ public long rxPacketCount;
+
+ /**
+ * Returns the total number of packets sent in the whole device.
+ */
+ public long txPacketCount;
+
+ /**
+ * Returns the total time the radio was active in the whole device.
+ */
+ public long totalActiveTimeMs;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
new file mode 100644
index 0000000..cda72ee
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ModemProfile extends ComponentProfile {
+ public float sleepMa;
+ public float idleMa;
+ public float scanningMa;
+ public float rxMa;
+ public float[] txMa;
+
+ public float getSleepMa() {
+ return sleepMa;
+ }
+
+ public float getIdleMa() {
+ return idleMa;
+ }
+
+ public float getRxMa() {
+ return rxMa;
+ }
+
+ public float[] getTxMa() {
+ return Arrays.copyOf(txMa, txMa.length);
+ }
+
+ public float getScanningMa() {
+ return scanningMa;
+ }
+
+ public static class Builder {
+ private float mSleepMa;
+ private float mIdleMa;
+ private float mRxMa;
+ private float[] mTxMa;
+ private float mScanningMa;
+
+ public Builder() {
+ }
+
+ public void setSleepMa(float value) throws ParseException {
+ mSleepMa = value;
+ }
+
+ public void setIdleMa(float value) throws ParseException {
+ mIdleMa = value;
+ }
+
+ public void setRxMa(float value) throws ParseException {
+ mRxMa = value;
+ }
+
+ public void setTxMa(float[] value) throws ParseException {
+ mTxMa = Arrays.copyOf(value, value.length);
+ }
+
+ public void setScanningMa(float value) throws ParseException {
+ mScanningMa = value;
+ }
+
+ public ModemProfile build() throws ParseException {
+ ModemProfile result = new ModemProfile();
+ result.sleepMa = mSleepMa;
+ result.idleMa = mIdleMa;
+ result.rxMa = mRxMa;
+ result.txMa = mTxMa == null ? new float[0] : mTxMa;
+ result.scanningMa = mScanningMa;
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java
new file mode 100644
index 0000000..0e268c2
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.PowerProfile;
+import com.android.powermodel.util.Conversion;
+
+/**
+ * Encapsulates the work done by the remaining
+ */
+public class ModemRemainderActivity extends ComponentActivity {
+ /**
+ * Construct a new ModemRemainderActivity.
+ */
+ public ModemRemainderActivity(AttributionKey attribution) {
+ super(attribution);
+ }
+
+ /**
+ * Number of milliseconds spent at each of the signal strengths.
+ */
+ public long[] strengthTimeMs;
+
+ /**
+ * Number of milliseconds spent scanning for a network.
+ */
+ public long scanningTimeMs;
+
+ /**
+ * Number of milliseconds that the radio is active for reasons other
+ * than an app transmitting and receiving data.
+ */
+ public long activeTimeMs;
+
+ @Override
+ public ModemRemainderPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+ // Profile
+ final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM);
+ if (modemProfile == null) {
+ return null;
+ }
+
+ // Activity
+ final ModemRemainderPower result = new ModemRemainderPower();
+ result.attribution = this.attribution;
+ result.activity = this;
+
+ // strengthMah
+ // TODO: If the array lengths don't match... then?
+ result.strengthMah = new double[this.strengthTimeMs.length];
+ for (int i=0; i<this.strengthTimeMs.length; i++) {
+ result.strengthMah[i] = Conversion.msToHr(
+ this.strengthTimeMs[i] * modemProfile.getTxMa()[i]);
+ result.powerMah += result.strengthMah[i];
+ }
+
+ // scanningMah
+ result.scanningMah = Conversion.msToHr(this.scanningTimeMs * modemProfile.getScanningMa());
+ result.powerMah += result.scanningMah;
+
+ // activeMah
+ result.activeMah = Conversion.msToHr(
+ this.activeTimeMs * ModemAppActivity.getAverageModemPowerMa(modemProfile));
+ result.powerMah += result.activeMah;
+
+ return result;
+ }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
index 27d25b8..7f38cd3 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
@@ -13,12 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentPower;
+
+public class ModemRemainderPower extends ComponentPower<ModemRemainderActivity> {
+
+ public double[] strengthMah;
+
+ public double scanningMah;
+
+ public double activeMah;
}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
index 27d25b8..e1051c6 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ScreenProfile extends ComponentProfile {
+ public float onMa;
+ public float fullMa;
+ public float ambientMa;
}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
index 27d25b8..5152795 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
@@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class VideoProfile extends ComponentProfile {
+ public float onMa;
}
+
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
index 27d25b8..6f424bf 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.biometrics;
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
- void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class WifiProfile extends ComponentProfile {
+ public float idleMa;
+ public float rxMa;
+ public float txMa;
}
+
diff --git a/tools/powermodel/src/com/android/powermodel/util/Conversion.java b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
new file mode 100644
index 0000000..e556c25
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel.util;
+
+public class Conversion {
+
+ /**
+ * Convert the the float[] to an int[].
+ * <p>
+ * Values are rounded to the nearest integral value. Null input
+ * results in null output.
+ */
+ public static int[] toIntArray(float[] value) {
+ if (value == null) {
+ return null;
+ }
+ int[] result = new int[value.length];
+ for (int i=0; i<result.length; i++) {
+ result[i] = (int)(value[i] + 0.5f);
+ }
+ return result;
+ }
+
+ public static double msToHr(double ms) {
+ return ms / 3600.0 / 1000.0;
+ }
+
+ /**
+ * No public constructor.
+ */
+ private Conversion() {
+ }
+}
diff --git a/tools/powermodel/test-resource/bs.csv b/tools/powermodel/test-resource/bs.csv
new file mode 100644
index 0000000..6e84120
--- /dev/null
+++ b/tools/powermodel/test-resource/bs.csv
@@ -0,0 +1,7 @@
+9,0,i,vers,32,177,PPR1.180326.002,PQ1A.181105.015
+9,0,i,uid,10139,com.google.android.gm
+9,0,l,gn,108060756,17293456,4896592,3290614,97840,72941,6903,8107,390,105
+9,0,l,m,2590630,0,384554,3943868,5113727,265,2565483,0,16,0,0,0,0,192,25331,3472068,17,3543323,14,614050,0
+9,10139,l,nt,13688501,534571,13842,7792,9925,5577,30,67,190051799,27,0,0,5,3,126020,42343,13842,7792,207,167,30,67
+9,0,l,sgt,3066958,0,34678,1643364,7045084
+9,0,l,sst,2443805
diff --git a/tools/powermodel/test-resource/power_profile.xml b/tools/powermodel/test-resource/power_profile.xml
new file mode 100644
index 0000000..8e388ea
--- /dev/null
+++ b/tools/powermodel/test-resource/power_profile.xml
@@ -0,0 +1,170 @@
+<?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.
+-->
+
+<!-- Test power profile that parses correctly. -->
+<device>
+ <item name="battery.capacity">2915</item>
+
+ <!-- Number of cores each CPU cluster contains -->
+ <array name="cpu.clusters.cores">
+ <value>4</value>
+ <value>2</value>
+ </array>
+
+ <!-- Power consumption when CPU is suspended -->
+ <item name="cpu.suspend">1.3</item>
+
+ <!-- Additional power consumption when CPU is in a kernel idle loop -->
+ <item name="cpu.idle">3.9</item>
+
+ <!-- Additional power consumption by CPU excluding cluster and core when
+ running -->
+ <item name="cpu.active">18.33</item>
+
+ <!-- Additional power consumption by CPU cluster0 itself when running
+ excluding cores in it -->
+ <item name="cpu.cluster_power.cluster0">2.41</item>
+
+ <!-- Additional power consumption by CPU cluster1 itself when running
+ excluding cores in it -->
+ <item name="cpu.cluster_power.cluster1">5.29</item>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster0">
+ <value>100000</value>
+ <value>303200</value>
+ <value>380000</value>
+ <value>476000</value>
+ <value>552800</value>
+ <value>648800</value>
+ <value>725600</value>
+ <value>802400</value>
+ <value>879200</value>
+ </array>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster1">
+ <value>825600</value>
+ <value>902400</value>
+ <value>979200</value>
+ <value>1056000</value>
+ <value>1209600</value>
+ <value>1286400</value>
+ <value>1363200</value>
+ </array>
+
+ <!-- Additional power used by a CPU core from cluster 0 when running at
+ different speeds, excluding cluster and active cost -->
+ <array name="cpu.core_power.cluster0">
+ <value>0.29</value>
+ <value>0.63</value>
+ <value>1.23</value>
+ <value>1.24</value>
+ <value>2.47</value>
+ <value>2.54</value>
+ <value>3.60</value>
+ <value>3.64</value>
+ <value>4.42</value>
+ </array>
+
+ <!-- Additional power used by a CPU core from cluster 1 when running at
+ different speeds, excluding cluster and active cost -->
+ <array name="cpu.core_power.cluster1">
+ <value>28.98</value>
+ <value>31.40</value>
+ <value>33.33</value>
+ <value>40.12</value>
+ <value>44.10</value>
+ <value>90.14</value>
+ <value>100</value>
+ </array>
+
+ <!-- Additional power used when screen is ambient mode -->
+ <item name="ambient.on">12</item>
+
+ <!-- Additional power used when screen is turned on at minimum brightness -->
+ <item name="screen.on">102.4</item>
+ <!-- Additional power used when screen is at maximum brightness, compared to
+ screen at minimum brightness -->
+ <item name="screen.full">1234</item>
+
+ <!-- Average power used by the camera flash module when on -->
+ <item name="camera.flashlight">1233.47</item>
+
+ <!-- Average power use by the camera subsystem for a typical camera
+ application. Intended as a rough estimate for an application running a
+ preview and capturing approximately 10 full-resolution pictures per
+ minute. -->
+ <item name="camera.avg">941</item>
+
+ <!-- Additional power used when video is playing -->
+ <item name="video">123</item>
+
+ <!-- Additional power used when audio is playing -->
+ <item name="audio">12</item>
+
+ <!-- Cellular modem related values.-->
+ <item name="modem.controller.sleep">1</item>
+ <item name="modem.controller.idle">44</item>
+ <item name="modem.controller.rx">11</item>
+ <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+ <value>16</value>
+ <value>19</value>
+ <value>22</value>
+ <value>73</value>
+ <value>132</value>
+ </array>
+ <item name="modem.controller.voltage">1400</item>
+ <item name="radio.scanning">12</item>
+
+ <!-- GPS related values.-->
+ <item name="gps.on">1</item>
+ <array name="gps.signalqualitybased"> <!-- Strength 0 to 1 -->
+ <value>88</value>
+ <value>07</value>
+ </array>
+ <item name="gps.voltage">1500</item>
+
+ <!-- Idle Receive current for wifi radio in mA.-->
+ <item name="wifi.controller.idle">2</item>
+
+ <!-- Rx current for wifi radio in mA.-->
+ <item name="wifi.controller.rx">123</item>
+
+ <!-- Tx current for wifi radio in mA-->
+ <item name="wifi.controller.tx">333</item>
+
+ <!-- Operating volatage for wifi radio in mV.-->
+ <item name="wifi.controller.voltage">3700</item>
+
+ <!-- Idle current for bluetooth in mA.-->
+ <item name="bluetooth.controller.idle">0.02</item>
+
+ <!-- Rx current for bluetooth in mA.-->
+ <item name="bluetooth.controller.rx">3</item>
+
+ <!-- Tx current for bluetooth in mA-->
+ <item name="bluetooth.controller.tx">5</item>
+
+ <!-- Operating voltage for bluetooth in mV.-->
+ <item name="bluetooth.controller.voltage">3300</item>
+
+</device>
+
+
diff --git a/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java
new file mode 100644
index 0000000..e7b2c37
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+import com.android.powermodel.component.ModemAppActivity;
+import com.android.powermodel.component.ModemGlobalActivity;
+import com.android.powermodel.component.ModemRemainderActivity;
+
+/**
+ * Tests {@link BatteryStatsReader}.
+ */
+public class BatteryStatsReaderTest {
+ private static InputStream loadCsvStream() {
+ return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv");
+ }
+
+ @Test public void testModemGlobal() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final AppActivity global = report.findApp(SpecialApp.GLOBAL);
+ Assert.assertNotNull(global);
+
+ final ModemGlobalActivity modem
+ = (ModemGlobalActivity)global.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(97840, modem.rxPacketCount);
+ Assert.assertEquals(72941, modem.txPacketCount);
+ Assert.assertEquals(5113727, modem.totalActiveTimeMs);
+ }
+
+ @Test public void testModemApp() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final List<AppActivity> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppActivity gmail = gmailList.get(0);
+
+ final ModemAppActivity modem
+ = (ModemAppActivity)gmail.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(9925, modem.rxPacketCount);
+ Assert.assertEquals(5577, modem.txPacketCount);
+ }
+
+ @Test public void testModemRemainder() throws Exception {
+ final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+ final AppActivity remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ final ModemRemainderActivity modem
+ = (ModemRemainderActivity)remainder.getComponentActivity(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertArrayEquals(new long[] { 3066958, 0, 34678, 1643364, 7045084 },
+ modem.strengthTimeMs);
+ Assert.assertEquals(2443805, modem.scanningTimeMs);
+ Assert.assertEquals(4923676, modem.activeTimeMs);
+ }
+}
diff --git a/tools/powermodel/test/com/android/powermodel/CsvParserTest.java b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java
new file mode 100644
index 0000000..55dde41
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java
@@ -0,0 +1,311 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests {@link PowerProfile}
+ */
+public class CsvParserTest {
+
+ class LineCollector implements CsvParser.LineProcessor {
+ ArrayList<ArrayList<String>> results = new ArrayList<ArrayList<String>>();
+
+ @Override
+ public void onLine(int lineNumber, ArrayList<String> fields) {
+ System.out.println(lineNumber);
+ for (String str: fields) {
+ System.out.println("-->" + str + "<--");
+ }
+ results.add(fields);
+ }
+ }
+
+ private void assertEquals(String[][] expected, ArrayList<ArrayList<String>> results) {
+ final String[][] resultArray = new String[results.size()][];
+ for (int i=0; i<results.size(); i++) {
+ final ArrayList<String> list = results.get(i);
+ resultArray[i] = list.toArray(new String[list.size()]);
+ }
+ Assert.assertArrayEquals(expected, resultArray);
+ }
+
+ private String makeString(int length) {
+ final StringBuilder str = new StringBuilder();
+ for (int i=0; i<length; i++) {
+ str.append('a');
+ }
+ return str.toString();
+ }
+
+ @Test public void testEmpty() throws Exception {
+ final String text = "";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ }, collector.results);
+ }
+
+ @Test public void testOnlyNewline() throws Exception {
+ final String text = "\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ }, collector.results);
+ }
+
+ @Test public void testTwoLines() throws Exception {
+ final String text = "one,twoo,3\nfour,5,six\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "one", "twoo", "3", },
+ { "four", "5", "six", },
+ }, collector.results);
+ }
+
+
+ @Test public void testEscapedEmpty() throws Exception {
+ final String text = "\"\",\"\",\"\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "", "", "", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedText() throws Exception {
+ final String text = "\"one\",\"twoo\",\"3\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "one", "twoo", "3", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedQuotes() throws Exception {
+ final String text = "\"\"\"\",\"\"\"\"\"\",\"\"\"\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "\"", "\"\"", "\"", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedCommas() throws Exception {
+ final String text = "\",\",\",\",\",\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { ",", ",", ",", },
+ }, collector.results);
+ }
+
+ @Test public void testEscapedQuotesAndCommas() throws Exception {
+ final String text = "\"\"\",\",\"\"\",\",\"\"\",\"\n";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "\",", "\",", "\",", },
+ }, collector.results);
+ }
+
+ @Test public void testNoNewline() throws Exception {
+ final String text = "a,b,c";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "c", }
+ }, collector.results);
+ }
+
+ @Test public void testNoNewlineWithCommas() throws Exception {
+ final String text = "a,b,,";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "", "" }
+ }, collector.results);
+ }
+
+ @Test public void testNoNewlineWithQuote() throws Exception {
+ final String text = "a,b,\",\"";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "a", "b", "," }
+ }, collector.results);
+ }
+
+ @Test public void testNoCommas() throws Exception {
+ final String text = "aasdfadfadfad";
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { "aasdfadfadfad", }
+ }, collector.results);
+ }
+
+ @Test public void testMaxLength() throws Exception {
+ final String text = makeString(CsvParser.MAX_FIELD_SIZE);
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { text, }
+ }, collector.results);
+ }
+
+ @Test public void testMaxLengthTwice() throws Exception {
+ String big = makeString(CsvParser.MAX_FIELD_SIZE);
+ final String text = big + "," + big;
+ System.out.println("Test: [" + text + "]");
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, big, }
+ }, collector.results);
+ }
+
+ @Test public void testTooLong() throws Exception {
+ final String text = makeString(CsvParser.MAX_FIELD_SIZE+1);
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ try {
+ CsvParser.parse(is, collector);
+ throw new RuntimeException("Expected CsvParser.parse to throw ParseException");
+ } catch (ParseException ex) {
+ // good
+ }
+ }
+
+ @Test public void testBufferBoundary() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",b,c,d,e,f,g";
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "b", "c", "d", "e", "f", "g", }
+ }, collector.results);
+ }
+
+ @Test public void testBufferBoundaryEmpty() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",,,,,,";
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "", "", "", "", "", "", }
+ }, collector.results);
+ }
+
+ // Checks that the escaping and sawQuote behavior is correct at the buffer boundary
+ @Test public void testBufferBoundaryEscapingEven() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-2);
+ final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big;
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "\"\"\"\"\"", big }
+ }, collector.results);
+ }
+
+ // Checks that the escaping and sawQuote behavior is correct at the buffer boundary
+ @Test public void testBufferBoundaryEscapingOdd() throws Exception {
+ final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+ final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big;
+ final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ LineCollector collector = new LineCollector();
+
+ CsvParser.parse(is, collector);
+
+ assertEquals(new String[][] {
+ { big, "\"\"\"\"\"", big }
+ }, collector.results);
+ }
+
+}
diff --git a/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java
new file mode 100644
index 0000000..ab45831
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import org.junit.Assert;
+import org.junit.Test;
+
+/*
+ * Additional tests needed:
+ * - CPU clusters with mismatching counts of speeds and coefficients
+ * - Extra fields
+ * - Name listed twice
+ */
+
+/**
+ * Tests {@link PowerProfile}
+ */
+public class PowerProfileTest {
+ private static final float EPSILON = 0.00001f;
+
+ private static InputStream loadPowerProfileStream() {
+ return PowerProfileTest.class.getResourceAsStream("/power_profile.xml");
+ }
+
+ @Test public void testReadGood() throws Exception {
+ final InputStream is = loadPowerProfileStream();
+
+ final PowerProfile profile = PowerProfile.parse(is);
+
+ // Audio
+ final AudioProfile audio = (AudioProfile)profile.getComponent(Component.AUDIO);
+ Assert.assertEquals(12.0f, audio.onMa, EPSILON);
+
+ // Bluetooth
+ final BluetoothProfile bluetooth
+ = (BluetoothProfile)profile.getComponent(Component.BLUETOOTH);
+ Assert.assertEquals(0.02f, bluetooth.idleMa, EPSILON);
+ Assert.assertEquals(3.0f, bluetooth.rxMa, EPSILON);
+ Assert.assertEquals(5.0f, bluetooth.txMa, EPSILON);
+
+ // Camera
+ final CameraProfile camera = (CameraProfile)profile.getComponent(Component.CAMERA);
+ Assert.assertEquals(941.0f, camera.onMa, EPSILON);
+
+ // CPU
+ final CpuProfile cpu = (CpuProfile)profile.getComponent(Component.CPU);
+ Assert.assertEquals(1.3f, cpu.suspendMa, EPSILON);
+ Assert.assertEquals(3.9f, cpu.idleMa, EPSILON);
+ Assert.assertEquals(18.33f, cpu.activeMa, EPSILON);
+ Assert.assertEquals(2, cpu.clusters.length);
+ // Cluster 0
+ Assert.assertEquals(4, cpu.clusters[0].coreCount);
+ Assert.assertEquals(2.41f, cpu.clusters[0].onMa, EPSILON);
+ Assert.assertEquals(9, cpu.clusters[0].frequencies.length, EPSILON);
+ Assert.assertEquals(100000, cpu.clusters[0].frequencies[0].speedHz);
+ Assert.assertEquals(0.29f, cpu.clusters[0].frequencies[0].onMa, EPSILON);
+ Assert.assertEquals(303200, cpu.clusters[0].frequencies[1].speedHz);
+ Assert.assertEquals(0.63f, cpu.clusters[0].frequencies[1].onMa, EPSILON);
+ Assert.assertEquals(380000, cpu.clusters[0].frequencies[2].speedHz);
+ Assert.assertEquals(1.23f, cpu.clusters[0].frequencies[2].onMa, EPSILON);
+ Assert.assertEquals(476000, cpu.clusters[0].frequencies[3].speedHz);
+ Assert.assertEquals(1.24f, cpu.clusters[0].frequencies[3].onMa, EPSILON);
+ Assert.assertEquals(552800, cpu.clusters[0].frequencies[4].speedHz);
+ Assert.assertEquals(2.47f, cpu.clusters[0].frequencies[4].onMa, EPSILON);
+ Assert.assertEquals(648800, cpu.clusters[0].frequencies[5].speedHz);
+ Assert.assertEquals(2.54f, cpu.clusters[0].frequencies[5].onMa, EPSILON);
+ Assert.assertEquals(725600, cpu.clusters[0].frequencies[6].speedHz);
+ Assert.assertEquals(3.60f, cpu.clusters[0].frequencies[6].onMa, EPSILON);
+ Assert.assertEquals(802400, cpu.clusters[0].frequencies[7].speedHz);
+ Assert.assertEquals(3.64f, cpu.clusters[0].frequencies[7].onMa, EPSILON);
+ Assert.assertEquals(879200, cpu.clusters[0].frequencies[8].speedHz);
+ Assert.assertEquals(4.42f, cpu.clusters[0].frequencies[8].onMa, EPSILON);
+ // Cluster 1
+ Assert.assertEquals(2, cpu.clusters[1].coreCount);
+ Assert.assertEquals(5.29f, cpu.clusters[1].onMa, EPSILON);
+ Assert.assertEquals(7, cpu.clusters[1].frequencies.length, EPSILON);
+ Assert.assertEquals(825600, cpu.clusters[1].frequencies[0].speedHz);
+ Assert.assertEquals(28.98f, cpu.clusters[1].frequencies[0].onMa, EPSILON);
+ Assert.assertEquals(902400, cpu.clusters[1].frequencies[1].speedHz);
+ Assert.assertEquals(31.40f, cpu.clusters[1].frequencies[1].onMa, EPSILON);
+ Assert.assertEquals(979200, cpu.clusters[1].frequencies[2].speedHz);
+ Assert.assertEquals(33.33f, cpu.clusters[1].frequencies[2].onMa, EPSILON);
+ Assert.assertEquals(1056000, cpu.clusters[1].frequencies[3].speedHz);
+ Assert.assertEquals(40.12f, cpu.clusters[1].frequencies[3].onMa, EPSILON);
+ Assert.assertEquals(1209600, cpu.clusters[1].frequencies[4].speedHz);
+ Assert.assertEquals(44.10f, cpu.clusters[1].frequencies[4].onMa, EPSILON);
+ Assert.assertEquals(1286400, cpu.clusters[1].frequencies[5].speedHz);
+ Assert.assertEquals(90.14f, cpu.clusters[1].frequencies[5].onMa, EPSILON);
+ Assert.assertEquals(1363200, cpu.clusters[1].frequencies[6].speedHz);
+ Assert.assertEquals(100f, cpu.clusters[1].frequencies[6].onMa, EPSILON);
+
+ // Flashlight
+ final FlashlightProfile flashlight
+ = (FlashlightProfile)profile.getComponent(Component.FLASHLIGHT);
+ Assert.assertEquals(1233.47f, flashlight.onMa, EPSILON);
+
+ // GPS
+ final GpsProfile gps = (GpsProfile)profile.getComponent(Component.GPS);
+ Assert.assertEquals(1.0f, gps.onMa, EPSILON);
+ Assert.assertEquals(2, gps.signalQualityMa.length);
+ Assert.assertEquals(88.0f, gps.signalQualityMa[0], EPSILON);
+ Assert.assertEquals(7.0f, gps.signalQualityMa[1], EPSILON);
+
+ // Modem
+ final ModemProfile modem = (ModemProfile)profile.getComponent(Component.MODEM);
+ Assert.assertEquals(1.0f, modem.sleepMa, EPSILON);
+ Assert.assertEquals(44.0f, modem.idleMa, EPSILON);
+ Assert.assertEquals(12.0f, modem.scanningMa, EPSILON);
+ Assert.assertEquals(11.0f, modem.rxMa, EPSILON);
+ Assert.assertEquals(5, modem.txMa.length);
+ Assert.assertEquals(16.0f, modem.txMa[0], EPSILON);
+ Assert.assertEquals(19.0f, modem.txMa[1], EPSILON);
+ Assert.assertEquals(22.0f, modem.txMa[2], EPSILON);
+ Assert.assertEquals(73.0f, modem.txMa[3], EPSILON);
+ Assert.assertEquals(132.0f, modem.txMa[4], EPSILON);
+
+ // Screen
+ final ScreenProfile screen = (ScreenProfile)profile.getComponent(Component.SCREEN);
+ Assert.assertEquals(102.4f, screen.onMa, EPSILON);
+ Assert.assertEquals(1234.0f, screen.fullMa, EPSILON);
+ Assert.assertEquals(12.0f, screen.ambientMa, EPSILON);
+
+ // Video
+ final VideoProfile video = (VideoProfile)profile.getComponent(Component.VIDEO);
+ Assert.assertEquals(123.0f, video.onMa, EPSILON);
+
+ // Wifi
+ final WifiProfile wifi = (WifiProfile)profile.getComponent(Component.WIFI);
+ Assert.assertEquals(2.0f, wifi.idleMa, EPSILON);
+ Assert.assertEquals(123.0f, wifi.rxMa, EPSILON);
+ Assert.assertEquals(333.0f, wifi.txMa, EPSILON);
+ }
+}
diff --git a/tools/powermodel/test/com/android/powermodel/PowerReportTest.java b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java
new file mode 100644
index 0000000..1a61737
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+import com.android.powermodel.component.ModemAppPower;
+import com.android.powermodel.component.ModemRemainderPower;
+
+/**
+ * Tests {@link PowerReport}.
+ */
+public class PowerReportTest {
+ private static final double EPSILON = 0.001;
+ private static final double MS_PER_HR = 3600000.0;
+
+ private static final double AVERAGE_MODEM_POWER = ((11+16+19+22+73+132) / 6.0);
+ private static final double GMAIL_MODEM_MAH = ((9925+5577) / (double)(97840+72941))
+ * 5113727 * AVERAGE_MODEM_POWER * (1.0 / 3600 / 1000);
+ private static final double GMAIL_MAH
+ = GMAIL_MODEM_MAH;
+
+ private static final double REMAINDER_MODEM_MAH
+ = (1.0 / 3600 / 1000)
+ * ((3066958 * 16) + (0 * 19) + (34678 * 22) + (1643364 * 73) + (7045084 * 132)
+ + (2443805 * 12)
+ + (4923676 * AVERAGE_MODEM_POWER));
+ private static final double REMAINDER_MAH
+ = REMAINDER_MODEM_MAH;
+
+ private static final double TOTAL_MAH
+ = GMAIL_MAH
+ + REMAINDER_MAH;
+
+ private static InputStream loadPowerProfileStream() {
+ return PowerProfileTest.class.getResourceAsStream("/power_profile.xml");
+ }
+
+ private static InputStream loadCsvStream() {
+ return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv");
+ }
+
+ private static PowerReport loadPowerReport() throws Exception {
+ final PowerProfile profile = PowerProfile.parse(loadPowerProfileStream());
+ final ActivityReport activity = BatteryStatsReader.parse(loadCsvStream());
+ return PowerReport.createReport(profile, activity);
+ }
+
+ @Test public void testModemApp() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final List<AppPower> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppPower gmail = gmailList.get(0);
+
+ final ModemAppPower modem = (ModemAppPower)gmail.getComponentPower(Component.MODEM);
+ Assert.assertNotNull(modem);
+ Assert.assertEquals(GMAIL_MODEM_MAH, modem.powerMah, EPSILON);
+ }
+
+ @Test public void testModemRemainder() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final AppPower remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ final ModemRemainderPower modem
+ = (ModemRemainderPower)remainder.getComponentPower(Component.MODEM);
+ Assert.assertNotNull(modem);
+
+ Assert.assertArrayEquals(new double[] {
+ 3066958 * 16.0 / MS_PER_HR,
+ 0 * 19.0 / MS_PER_HR,
+ 34678 * 22.0 / MS_PER_HR,
+ 1643364 * 73.0 / MS_PER_HR,
+ 7045084 * 132.0 / MS_PER_HR },
+ modem.strengthMah, EPSILON);
+ Assert.assertEquals(2443805 * 12 / MS_PER_HR, modem.scanningMah, EPSILON);
+ Assert.assertEquals(4923676 * AVERAGE_MODEM_POWER / MS_PER_HR, modem.activeMah, EPSILON);
+
+ Assert.assertEquals(REMAINDER_MODEM_MAH, modem.powerMah, EPSILON);
+ }
+
+ @Test public void testAppTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final List<AppPower> gmailList = report.findApp("com.google.android.gm");
+ Assert.assertEquals(1, gmailList.size());
+ final AppPower gmail = gmailList.get(0);
+
+ Assert.assertEquals(GMAIL_MAH, gmail.getAppPowerMah(), EPSILON);
+ }
+
+ @Test public void testRemainderTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ final AppPower remainder = report.findApp(SpecialApp.REMAINDER);
+ Assert.assertNotNull(remainder);
+
+ Assert.assertEquals(REMAINDER_MAH, remainder.getAppPowerMah(), EPSILON);
+ }
+
+ @Test public void testTotal() throws Exception {
+ final PowerReport report = loadPowerReport();
+
+ Assert.assertEquals(TOTAL_MAH, report.getTotalPowerMah(), EPSILON);
+ }
+}
+
diff --git a/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java
new file mode 100644
index 0000000..fbcac41
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package com.android.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * Tests {@link RawBatteryStats}.
+ */
+public class RawBatteryStatsTest {
+ private static final int BS_VERSION = 32;
+
+ private static InputStream makeCsv(String... lines) {
+ return makeCsv(BS_VERSION, lines);
+ }
+
+ private static InputStream makeCsv(int version, String... lines) {
+ final StringBuilder result = new StringBuilder("9,0,i,vers,");
+ result.append(version);
+ result.append(",177,PPR1.180326.002,PQ1A.181105.015\n");
+ for (String line: lines) {
+ result.append(line);
+ result.append('\n');
+ }
+ return new ByteArrayInputStream(result.toString().getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Test public void testVersion() throws Exception {
+ final InputStream is = makeCsv();
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.Version line = (RawBatteryStats.Version)records.get(0);
+
+ Assert.assertEquals(0, bs.getWarnings().size());
+ Assert.assertEquals(true, line.complete);
+
+ Assert.assertEquals(9, line.lineVersion);
+ Assert.assertEquals(0, line.uid);
+ Assert.assertEquals(RawBatteryStats.Category.INFO, line.category);
+ Assert.assertEquals("vers", line.lineType);
+
+ Assert.assertEquals(BS_VERSION, line.dumpsysVersion);
+ Assert.assertEquals(177, line.parcelVersion);
+ Assert.assertEquals("PPR1.180326.002", line.startPlatformVersion);
+ Assert.assertEquals("PQ1A.181105.015", line.endPlatformVersion);
+ }
+
+ @Test public void testUid() throws Exception {
+ final InputStream is = makeCsv("9,0,i,uid,1000,com.example.app");
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.Uid line = (RawBatteryStats.Uid)records.get(1);
+
+ Assert.assertEquals(1000, line.uidKey);
+ Assert.assertEquals("com.example.app", line.pkg);
+ }
+
+ @Test public void testVarargs() throws Exception {
+ final InputStream is = makeCsv("9,0,i,gmcd,1,2,3,4,5,6,7");
+
+ final RawBatteryStats bs = RawBatteryStats.parse(is);
+ final List<RawBatteryStats.Record> records = bs.getRecords();
+ final RawBatteryStats.GlobalModemController line
+ = (RawBatteryStats.GlobalModemController)records.get(1);
+
+ Assert.assertEquals(1, line.idleMs);
+ Assert.assertEquals(2, line.rxTimeMs);
+ Assert.assertEquals(3, line.powerMaMs);
+ Assert.assertEquals(4, line.txTimeMs.length);
+ Assert.assertEquals(4, line.txTimeMs[0]);
+ Assert.assertEquals(5, line.txTimeMs[1]);
+ Assert.assertEquals(6, line.txTimeMs[2]);
+ Assert.assertEquals(7, line.txTimeMs[3]);
+ }
+}
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
index 1d4c435..d368136 100644
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
+++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
@@ -28,6 +28,7 @@
import java.io.IOException;
import java.io.PrintStream;
+import java.net.URLEncoder;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
@@ -38,7 +39,9 @@
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
/**
@@ -108,10 +111,25 @@
"startline",
"startcol",
"endline",
- "endcol"
+ "endcol",
+ "properties"
);
}
+ private String encodeAnnotationProperties(AnnotationMirror annotation) {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
+ : annotation.getElementValues().entrySet()) {
+ if (sb.length() > 0) {
+ sb.append("&");
+ }
+ sb.append(e.getKey().getSimpleName())
+ .append("=")
+ .append(URLEncoder.encode(e.getValue().toString()));
+ }
+ return sb.toString();
+ }
+
/**
* Maps an annotated element to the source position of the @UnsupportedAppUsage annotation
* attached to it. It returns CSV in the format:
@@ -137,7 +155,8 @@
lines.getLineNumber(pair.fst.pos().getStartPosition()),
lines.getColumnNumber(pair.fst.pos().getStartPosition()),
lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
- lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)));
+ lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
+ encodeAnnotationProperties(unsupportedAppUsage));
}
/**
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 8585ae9..88b7e2e 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -1128,7 +1128,10 @@
hadStringOrChain = true;
fprintf(out, " jbyte* jbyte_array%d;\n", argIndex);
fprintf(out, " const char* str%d;\n", argIndex);
- fprintf(out, " if (arg%d != NULL) {\n", argIndex);
+ fprintf(out,
+ " if (arg%d != NULL && env->GetArrayLength(arg%d) > "
+ "0) {\n",
+ argIndex, argIndex);
fprintf(out,
" jbyte_array%d = "
"env->GetByteArrayElements(arg%d, NULL);\n",
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3ec8a41..c6acd02 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -58,7 +58,7 @@
*/
oneway void requestActivityInfo(in ResultReceiver result);
- ParceledListSlice getConfiguredNetworks();
+ ParceledListSlice getConfiguredNetworks(String packageName);
ParceledListSlice getPrivilegedConfiguredNetworks();
@@ -90,11 +90,11 @@
List<ScanResult> getScanResults(String callingPackage);
- void disconnect(String packageName);
+ boolean disconnect(String packageName);
- void reconnect(String packageName);
+ boolean reconnect(String packageName);
- void reassociate(String packageName);
+ boolean reassociate(String packageName);
WifiInfo getConnectionInfo(String callingPackage);
@@ -190,8 +190,8 @@
void unregisterNetworkRequestMatchCallback(int callbackIdentifier);
- boolean addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+ int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
- boolean removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+ int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7aff03c..89b6703 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -25,6 +25,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.net.ConnectivityManager;
@@ -137,6 +138,55 @@
public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3;
/**
+ * Maximum number of active network suggestions allowed per app.
+ * @hide
+ */
+ public static final int NETWORK_SUGGESTIONS_MAX_PER_APP =
+ ActivityManager.isLowRamDeviceStatic() ? 256 : 1024;
+
+ /**
+ * Reason code if all of the network suggestions were successfully added or removed.
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0;
+
+ /**
+ * Reason code if there was an internal error in the platform while processing the addition or
+ * removal of suggestions.
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1;
+
+ /**
+ * Reason code if one or more of the network suggestions added already exists in platform's
+ * database.
+ * @see WifiNetworkSuggestion#equals(Object)
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2;
+
+ /**
+ * Reason code if the number of network suggestions provided by the app crosses the max
+ * threshold set per app.
+ * @see #getMaxNumberOfNetworkSuggestionsPerApp()
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3;
+
+ /**
+ * Reason code if one or more of the network suggestions removed does not exist in platform's
+ * database.
+ */
+ public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4;
+
+ @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = {
+ STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP,
+ STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
+ })
+
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkSuggestionsStatusCode {}
+
+ /**
* Broadcast intent action indicating whether Wi-Fi scanning is allowed currently
* @hide
*/
@@ -903,8 +953,12 @@
* establish a connection to a remembered access point that is
* within range, and will do periodic scans if there are remembered
* access points but none are in range.
+ *
+ * @deprecated This API is non-functional and will have no impact.
*/
+ @Deprecated
public static final int WIFI_MODE_FULL = 1;
+
/**
* In this Wi-Fi lock mode, Wi-Fi will be kept active,
* but the only operation that will be supported is initiation of
@@ -913,28 +967,62 @@
* nor will periodic scans be automatically performed looking for
* remembered access points. Scans must be explicitly requested by
* an application in this mode.
+ *
+ * @deprecated This API is non-functional and will have no impact.
*/
+ @Deprecated
public static final int WIFI_MODE_SCAN_ONLY = 2;
+
/**
- * In this Wi-Fi lock mode, Wi-Fi will be kept active as in mode
- * {@link #WIFI_MODE_FULL} but it operates at high performance
- * with minimum packet loss and low packet latency even when
- * the device screen is off. This mode will consume more power
- * and hence should be used only when there is a need for such
- * an active connection.
+ * In this Wi-Fi lock mode, Wi-Fi will not go to power save.
+ * This results in operating with low packet latency.
+ * The lock is active even when the device screen is off or
+ * the acquiring application is running in the background.
+ * This mode will consume more power and hence should be used only
+ * when there is a need for this tradeoff.
* <p>
* An example use case is when a voice connection needs to be
- * kept active even after the device screen goes off. Holding the
- * regular {@link #WIFI_MODE_FULL} lock will keep the wifi
- * connection active, but the connection can be lossy.
+ * kept active even after the device screen goes off.
* Holding a {@link #WIFI_MODE_FULL_HIGH_PERF} lock for the
- * duration of the voice call will improve the call quality.
+ * duration of the voice call may improve the call quality.
* <p>
- * When there is no support from the hardware, this lock mode
- * will have the same behavior as {@link #WIFI_MODE_FULL}
+ * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_HIGH_PERF}
+ * lock will have no impact.
*/
public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
+ /**
+ * In this Wi-Fi lock mode, Wi-Fi will operate with a priority to achieve low latency.
+ * {@link #WIFI_MODE_FULL_LOW_LATENCY} lock has the following limitations:
+ * <ol>
+ * <li>The lock is only active when the screen is on.</li>
+ * <li>The lock is only active when the acquiring app is running in the foreground.</li>
+ * </ol>
+ * Low latency mode optimizes for reduced packet latency,
+ * and as a result other performance measures may suffer when there are trade-offs to make:
+ * <ol>
+ * <li>Battery life may be reduced.</li>
+ * <li>Throughput may be reduced.</li>
+ * <li>Frequency of Wi-Fi scanning may be reduced. This may result in: </li>
+ * <ul>
+ * <li>The device may not roam or switch to the AP with highest signal quality.</li>
+ * <li>Location accuracy may be reduced.</li>
+ * </ul>
+ * </ol>
+ * <p>
+ * Example use cases are real time gaming or virtual reality applications where
+ * low latency is a key factor for user experience.
+ * <p>
+ * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_LOW_LATENCY}
+ * lock will cause the device not to go power save.
+ * <p>
+ * Note: For an app which acquires both {@link #WIFI_MODE_FULL_LOW_LATENCY} and
+ * {@link #WIFI_MODE_FULL_HIGH_PERF} locks, {@link #WIFI_MODE_FULL_LOW_LATENCY}
+ * lock will be effective when app is running in foreground and screen is on,
+ * while the {@link #WIFI_MODE_FULL_HIGH_PERF} lock will take effect otherwise.
+ */
+ public static final int WIFI_MODE_FULL_LOW_LATENCY = 4;
+
/** Anything worse than or equal to this will show 0 bars. */
@UnsupportedAppUsage
private static final int MIN_RSSI = -100;
@@ -1067,7 +1155,7 @@
public List<WifiConfiguration> getConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
- mService.getConfiguredNetworks();
+ mService.getConfiguredNetworks(mContext.getOpPackageName());
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1126,7 +1214,6 @@
* @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
try {
@@ -1497,12 +1584,13 @@
* suggestion back using this API.</li>
*
* @param networkSuggestions List of network suggestions provided by the app.
- * @return true on success, false if any of the suggestions match (See
+ * @return Status code corresponding to the values in {@link NetworkSuggestionsStatusCode}.
* {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
* @throws {@link SecurityException} if the caller is missing required permissions.
*/
@RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
- public boolean addNetworkSuggestions(@NonNull List<WifiNetworkSuggestion> networkSuggestions) {
+ public @NetworkSuggestionsStatusCode int addNetworkSuggestions(
+ @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
try {
return mService.addNetworkSuggestions(networkSuggestions, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1510,21 +1598,20 @@
}
}
-
/**
- * Remove a subset of or all of networks from previously provided suggestions by the app to the
- * device.
+ * Remove some or all of the network suggestions that were previously provided by the app.
* See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters.
* See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used.
*
* @param networkSuggestions List of network suggestions to be removed. Pass an empty list
* to remove all the previous suggestions provided by the app.
- * @return true on success, false if any of the suggestions do not match any suggestions
- * previously provided by the app. Any matching suggestions are removed from the device and
- * will not be considered for any further connection attempts.
+ * @return Status code corresponding to the values in
+ * {@link NetworkSuggestionsStatusCode}.
+ * Any matching suggestions are removed from the device and will not be considered for any
+ * further connection attempts.
*/
@RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
- public boolean removeNetworkSuggestions(
+ public @NetworkSuggestionsStatusCode int removeNetworkSuggestions(
@NonNull List<WifiNetworkSuggestion> networkSuggestions) {
try {
return mService.removeNetworkSuggestions(
@@ -1535,6 +1622,15 @@
}
/**
+ * Returns the max number of network suggestions that are allowed per app on the device.
+ * @see #addNetworkSuggestions(List)
+ * @see #removeNetworkSuggestions(List)
+ */
+ public int getMaxNumberOfNetworkSuggestionsPerApp() {
+ return NETWORK_SUGGESTIONS_MAX_PER_APP;
+ }
+
+ /**
* Add or update a Passpoint configuration. The configuration provides a credential
* for connecting to Passpoint networks that are operated by the Passpoint
* service provider specified in the configuration.
@@ -1761,8 +1857,7 @@
@Deprecated
public boolean disconnect() {
try {
- mService.disconnect(mContext.getOpPackageName());
- return true;
+ return mService.disconnect(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1786,8 +1881,7 @@
@Deprecated
public boolean reconnect() {
try {
- mService.reconnect(mContext.getOpPackageName());
- return true;
+ return mService.reconnect(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1811,8 +1905,7 @@
@Deprecated
public boolean reassociate() {
try {
- mService.reassociate(mContext.getOpPackageName());
- return true;
+ return mService.reassociate(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1889,6 +1982,8 @@
public static final int WIFI_FEATURE_WPA3_SUITE_B = 0x10000000; // WPA3-Enterprise Suite-B
/** @hide */
public static final int WIFI_FEATURE_OWE = 0x20000000; // Enhanced Open
+ /** @hide */
+ public static final int WIFI_FEATURE_LOW_LATENCY = 0x40000000; // Low Latency modes
private int getSupportedFeatures() {
try {
@@ -2132,14 +2227,14 @@
* existing networks. You should assume the network IDs can be different
* after calling this method.
*
- * @return {@code false} Will always return true.
+ * @return {@code false}.
* @deprecated There is no need to call this method -
* {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)}
* and {@link #removeNetwork(int)} already persist the configurations automatically.
*/
@Deprecated
public boolean saveConfiguration() {
- return true;
+ return false;
}
/**
@@ -3406,6 +3501,11 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void connect(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
// Use INVALID_NETWORK_ID for arg1 when passing a config object
@@ -3426,7 +3526,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void connect(int networkId, ActionListener listener) {
if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
@@ -3452,7 +3557,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void save(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
@@ -3471,7 +3581,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void forget(int netId, ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener));
@@ -3486,7 +3601,12 @@
* initialized again
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void disable(int netId, ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener));
@@ -3498,6 +3618,12 @@
* @param SSID, in the format of WifiConfiguration's SSID.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK
+ })
public void disableEphemeralNetwork(String SSID) {
if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
try {
@@ -3744,9 +3870,8 @@
/**
* Creates a new WifiLock.
*
- * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
- * {@link #WIFI_MODE_FULL_HIGH_PERF} and {@link #WIFI_MODE_SCAN_ONLY} for
- * descriptions of the types of Wi-Fi locks.
+ * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL_HIGH_PERF}
+ * and {@link #WIFI_MODE_FULL_LOW_LATENCY} for descriptions of the types of Wi-Fi locks.
* @param tag a tag for the WifiLock to identify it in debugging messages. This string is
* never shown to the user under normal conditions, but should be descriptive
* enough to identify your application and the specific WifiLock within it, if it
@@ -3771,12 +3896,14 @@
* @return a new, unacquired WifiLock with the given tag.
*
* @see WifiLock
+ *
+ * @deprecated This API is non-functional.
*/
+ @Deprecated
public WifiLock createWifiLock(String tag) {
return new WifiLock(WIFI_MODE_FULL, tag);
}
-
/**
* Create a new MulticastLock
*
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index 87706b9..f73b9e5 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -42,8 +42,10 @@
public class WifiNetworkConfigBuilder {
private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*";
private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
- private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN =
+ private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN1 =
new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+ private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN2 =
+ new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.BROADCAST_ADDRESS);
private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK =
@@ -189,7 +191,13 @@
* Set the BSSID to use for filtering networks from scan results. Will only match network whose
* BSSID is identical to the specified value.
* <p>
- * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+ * <li>For network requests ({@link NetworkSpecifier}), built using
+ * {@link #buildNetworkSpecifier}, sets the BSSID to use for filtering networks from scan
+ * results. Will only match networks whose BSSID is identical to specified value.</li>
+ * <li>For network suggestions ({@link WifiNetworkSuggestion}), built using
+ * {@link #buildNetworkSuggestion()}, sets a specific BSSID for the network suggestion.
+ * If set, only the specified BSSID with the specified SSID will be considered for connection.
+ * If not set, all BSSIDs with the specified SSID will be considered for connection.</li>
* <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or
* {@link #setBssidPattern(MacAddress, MacAddress)}.</li>
*
@@ -432,6 +440,9 @@
if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
}
+ if (mBssidPatternMatcher.second == MATCH_EXACT_BSSID_PATTERN_MASK) {
+ wifiConfiguration.BSSID = mBssidPatternMatcher.first.toString();
+ }
setSecurityParamsInWifiConfiguration(wifiConfiguration);
wifiConfiguration.hiddenSSID = mIsHiddenSSID;
wifiConfiguration.priority = mPriority;
@@ -460,7 +471,10 @@
&& mSsidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) {
return true;
}
- if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN)) {
+ if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN1)) {
+ return true;
+ }
+ if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN2)) {
return true;
}
return false;
@@ -474,6 +488,16 @@
return false;
}
+ private boolean hasSetMatchExactPattern() {
+ // exact ssid match with either match-all bssid or match-exact bssid.
+ if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL
+ && (mBssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)
+ || mBssidPatternMatcher.second.equals(MATCH_EXACT_BSSID_PATTERN_MASK))) {
+ return true;
+ }
+ return false;
+ }
+
private void validateSecurityParams() {
int numSecurityTypes = 0;
numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
@@ -566,9 +590,42 @@
}
/**
- * Create a network suggestion object use in
- * {@link WifiManager#addNetworkSuggestions(List)}.
+ * Create a network suggestion object use in {@link WifiManager#addNetworkSuggestions(List)}.
* See {@link WifiNetworkSuggestion}.
+ *<p>
+ * Note: Apps can set a combination of SSID using {@link #setSsid(String)} and BSSID
+ * using {@link #setBssid(MacAddress)} to provide more fine grained network suggestions to the
+ * platform.
+ * </p>
+ *
+ * For example:
+ * To provide credentials for one open, one WPA2 and one WPA3 network with their
+ * corresponding SSID's:
+ * {@code
+ * final WifiNetworkSuggestion suggestion1 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test111111")
+ * .buildNetworkSuggestion()
+ * final WifiNetworkSuggestion suggestion2 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test222222")
+ * .setWpa2Passphrase("test123456")
+ * .buildNetworkSuggestion()
+ * final WifiNetworkSuggestion suggestion3 =
+ * new WifiNetworkConfigBuilder()
+ * .setSsid("test333333")
+ * .setWpa3Passphrase("test6789")
+ * .buildNetworkSuggestion()
+ * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<WifiNetworkSuggestion> {{
+ * add(suggestion1);
+ * add(suggestion2);
+ * add(suggestion3);
+ * }};
+ * final WifiManager wifiManager =
+ * context.getSystemService(Context.WIFI_SERVICE);
+ * wifiManager.addNetworkSuggestions(suggestionsList);
+ * ...
+ * }
*
* @return Instance of {@link WifiNetworkSuggestion}.
* @throws IllegalStateException on invalid params set.
@@ -577,11 +634,14 @@
if (mSsidPatternMatcher == null) {
throw new IllegalStateException("setSsid should be invoked for suggestion");
}
- if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL
- || mBssidPatternMatcher != null) {
- throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
+ setMatchAnyPatternIfUnset();
+ if (!hasSetMatchExactPattern()) {
+ throw new IllegalStateException("none of setSsidPattern/setBssidPattern are"
+ " allowed for suggestion");
}
+ if (hasSetMatchNonePattern()) {
+ throw new IllegalStateException("cannot set match-none for suggestion");
+ }
validateSecurityParams();
return new WifiNetworkSuggestion(
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 25121e2..760f1e6 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -104,8 +104,8 @@
@Override
public int hashCode() {
- return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.allowedKeyManagement,
- suggestorUid);
+ return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
+ wifiConfiguration.allowedKeyManagement, suggestorUid);
}
/**
@@ -121,6 +121,7 @@
}
WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj;
return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
+ && Objects.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
&& Objects.equals(this.wifiConfiguration.allowedKeyManagement,
lhs.wifiConfiguration.allowedKeyManagement)
&& suggestorUid == lhs.suggestorUid;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 529548f..fc5caf0 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -184,6 +184,9 @@
public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
/** {@hide} */
public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource";
+ /** {@hide} */
+ public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName";
+
/**
* scan configuration parameters to be sent to {@link #startBackgroundScan}
*/
@@ -250,6 +253,14 @@
*/
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public int type = TYPE_LOW_LATENCY;
+ /**
+ * This scan request may ignore location settings while receiving scans. This should only
+ * be used in emergency situations.
+ * {@hide}
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ public boolean ignoreLocationSettings;
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
@@ -267,6 +278,7 @@
dest.writeInt(stepCount);
dest.writeInt(isPnoScan ? 1 : 0);
dest.writeInt(type);
+ dest.writeInt(ignoreLocationSettings ? 1 : 0);
if (channels != null) {
dest.writeInt(channels.length);
for (int i = 0; i < channels.length; i++) {
@@ -301,6 +313,7 @@
settings.stepCount = in.readInt();
settings.isPnoScan = in.readInt() == 1;
settings.type = in.readInt();
+ settings.ignoreLocationSettings = in.readInt() == 1;
int num_channels = in.readInt();
settings.channels = new ChannelSpec[num_channels];
for (int i = 0; i < num_channels; i++) {
@@ -798,6 +811,7 @@
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
}
@@ -812,8 +826,11 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams);
}
+
/**
* reports currently available scan results on appropriate listeners
* @return true if all scan results were reported correctly
@@ -821,7 +838,10 @@
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public boolean getScanResults() {
validateChannel();
- Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ Message reply =
+ mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams);
return reply.what == CMD_OP_SUCCEEDED;
}
@@ -856,6 +876,7 @@
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
@@ -870,7 +891,9 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams);
}
/**
@@ -879,7 +902,10 @@
*/
public List<ScanResult> getSingleScanResults() {
validateChannel();
- Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0);
+ Bundle scanParams = new Bundle();
+ scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+ Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0,
+ scanParams);
if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) {
return Arrays.asList(((ParcelableScanResults) reply.obj).getResults());
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 6772096..6631fa8 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -16,10 +16,16 @@
package android.net.wifi.p2p;
+import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
+import android.net.MacAddress;
import android.net.wifi.WpsInfo;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* A class representing a Wi-Fi P2p configuration for setting up a connection
@@ -38,12 +44,46 @@
*/
public WpsInfo wps;
+ /**
+ * The network name of a group, should be configured by helper method
+ */
+ /** @hide */
+ public String networkName = "";
+
+ /**
+ * The passphrase of a group, should be configured by helper method
+ */
+ /** @hide */
+ public String passphrase = "";
+
+ /**
+ * The required band for Group Owner
+ */
+ /** @hide */
+ public int groupOwnerBand = GROUP_OWNER_BAND_AUTO;
+
/** @hide */
public static final int MAX_GROUP_OWNER_INTENT = 15;
/** @hide */
@UnsupportedAppUsage
public static final int MIN_GROUP_OWNER_INTENT = 0;
+ /** @hide */
+ @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = {
+ GROUP_OWNER_BAND_AUTO,
+ GROUP_OWNER_BAND_2GHZ,
+ GROUP_OWNER_BAND_5GHZ
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GroupOwnerBandType {}
+
+ /**
+ * Recognized Group Owner required band.
+ */
+ public static final int GROUP_OWNER_BAND_AUTO = 0;
+ public static final int GROUP_OWNER_BAND_2GHZ = 1;
+ public static final int GROUP_OWNER_BAND_5GHZ = 2;
+
/**
* This is an integer value between 0 and 15 where 0 indicates the least
* inclination to be a group owner and 15 indicates the highest inclination
@@ -115,6 +155,10 @@
sbuf.append("\n wps: ").append(wps);
sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
sbuf.append("\n persist: ").append(netId);
+ sbuf.append("\n networkName: ").append(networkName);
+ sbuf.append("\n passphrase: ").append(
+ TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>");
+ sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand);
return sbuf.toString();
}
@@ -130,6 +174,9 @@
wps = new WpsInfo(source.wps);
groupOwnerIntent = source.groupOwnerIntent;
netId = source.netId;
+ networkName = source.networkName;
+ passphrase = source.passphrase;
+ groupOwnerBand = source.groupOwnerBand;
}
}
@@ -139,6 +186,9 @@
dest.writeParcelable(wps, flags);
dest.writeInt(groupOwnerIntent);
dest.writeInt(netId);
+ dest.writeString(networkName);
+ dest.writeString(passphrase);
+ dest.writeInt(groupOwnerBand);
}
/** Implement the Parcelable interface */
@@ -150,6 +200,9 @@
config.wps = (WpsInfo) in.readParcelable(null);
config.groupOwnerIntent = in.readInt();
config.netId = in.readInt();
+ config.networkName = in.readString();
+ config.passphrase = in.readString();
+ config.groupOwnerBand = in.readInt();
return config;
}
@@ -157,4 +210,140 @@
return new WifiP2pConfig[size];
}
};
+
+ /**
+ * Builder used to build {@link WifiP2pConfig} objects for
+ * creating or joining a group.
+ */
+ public static final class Builder {
+
+ private static final MacAddress MAC_ANY_ADDRESS =
+ MacAddress.fromString("00:00:00:00:00:00");
+
+ private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
+ private String mNetworkName = "";
+ private String mPassphrase = "";
+ private int mGroupOwnerBand = GROUP_OWNER_BAND_AUTO;
+ private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+
+ /**
+ * Specify the peer's MAC address. If not set, the device will
+ * try to find a peer whose SSID matches the network name as
+ * specified by {@link #setNetworkName(String)}. Specifying null will
+ * reset the peer's MAC address to "00:00:00:00:00:00".
+ * <p>
+ * Optional. "00:00:00:00:00:00" by default.
+ *
+ * @param deviceAddress the peer's MAC address.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setDeviceAddress(MacAddress deviceAddress) {
+ if (deviceAddress == null) {
+ mDeviceAddress = MAC_ANY_ADDRESS;
+ } else {
+ mDeviceAddress = deviceAddress;
+ }
+ return this;
+ }
+
+ /**
+ * Specify the network name, a.k.a. group name,
+ * for creating or joining a group.
+ * <p>
+ * Must be called - an empty network name is not valid.
+ *
+ * @param networkName network name of a group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setNetworkName(String networkName) {
+ if (TextUtils.isEmpty(networkName)) {
+ throw new IllegalArgumentException(
+ "network name must be non-empty.");
+ }
+ mNetworkName = networkName;
+ return this;
+ }
+
+ /**
+ * Specify the passphrase for creating or joining a group.
+ * <p>
+ * Must be called - an empty passphrase is not valid.
+ *
+ * @param passphrase the passphrase of a group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setPassphrase(String passphrase) {
+ if (TextUtils.isEmpty(passphrase)) {
+ throw new IllegalArgumentException(
+ "passphrase must be non-empty.");
+ }
+ mPassphrase = passphrase;
+ return this;
+ }
+
+ /**
+ * Specify the band to use for creating the group. This method only applies when
+ * creating a group as Group Owner using {@link WifiP2pManager#createGroup}.
+ * The band should be {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ},
+ * or allow the system to pick the band by specifying {@link #GROUP_OWNER_BAND_AUTO}.
+ * If the Group Owner cannot create a group in the specified band, the operation will fail.
+ * <p>
+ * Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
+ *
+ * @param band the required band of group owner.
+ * This should be one of {@link #GROUP_OWNER_BAND_AUTO},
+ * {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setGroupOwnerBand(int band) {
+ mGroupOwnerBand = band;
+ return this;
+ }
+
+ /**
+ * Specify that the group configuration be persisted (i.e. saved).
+ * By default the group configuration will not be saved.
+ * <p>
+ * Optional. false by default.
+ *
+ * @param persistent is this group persistent group.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder enablePersistentMode(boolean persistent) {
+ if (persistent) {
+ mNetId = WifiP2pGroup.PERSISTENT_NET_ID;
+ } else {
+ mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+ }
+ return this;
+ }
+
+ /**
+ * Build {@link WifiP2pConfig} given the current requests made on the builder.
+ * @return {@link WifiP2pConfig} constructed based on builder method calls.
+ */
+ public WifiP2pConfig build() {
+ if (TextUtils.isEmpty(mNetworkName)) {
+ throw new IllegalStateException(
+ "network name must be non-empty.");
+ }
+ if (TextUtils.isEmpty(mPassphrase)) {
+ throw new IllegalStateException(
+ "passphrase must be non-empty.");
+ }
+
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = mDeviceAddress.toString();
+ config.networkName = mNetworkName;
+ config.passphrase = mPassphrase;
+ config.groupOwnerBand = mGroupOwnerBand;
+ config.netId = mNetId;
+ return config;
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index f58a006..b0ed110 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -16,6 +16,7 @@
package android.net.wifi.p2p;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -25,6 +26,7 @@
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.net.NetworkInfo;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceResponse;
@@ -49,6 +51,8 @@
import dalvik.system.CloseGuard;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -158,6 +162,14 @@
*/
public static final String EXTRA_WIFI_STATE = "wifi_p2p_state";
+ /** @hide */
+ @IntDef({
+ WIFI_P2P_STATE_DISABLED,
+ WIFI_P2P_STATE_ENABLED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WifiP2pState {
+ }
+
/**
* Wi-Fi p2p is disabled.
*
@@ -250,6 +262,14 @@
*/
public static final String EXTRA_DISCOVERY_STATE = "discoveryState";
+ /** @hide */
+ @IntDef({
+ WIFI_P2P_DISCOVERY_STOPPED,
+ WIFI_P2P_DISCOVERY_STARTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WifiP2pDiscoveryState {
+ }
+
/**
* p2p discovery has stopped
*
@@ -502,6 +522,21 @@
/** @hide */
public static final int SET_ONGOING_PEER_CONFIG_SUCCEEDED = BASE + 89;
+ /** @hide */
+ public static final int REQUEST_P2P_STATE = BASE + 90;
+ /** @hide */
+ public static final int RESPONSE_P2P_STATE = BASE + 91;
+
+ /** @hide */
+ public static final int REQUEST_DISCOVERY_STATE = BASE + 92;
+ /** @hide */
+ public static final int RESPONSE_DISCOVERY_STATE = BASE + 93;
+
+ /** @hide */
+ public static final int REQUEST_NETWORK_INFO = BASE + 94;
+ /** @hide */
+ public static final int RESPONSE_NETWORK_INFO = BASE + 95;
+
/**
* Create a new WifiP2pManager instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -690,6 +725,43 @@
public void onHandoverMessageAvailable(String handoverMessage);
}
+ /** Interface for callback invocation when p2p state is available
+ * in response to {@link #requestP2pState}.
+ */
+ public interface P2pStateListener {
+ /**
+ * The requested p2p state is available.
+ * @param state Wi-Fi p2p state
+ * @see #WIFI_P2P_STATE_DISABLED
+ * @see #WIFI_P2P_STATE_ENABLED
+ */
+ void onP2pStateAvailable(@WifiP2pState int state);
+ }
+
+ /** Interface for callback invocation when p2p state is available
+ * in response to {@link #requestDiscoveryState}.
+ */
+ public interface DiscoveryStateListener {
+ /**
+ * The requested p2p discovery state is available.
+ * @param state Wi-Fi p2p discovery state
+ * @see #WIFI_P2P_DISCOVERY_STARTED
+ * @see #WIFI_P2P_DISCOVERY_STOPPED
+ */
+ void onDiscoveryStateAvailable(@WifiP2pDiscoveryState int state);
+ }
+
+ /** Interface for callback invocation when {@link android.net.NetworkInfo} is available
+ * in response to {@link #requestNetworkInfo}.
+ */
+ public interface NetworkInfoListener {
+ /**
+ * The requested {@link android.net.NetworkInfo} is available
+ * @param networkInfo Wi-Fi p2p {@link android.net.NetworkInfo}
+ */
+ void onNetworkInfoAvailable(NetworkInfo networkInfo);
+ }
+
/**
* Interface for callback invocation when ongoing peer info is available
* @hide
@@ -889,6 +961,24 @@
.onOngoingPeerAvailable(peerConfig);
}
break;
+ case RESPONSE_P2P_STATE:
+ if (listener != null) {
+ ((P2pStateListener) listener)
+ .onP2pStateAvailable(message.arg1);
+ }
+ break;
+ case RESPONSE_DISCOVERY_STATE:
+ if (listener != null) {
+ ((DiscoveryStateListener) listener)
+ .onDiscoveryStateAvailable(message.arg1);
+ }
+ break;
+ case RESPONSE_NETWORK_INFO:
+ if (listener != null) {
+ ((NetworkInfoListener) listener)
+ .onNetworkInfoAvailable((NetworkInfo) message.obj);
+ }
+ break;
default:
Log.d(TAG, "Ignored " + message);
break;
@@ -1126,6 +1216,38 @@
}
/**
+ * Create a p2p group with the current device as the group owner. This essentially creates
+ * an access point that can accept connections from legacy clients as well as other p2p
+ * devices.
+ *
+ * <p> An app should use {@link WifiP2pConfig.Builder} to build the configuration
+ * for a group.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * This function would normally not be used unless the current device needs
+ * to form a p2p group as a Group Owner and allow peers to join it as either
+ * Group Clients or legacy Wi-Fi STAs.
+ *
+ * <p> The function call immediately returns after sending a group creation request
+ * to the framework. The application is notified of a success or failure to initiate
+ * group creation through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * <p> Application can request for the group details with {@link #requestGroupInfo}.
+ *
+ * @param c is the channel created at {@link #initialize}.
+ * @param config the configuration of a p2p group.
+ * @param listener for callbacks on success or failure. Can be null.
+ */
+ public void createGroup(@NonNull Channel c,
+ @Nullable WifiP2pConfig config,
+ @Nullable ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(CREATE_GROUP, 0,
+ c.putListener(listener), config);
+ }
+
+ /**
* Remove the current p2p group.
*
* <p> The function call immediately returns after sending a group removal request
@@ -1616,4 +1738,68 @@
c.mAsyncChannel.sendMessage(SET_ONGOING_PEER_CONFIG, 0,
c.putListener(listener), config);
}
+
+ /**
+ * Request p2p enabled state.
+ *
+ * <p> This state indicates whether Wi-Fi p2p is enabled or disabled.
+ * The valid value is one of {@link #WIFI_P2P_STATE_DISABLED} or
+ * {@link #WIFI_P2P_STATE_ENABLED}. The state is returned using the
+ * {@link P2pStateListener} listener.
+ *
+ * <p> This state is also included in the {@link #WIFI_P2P_STATE_CHANGED_ACTION}
+ * broadcast event with extra {@link #EXTRA_WIFI_STATE}.
+ *
+ * @param c is the channel created at {@link #initialize}.
+ * @param listener for callback when p2p state is available..
+ */
+ public void requestP2pState(@NonNull Channel c,
+ @NonNull P2pStateListener listener) {
+ checkChannel(c);
+ if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+ c.mAsyncChannel.sendMessage(REQUEST_P2P_STATE, 0, c.putListener(listener));
+ }
+
+ /**
+ * Request p2p discovery state.
+ *
+ * <p> This state indicates whether p2p discovery has started or stopped.
+ * The valid value is one of {@link #WIFI_P2P_DISCOVERY_STARTED} or
+ * {@link #WIFI_P2P_DISCOVERY_STOPPED}. The state is returned using the
+ * {@link DiscoveryStateListener} listener.
+ *
+ * <p> This state is also included in the {@link #WIFI_P2P_DISCOVERY_CHANGED_ACTION}
+ * broadcast event with extra {@link #EXTRA_DISCOVERY_STATE}.
+ *
+ * @param c is the channel created at {@link #initialize}.
+ * @param listener for callback when discovery state is available..
+ */
+ public void requestDiscoveryState(@NonNull Channel c,
+ @NonNull DiscoveryStateListener listener) {
+ checkChannel(c);
+ if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+ c.mAsyncChannel.sendMessage(REQUEST_DISCOVERY_STATE, 0, c.putListener(listener));
+ }
+
+ /**
+ * Request network info.
+ *
+ * <p> This method provides the network info in the form of a {@link android.net.NetworkInfo}.
+ * {@link android.net.NetworkInfo#isAvailable()} indicates the p2p availability and
+ * {@link android.net.NetworkInfo#getDetailedState()} reports the current fine-grained state
+ * of the network. This {@link android.net.NetworkInfo} is returned using the
+ * {@link NetworkInfoListener} listener.
+ *
+ * <p> This information is also included in the {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION}
+ * broadcast event with extra {@link #EXTRA_NETWORK_INFO}.
+ *
+ * @param c is the channel created at {@link #initialize}.
+ * @param listener for callback when network info is available..
+ */
+ public void requestNetworkInfo(@NonNull Channel c,
+ @NonNull NetworkInfoListener listener) {
+ checkChannel(c);
+ if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+ c.mAsyncChannel.sendMessage(REQUEST_NETWORK_INFO, 0, c.putListener(listener));
+ }
}
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index 04bc557..0f4e3a8 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -73,7 +73,7 @@
}
@Override
- public ParceledListSlice getConfiguredNetworks() {
+ public ParceledListSlice getConfiguredNetworks(String packageName) {
throw new UnsupportedOperationException();
}
@@ -188,17 +188,17 @@
}
@Override
- public void disconnect(String packageName) {
+ public boolean disconnect(String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void reconnect(String packageName) {
+ public boolean reconnect(String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void reassociate(String packageName) {
+ public boolean reassociate(String packageName) {
throw new UnsupportedOperationException();
}
@@ -442,13 +442,13 @@
}
@Override
- public boolean addNetworkSuggestions(
+ public int addNetworkSuggestions(
List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
throw new UnsupportedOperationException();
}
@Override
- public boolean removeNetworkSuggestions(
+ public int removeNetworkSuggestions(
List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 8fe5af9..13c8c9e 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1298,13 +1298,26 @@
*/
@Test
public void addRemoveNetworkSuggestions() throws Exception {
- when(mWifiService.addNetworkSuggestions(any(List.class), anyString())).thenReturn(true);
- when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())).thenReturn(true);
+ when(mWifiService.addNetworkSuggestions(any(List.class), anyString()))
+ .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
+ when(mWifiService.removeNetworkSuggestions(any(List.class), anyString()))
+ .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
- assertTrue(mWifiManager.addNetworkSuggestions(new ArrayList<>()));
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiManager.addNetworkSuggestions(new ArrayList<>()));
verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
- assertTrue(mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
+ assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
verify(mWifiService).removeNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
}
+
+ /**
+ * Verify call to {@link WifiManager#getMaxNumberOfNetworkSuggestionsPerApp()}.
+ */
+ @Test
+ public void getMaxNumberOfNetworkSuggestionsPerApp() {
+ assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP,
+ mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index c455c6f..2505499 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -244,7 +244,7 @@
* when match-none SSID pattern is set.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern() {
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern1() {
new WifiNetworkConfigBuilder()
.setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_LITERAL))
.buildNetworkSpecifier();
@@ -252,10 +252,21 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none SSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern2() {
+ new WifiNetworkConfigBuilder()
+ .setSsid("")
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
* when match-none BSSID pattern is set.
*/
@Test(expected = IllegalStateException.class)
- public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern() {
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern1() {
new WifiNetworkConfigBuilder()
.setBssidPattern(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS)
.buildNetworkSpecifier();
@@ -263,6 +274,28 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none BSSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern2() {
+ new WifiNetworkConfigBuilder()
+ .setBssid(MacAddress.BROADCAST_ADDRESS)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+ * when match-none BSSID pattern is set.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern3() {
+ new WifiNetworkConfigBuilder()
+ .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+ .buildNetworkSpecifier();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
* when SSID pattern is set for hidden network.
*/
@Test(expected = IllegalStateException.class)
@@ -429,13 +462,15 @@
* {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() {
+ public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetworkWithBssid() {
WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
.setSsid(TEST_SSID)
+ .setBssid(MacAddress.fromString(TEST_BSSID))
.setIsEnhancedOpen()
.buildNetworkSuggestion();
assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertEquals(TEST_BSSID, suggestion.wifiConfiguration.BSSID);
assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
.get(WifiConfiguration.KeyMgmt.OWE));
assertNull(suggestion.wifiConfiguration.preSharedKey);
@@ -505,7 +540,7 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
- * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is set.
+ * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
*/
@Test(expected = IllegalStateException.class)
public void testWifiNetworkSuggestionBuilderWithBssidPattern() {
@@ -518,18 +553,6 @@
/**
* Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
- * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
- */
- @Test(expected = IllegalStateException.class)
- public void testWifiNetworkSuggestionBuilderWithBssid() {
- new WifiNetworkConfigBuilder()
- .setSsid(TEST_SSID)
- .setBssid(MacAddress.fromString(TEST_BSSID))
- .buildNetworkSuggestion();
- }
-
- /**
- * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
* when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set.
*/
@Test(expected = IllegalStateException.class)
@@ -539,6 +562,41 @@
}
/**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+ * when {@link WifiNetworkConfigBuilder#setSsid(String)} is invoked with an invalid value.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithInvalidSsid() {
+ new WifiNetworkConfigBuilder()
+ .setSsid("")
+ .buildNetworkSuggestion();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+ * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithInvalidBroadcastBssid() {
+ new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setBssid(MacAddress.BROADCAST_ADDRESS)
+ .buildNetworkSuggestion();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+ * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithInvalidAllZeroBssid() {
+ new WifiNetworkConfigBuilder()
+ .setSsid(TEST_SSID)
+ .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+ .buildNetworkSuggestion();
+ }
+
+ /**
* Ensure {@link WifiNetworkConfigBuilder#setPriority(int)} throws an exception
* when the value is negative.
*/
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 6bab60d..5cc8217 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -29,6 +29,7 @@
@SmallTest
public class WifiNetworkSuggestionTest {
private static final String TEST_SSID = "\"Test123\"";
+ private static final String TEST_BSSID = "12:12:12:12:12:12";
private static final String TEST_SSID_1 = "\"Test1234\"";
/**
@@ -38,6 +39,7 @@
public void testWifiNetworkSuggestionParcel() {
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
new WifiNetworkSuggestion(configuration, false, true, 0);
@@ -65,18 +67,20 @@
/**
* Check NetworkSuggestion equals returns {@code true} for 2 network suggestions with the same
- * SSID, key mgmt and UID.
+ * SSID, BSSID, key mgmt and UID.
*/
@Test
public void testWifiNetworkSuggestionEqualsSame() {
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
new WifiNetworkSuggestion(configuration, true, false, 0);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
+ configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
new WifiNetworkSuggestion(configuration1, false, true, 0);
@@ -86,7 +90,7 @@
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * key mgmt and UID, but different SSID.
+ * BSSID, key mgmt and UID, but different SSID.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenSsidIsDifferent() {
@@ -107,7 +111,29 @@
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * SSID and UID, but different key mgmt.
+ * SSID, key mgmt and UID, but different BSSID.
+ */
+ @Test
+ public void testWifiNetworkSuggestionEqualsFailsWhenBssidIsDifferent() {
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = TEST_SSID;
+ configuration.BSSID = TEST_BSSID;
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ WifiNetworkSuggestion suggestion =
+ new WifiNetworkSuggestion(configuration, false, false, 0);
+
+ WifiConfiguration configuration1 = new WifiConfiguration();
+ configuration1.SSID = TEST_SSID;
+ configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ WifiNetworkSuggestion suggestion1 =
+ new WifiNetworkSuggestion(configuration1, false, false, 0);
+
+ assertNotEquals(suggestion, suggestion1);
+ }
+
+ /**
+ * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+ * SSID, BSSID and UID, but different key mgmt.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenKeyMgmtIsDifferent() {
@@ -128,7 +154,7 @@
/**
* Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
- * SSID and key mgmt, but different UID.
+ * SSID, BSSID and key mgmt, but different UID.
*/
@Test
public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() {